# VFS User Functions - Templates and Examples This document provides template functions that you can implement for domain-specific needs. These functions combine VFS primitives to solve common problems. ## Table of Contents 1. [Code Analysis Functions](#code-analysis-functions) 2. [Export Format Functions](#export-format-functions) 3. [Project Management Functions](#project-management-functions) 4. [Creative Writing Functions](#creative-writing-functions) 5. [Game Development Functions](#game-development-functions) ## Code Analysis Functions ### Get Dependency Graph ```javascript /** * Build a dependency graph for JavaScript/TypeScript projects */ async function getDependencyGraph(vfs, srcPath) { const files = await vfs.readdir(srcPath, { recursive: true }) const graph = {} for (const file of files) { const filePath = `${srcPath}/${file}` // Only process JS/TS files if (file.match(/\.(js|ts|jsx|tsx)$/)) { const content = await vfs.readFile(filePath) const text = content.toString() // Parse imports (basic regex, use proper AST parser for production) const imports = [] const importRegex = /import\s+.*?\s+from\s+['"](.+?)['"]/g const requireRegex = /require\(['"](.+?)['"]\)/g let match while ((match = importRegex.exec(text)) !== null) { imports.push(match[1]) } while ((match = requireRegex.exec(text)) !== null) { imports.push(match[1]) } graph[filePath] = imports } } return graph } // Use it const deps = await getDependencyGraph(vfs, '/src') ``` ### Find Circular Dependencies ```javascript /** * Detect circular dependencies in your code */ async function findCircularDependencies(vfs, srcPath) { const graph = await getDependencyGraph(vfs, srcPath) const cycles = [] function detectCycle(node, visited = new Set(), stack = []) { if (stack.includes(node)) { const cycleStart = stack.indexOf(node) cycles.push(stack.slice(cycleStart)) return } if (visited.has(node)) return visited.add(node) stack.push(node) const dependencies = graph[node] || [] for (const dep of dependencies) { // Resolve relative imports const resolvedDep = resolvePath(node, dep) if (graph[resolvedDep]) { detectCycle(resolvedDep, visited, [...stack]) } } } Object.keys(graph).forEach(node => detectCycle(node)) return cycles } ``` ### Find Untested Code ```javascript /** * Find source files without corresponding test files */ async function findUntestedCode(vfs, srcPath, testPath = null) { testPath = testPath || srcPath.replace('/src', '/tests') const sourceFiles = await vfs.readdir(srcPath, { recursive: true }) const testFiles = await vfs.readdir(testPath, { recursive: true }).catch(() => []) const untestedFiles = [] for (const sourceFile of sourceFiles) { if (!sourceFile.match(/\.(js|ts|jsx|tsx)$/)) continue // Look for corresponding test file const baseName = sourceFile.replace(/\.(js|ts|jsx|tsx)$/, '') const hasTest = testFiles.some(testFile => testFile.includes(baseName) && testFile.match(/\.(test|spec)\.(js|ts|jsx|tsx)$/) ) if (!hasTest) { untestedFiles.push(`${srcPath}/${sourceFile}`) } } return untestedFiles } ``` ### Find Similar Code (Duplicate Detection) ```javascript /** * Find potentially duplicate code using similarity scoring */ async function findSimilarCode(vfs, filePath, options = {}) { const threshold = options.threshold || 0.8 const searchPath = options.searchPath || '/' // Get the reference file content const referenceContent = await vfs.readFile(filePath) const referenceText = referenceContent.toString() // Use VFS's semantic search const similar = await vfs.findSimilar(filePath, { limit: 10, threshold }) // Additionally, do structural comparison const results = [] for (const match of similar) { const matchContent = await vfs.readFile(match.path) const matchText = matchContent.toString() // Simple line-based similarity (use better algorithms in production) const similarity = calculateSimilarity(referenceText, matchText) if (similarity > threshold) { results.push({ path: match.path, similarity, semanticScore: match.score }) } } return results.sort((a, b) => b.similarity - a.similarity) } function calculateSimilarity(text1, text2) { // Simple Jaccard similarity on lines const lines1 = new Set(text1.split('\n').map(l => l.trim()).filter(l => l)) const lines2 = new Set(text2.split('\n').map(l => l.trim()).filter(l => l)) const intersection = new Set([...lines1].filter(x => lines2.has(x))) const union = new Set([...lines1, ...lines2]) return intersection.size / union.size } ``` ## Export Format Functions ### Export to EPUB (for novels) ```javascript /** * Export a directory of markdown files to EPUB format */ async function exportToEpub(vfs, path, metadata = {}) { // First get the markdown export const markdown = await vfs.exportToMarkdown(path) // You'll need an EPUB library like epub-gen const Epub = require('epub-gen') // Convert markdown chapters to EPUB format const chapters = [] const files = await vfs.readdir(path, { recursive: true }) for (const file of files.sort()) { if (file.endsWith('.md')) { const content = await vfs.readFile(`${path}/${file}`) const title = file.replace('.md', '').replace(/-/g, ' ') chapters.push({ title: title, data: content.toString() }) } } const options = { title: metadata.title || 'My Book', author: metadata.author || 'Author', chapters: chapters } return new Epub(options) } ``` ### Export to Static Site ```javascript /** * Export VFS content to static HTML site */ async function exportToStaticSite(vfs, sourcePath, options = {}) { const json = await vfs.exportToJSON(sourcePath) const html = [] html.push('') html.push('') html.push(`${options.title || 'Documentation'}`) html.push('') html.push('') function renderNode(node, name, depth = 0) { const indent = ' '.repeat(depth) if (node._meta?.type === 'file') { html.push(`${indent}
`) html.push(`${indent} ${name}`) if (typeof node._content === 'string') { // Convert markdown to HTML if needed html.push(`${indent}
${escapeHtml(node._content)}
`) } html.push(`${indent}
`) } else if (node._meta?.type === 'directory') { html.push(`${indent}
`) html.push(`${indent} ${name}`) for (const [childName, childNode] of Object.entries(node)) { if (!childName.startsWith('_')) { renderNode(childNode, childName, depth + 1) } } html.push(`${indent}
`) } } renderNode(json, options.title || 'Root') html.push('') return html.join('\n') } ``` ### Export to GraphQL Schema ```javascript /** * Generate GraphQL schema from VFS entities */ async function exportToGraphQLSchema(vfs) { const entities = await vfs.listEntities() const types = new Map() // Group entities by type for (const entity of entities) { const type = entity.type || 'Unknown' if (!types.has(type)) { types.set(type, []) } types.get(type).push(entity) } // Generate schema let schema = 'type Query {\n' for (const [typeName, entities] of types) { schema += ` get${typeName}(id: ID!): ${typeName}\n` schema += ` list${typeName}s: [${typeName}!]!\n` } schema += '}\n\n' // Generate types for (const [typeName, entities] of types) { schema += `type ${typeName} {\n` schema += ' id: ID!\n' // Infer fields from first entity if (entities.length > 0) { const sample = entities[0] for (const [key, value] of Object.entries(sample)) { if (key !== 'id') { const fieldType = inferGraphQLType(value) schema += ` ${key}: ${fieldType}\n` } } } schema += '}\n\n' } return schema } ``` ## Project Management Functions ### Get Project Insights ```javascript /** * Analyze project for insights and patterns */ async function getProjectInsights(vfs, projectPath) { const stats = await vfs.getProjectStats(projectPath) const todos = await vfs.getAllTodos(projectPath) const timeline = await vfs.getTimeline({ limit: 100 }) // Analyze activity patterns const activityByDay = {} const activityByUser = {} const activityByFile = {} for (const event of timeline) { const day = event.timestamp.toISOString().split('T')[0] activityByDay[day] = (activityByDay[day] || 0) + 1 const user = event.user || 'system' activityByUser[user] = (activityByUser[user] || 0) + 1 activityByFile[event.path] = (activityByFile[event.path] || 0) + 1 } // Find hotspots (most edited files) const hotspots = Object.entries(activityByFile) .sort(([,a], [,b]) => b - a) .slice(0, 10) .map(([path, count]) => ({ path, edits: count })) // Todo analysis const todosByPriority = {} const todosByStatus = {} for (const todo of todos) { todosByPriority[todo.priority] = (todosByPriority[todo.priority] || 0) + 1 todosByStatus[todo.status] = (todosByStatus[todo.status] || 0) + 1 } return { stats, activity: { byDay: activityByDay, byUser: activityByUser, hotspots }, todos: { total: todos.length, byPriority: todosByPriority, byStatus: todosByStatus, highPriority: todos.filter(t => t.priority === 'high' && t.status === 'pending') }, recommendations: generateRecommendations(stats, todos, hotspots) } } function generateRecommendations(stats, todos, hotspots) { const recommendations = [] if (stats.largestFile && stats.largestFile.size > 1024 * 1024) { recommendations.push({ type: 'refactor', message: `Consider splitting ${stats.largestFile.path} (${Math.round(stats.largestFile.size / 1024)}KB)` }) } if (todos.filter(t => t.priority === 'high' && t.status === 'pending').length > 5) { recommendations.push({ type: 'priority', message: 'You have many high-priority pending todos' }) } if (hotspots.length > 0 && hotspots[0].edits > 50) { recommendations.push({ type: 'stability', message: `${hotspots[0].path} changes frequently, consider stabilizing` }) } return recommendations } ``` ### Generate Sprint Report ```javascript /** * Generate a report for the current sprint */ async function generateSprintReport(vfs, sprintStart, sprintEnd = new Date()) { const timeline = await vfs.getTimeline({ from: sprintStart, to: sprintEnd }) const todos = await vfs.getAllTodos() // Group work by user const workByUser = {} for (const event of timeline) { const user = event.user || 'system' if (!workByUser[user]) { workByUser[user] = { commits: 0, filesModified: new Set(), linesChanged: 0 } } workByUser[user].commits++ workByUser[user].filesModified.add(event.path) } // Calculate completion rate const completedTodos = todos.filter(t => t.status === 'completed').length const totalTodos = todos.length const completionRate = totalTodos > 0 ? (completedTodos / totalTodos * 100).toFixed(1) : 0 return { period: { start: sprintStart, end: sprintEnd, days: Math.ceil((sprintEnd - sprintStart) / (1000 * 60 * 60 * 24)) }, team: Object.entries(workByUser).map(([user, work]) => ({ user, commits: work.commits, filesModified: work.filesModified.size })), todos: { completed: completedTodos, total: totalTodos, completionRate: `${completionRate}%`, remaining: todos.filter(t => t.status === 'pending') }, velocity: { commitsPerDay: (timeline.length / 7).toFixed(1), todosPerDay: (completedTodos / 7).toFixed(1) } } } ``` ## Creative Writing Functions ### Track Character Arcs ```javascript /** * Track how characters evolve throughout a story */ async function trackCharacterArc(vfs, characterName, storyPath = '/') { // Find the character entity const entities = await vfs.searchEntities({ type: 'character', name: characterName }) if (entities.length === 0) { throw new Error(`Character ${characterName} not found`) } const character = entities[0] const occurrences = await vfs.findEntityOccurrences(character.id) // Analyze each appearance const arc = [] for (const occurrence of occurrences) { const content = await vfs.readFile(occurrence.path) const text = content.toString() // Find mentions of the character (basic approach) const mentions = text.split('\n').filter(line => line.toLowerCase().includes(characterName.toLowerCase()) ) arc.push({ chapter: occurrence.path, mentions: mentions.length, // Analyze emotional tone (simplified) mood: analyzeMood(mentions), // Extract key actions actions: extractActions(mentions, characterName) }) } return { character: character.metadata, arc: arc, summary: summarizeArc(arc) } } function analyzeMood(mentions) { const positiveWords = ['smiled', 'laughed', 'happy', 'joy', 'love', 'success'] const negativeWords = ['cried', 'angry', 'sad', 'fear', 'fail', 'death'] let positive = 0, negative = 0 for (const mention of mentions) { const lower = mention.toLowerCase() positive += positiveWords.filter(w => lower.includes(w)).length negative += negativeWords.filter(w => lower.includes(w)).length } if (positive > negative) return 'positive' if (negative > positive) return 'negative' return 'neutral' } ``` ### Generate Story Bible ```javascript /** * Create a comprehensive reference for your story universe */ async function generateStoryBible(vfs, storyPath) { const characters = await vfs.listEntities({ type: 'character' }) const locations = await vfs.listEntities({ type: 'location' }) const concepts = await vfs.findConcepts({ domain: 'narrative' }) const bible = { title: 'Story Bible', generated: new Date(), characters: {}, locations: {}, plotThreads: {}, timeline: [] } // Document characters for (const char of characters) { const occurrences = await vfs.findEntityOccurrences(char.id) bible.characters[char.metadata.name] = { ...char.metadata, appearances: occurrences.map(o => o.path), relationships: await vfs.getEntityGraph(char.id, { depth: 1 }) } } // Document locations for (const loc of locations) { bible.locations[loc.metadata.name] = { ...loc.metadata, scenes: await vfs.findEntityOccurrences(loc.id) } } // Plot threads from concepts for (const concept of concepts) { if (concept.type === 'plot') { bible.plotThreads[concept.name] = { description: concept.description, keywords: concept.keywords, chapters: await vfs.findByConcept(concept.name) } } } // Generate timeline const events = await vfs.getTimeline({ limit: 1000 }) bible.timeline = events.map(e => ({ date: e.timestamp, event: e.description, chapter: e.path })) return bible } ``` ## Game Development Functions ### Validate Game Data ```javascript /** * Validate game configuration files for consistency */ async function validateGameData(vfs, gamePath) { const errors = [] const warnings = [] // Load all game data const gameData = await vfs.exportToJSON(gamePath) // Check quest references if (gameData.quests) { for (const [questName, quest] of Object.entries(gameData.quests)) { // Check NPC references if (quest._content?.questGiver) { const npcPath = `${gamePath}/npcs/${quest._content.questGiver}.json` const exists = await vfs.exists(npcPath) if (!exists) { errors.push(`Quest ${questName} references missing NPC: ${quest._content.questGiver}`) } } // Check item rewards if (quest._content?.rewards?.items) { for (const item of quest._content.rewards.items) { const itemPath = `${gamePath}/items/${item}.json` const exists = await vfs.exists(itemPath) if (!exists) { warnings.push(`Quest ${questName} rewards missing item: ${item}`) } } } } } // Check balance if (gameData.items) { const itemPowers = [] for (const [itemName, item] of Object.entries(gameData.items)) { if (item._content?.stats) { const totalPower = Object.values(item._content.stats) .reduce((a, b) => a + b, 0) itemPowers.push({ name: itemName, power: totalPower }) } } // Find outliers const avgPower = itemPowers.reduce((a, b) => a + b.power, 0) / itemPowers.length const outliers = itemPowers.filter(i => Math.abs(i.power - avgPower) > avgPower * 2) for (const outlier of outliers) { warnings.push(`Item ${outlier.name} may be imbalanced (power: ${outlier.power}, avg: ${avgPower})`) } } return { errors, warnings, valid: errors.length === 0 } } ``` ### Generate Loot Tables ```javascript /** * Generate weighted loot tables from item definitions */ async function generateLootTables(vfs, itemsPath) { const items = await vfs.exportToJSON(itemsPath) const tables = { common: [], uncommon: [], rare: [], epic: [], legendary: [] } for (const [itemName, item] of Object.entries(items)) { if (item._meta?.type === 'file' && item._content?.rarity) { const entry = { name: itemName.replace('.json', ''), weight: getWeight(item._content.rarity), data: item._content } tables[item._content.rarity.toLowerCase()].push(entry) } } // Normalize weights for (const table of Object.values(tables)) { const totalWeight = table.reduce((a, b) => a + b.weight, 0) for (const entry of table) { entry.probability = (entry.weight / totalWeight * 100).toFixed(2) + '%' } } return tables } function getWeight(rarity) { const weights = { common: 100, uncommon: 50, rare: 20, epic: 5, legendary: 1 } return weights[rarity.toLowerCase()] || 10 } ``` ## Using These Functions All these functions are templates that you can customize for your specific needs. To use them: 1. Copy the function you need 2. Modify it for your specific requirements 3. Use it with your VFS instance: ```javascript import { Brainy } from '@soulcraft/brainy' // Initialize VFS const brain = new Brainy() await brain.init() const vfs = brain.vfs() await vfs.init() // Use your custom function const insights = await getProjectInsights(vfs, '/my-project') console.log(insights.recommendations) // Combine multiple functions const deps = await getDependencyGraph(vfs, '/src') const cycles = await findCircularDependencies(vfs, '/src') const untested = await findUntestedCode(vfs, '/src', '/tests') ``` Remember: These are starting points. The power of VFS is that you can combine its primitives to build exactly what you need for your domain!