@gettymade/roux 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../node_modules/string-similarity/src/index.js","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/commands/status.ts","../../src/providers/docstore/cache.ts","../../src/providers/vector/sqlite.ts","../../src/cli/commands/serve.ts","../../src/providers/docstore/index.ts","../../src/providers/docstore/parser.ts","../../src/graph/builder.ts","../../src/graph/operations.ts","../../src/providers/embedding/transformers.ts","../../src/core/graphcore.ts","../../src/mcp/server.ts","../../src/mcp/types.ts","../../src/mcp/truncate.ts","../../src/mcp/transforms.ts","../../src/mcp/handlers.ts","../../src/cli/commands/viz.ts"],"sourcesContent":["module.exports = {\n\tcompareTwoStrings:compareTwoStrings,\n\tfindBestMatch:findBestMatch\n};\n\nfunction compareTwoStrings(first, second) {\n\tfirst = first.replace(/\\s+/g, '')\n\tsecond = second.replace(/\\s+/g, '')\n\n\tif (first === second) return 1; // identical or empty\n\tif (first.length < 2 || second.length < 2) return 0; // if either is a 0-letter or 1-letter string\n\n\tlet firstBigrams = new Map();\n\tfor (let i = 0; i < first.length - 1; i++) {\n\t\tconst bigram = first.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram) + 1\n\t\t\t: 1;\n\n\t\tfirstBigrams.set(bigram, count);\n\t};\n\n\tlet intersectionSize = 0;\n\tfor (let i = 0; i < second.length - 1; i++) {\n\t\tconst bigram = second.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram)\n\t\t\t: 0;\n\n\t\tif (count > 0) {\n\t\t\tfirstBigrams.set(bigram, count - 1);\n\t\t\tintersectionSize++;\n\t\t}\n\t}\n\n\treturn (2.0 * intersectionSize) / (first.length + second.length - 2);\n}\n\nfunction findBestMatch(mainString, targetStrings) {\n\tif (!areArgsValid(mainString, targetStrings)) throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');\n\t\n\tconst ratings = [];\n\tlet bestMatchIndex = 0;\n\n\tfor (let i = 0; i < targetStrings.length; i++) {\n\t\tconst currentTargetString = targetStrings[i];\n\t\tconst currentRating = compareTwoStrings(mainString, currentTargetString)\n\t\tratings.push({target: currentTargetString, rating: currentRating})\n\t\tif (currentRating > ratings[bestMatchIndex].rating) {\n\t\t\tbestMatchIndex = i\n\t\t}\n\t}\n\t\n\t\n\tconst bestMatch = ratings[bestMatchIndex]\n\t\n\treturn { ratings: ratings, bestMatch: bestMatch, bestMatchIndex: bestMatchIndex };\n}\n\nfunction areArgsValid(mainString, targetStrings) {\n\tif (typeof mainString !== 'string') return false;\n\tif (!Array.isArray(targetStrings)) return false;\n\tif (!targetStrings.length) return false;\n\tif (targetStrings.find( function (s) { return typeof s !== 'string'})) return false;\n\treturn true;\n}\n","#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { resolve } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { initCommand } from './commands/init.js';\nimport { statusCommand } from './commands/status.js';\nimport { serveCommand } from './commands/serve.js';\nimport { vizCommand } from './commands/viz.js';\n\nconst program = new Command();\n\nprogram\n .name('roux')\n .description('Graph Programming Interface for knowledge bases')\n .version('0.1.0');\n\nprogram\n .command('init')\n .description('Initialize Roux in a directory')\n .argument('[directory]', 'Directory to initialize', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n const result = await initCommand(resolvedDir);\n\n if (result.created) {\n console.log(`Initialized Roux in ${resolvedDir}`);\n console.log(` Config: ${result.configPath}`);\n if (result.hooksInstalled) {\n console.log(` Claude hooks: installed`);\n }\n } else {\n if (result.hooksInstalled) {\n console.log(`Upgraded Roux in ${resolvedDir}`);\n console.log(` Claude hooks: installed`);\n } else {\n console.log(`Already initialized: ${result.configPath}`);\n }\n }\n });\n\nprogram\n .command('status')\n .description('Show graph statistics')\n .argument('[directory]', 'Directory to check', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await statusCommand(resolvedDir);\n console.log('Graph Status:');\n console.log(` Nodes: ${result.nodeCount}`);\n console.log(` Edges: ${result.edgeCount}`);\n console.log(` Embeddings: ${result.embeddingCount}/${result.nodeCount}`);\n console.log(\n ` Coverage: ${(result.embeddingCoverage * 100).toFixed(1)}%`\n );\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n });\n\nprogram\n .command('serve')\n .description('Start MCP server with file watching')\n .argument('[directory]', 'Directory to serve', '.')\n .option('--no-watch', 'Disable file watching')\n .action(async (directory: string, options: { watch: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n console.log('Starting Roux server...');\n\n const handle = await serveCommand(resolvedDir, {\n watch: options.watch,\n onProgress: (current, total) => {\n process.stdout.write(\n `\\r[${current}/${total}] Generating embeddings...`\n );\n if (current === total) {\n console.log(' Done.');\n }\n },\n });\n\n console.log(`Serving ${handle.nodeCount} nodes`);\n if (handle.isWatching) {\n console.log('Watching for file changes...');\n }\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log('\\nShutting down...');\n await handle.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n // Keep process alive\n await new Promise(() => {});\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n });\n\nprogram\n .command('viz')\n .description('Generate graph visualization')\n .argument('[directory]', 'Directory to visualize', '.')\n .option('-o, --output <path>', 'Output file path')\n .option('--open', 'Open in browser after generation')\n .action(\n async (directory: string, options: { output?: string; open?: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await vizCommand(resolvedDir, {\n output: options.output,\n open: options.open,\n });\n\n console.log(\n `Generated visualization: ${result.nodeCount} nodes, ${result.edgeCount} edges`\n );\n console.log(` Output: ${result.outputPath}`);\n\n if (result.shouldOpen) {\n const openCmd =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'start'\n : 'xdg-open';\n execFile(openCmd, [result.outputPath]);\n }\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n }\n );\n\nprogram.parse();\n","import { mkdir, writeFile, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface InitResult {\n created: boolean;\n configPath: string;\n hooksInstalled: boolean;\n}\n\ninterface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n type?: string;\n url?: string;\n}\n\ninterface McpConfig {\n mcpServers?: Record<string, McpServerConfig>;\n}\n\ninterface ClaudeHook {\n type: string;\n command: string;\n}\n\ninterface ClaudeHookEntry {\n matcher: string;\n hooks: ClaudeHook[];\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: ClaudeHookEntry[];\n PostToolUse?: ClaudeHookEntry[];\n [key: string]: ClaudeHookEntry[] | undefined;\n };\n [key: string]: unknown;\n}\n\nconst DEFAULT_CONFIG = `providers:\n store:\n type: docstore\n`;\n\nconst ROUX_MCP_CONFIG: McpServerConfig = {\n command: 'npx',\n args: ['roux', 'serve', '.'],\n env: {},\n};\n\n/** Marker to identify roux's hook for idempotent updates */\nexport const HOOK_MARKER = 'roux-enforce-mcp';\n\n/** Hook command that rejects direct file operations on .md files */\nconst ENFORCE_MCP_HOOK_COMMAND = `node -e \"/* ${HOOK_MARKER} */ const d=JSON.parse(require('fs').readFileSync(0,'utf8'));const p=d.tool_input?.file_path||'';if(p.endsWith('.md')){console.error('Use mcp__roux__* tools for markdown files instead of Read/Edit/Write');process.exit(2)}\"`;\n\nconst ROUX_HOOK_ENTRY: ClaudeHookEntry = {\n matcher: 'Read|Edit|Write',\n hooks: [{ type: 'command', command: ENFORCE_MCP_HOOK_COMMAND }],\n};\n\nexport async function initCommand(directory: string): Promise<InitResult> {\n const configPath = join(directory, 'roux.yaml');\n const rouxDir = join(directory, '.roux');\n\n // Check if already initialized\n let configExists = false;\n try {\n await access(configPath);\n configExists = true;\n } catch {\n // File doesn't exist\n }\n\n // Create .roux directory regardless\n await mkdir(rouxDir, { recursive: true });\n\n // Create/update MCP config\n await updateMcpConfig(directory);\n\n // Determine store type and update Claude hooks if docstore\n const storeType = await getStoreType(directory, configExists);\n let hooksInstalled = false;\n if (storeType === 'docstore') {\n hooksInstalled = await updateClaudeSettings(directory);\n }\n\n if (configExists) {\n return { created: false, configPath, hooksInstalled };\n }\n\n // Create roux.yaml with minimal defaults\n await writeFile(configPath, DEFAULT_CONFIG, 'utf-8');\n return { created: true, configPath, hooksInstalled };\n}\n\nasync function updateMcpConfig(directory: string): Promise<void> {\n const mcpPath = join(directory, '.mcp.json');\n\n let config: McpConfig = {};\n\n // Try to read existing config\n try {\n const content = await readFile(mcpPath, 'utf-8');\n config = JSON.parse(content) as McpConfig;\n } catch {\n // File doesn't exist or invalid JSON — start fresh\n }\n\n // Ensure mcpServers object exists\n if (!config.mcpServers) {\n config.mcpServers = {};\n }\n\n // Add/update roux entry\n config.mcpServers.roux = ROUX_MCP_CONFIG;\n\n // Write back\n await writeFile(mcpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nasync function getStoreType(\n directory: string,\n configExists: boolean\n): Promise<string> {\n if (!configExists) {\n return 'docstore'; // Default\n }\n\n try {\n const configPath = join(directory, 'roux.yaml');\n const content = await readFile(configPath, 'utf-8');\n // Simple YAML parsing for store type\n const typeMatch = content.match(/store:\\s*\\n\\s*type:\\s*(\\w+)/);\n if (typeMatch?.[1]) {\n return typeMatch[1];\n }\n } catch {\n // Can't read config, assume default\n }\n\n return 'docstore';\n}\n\n/** Returns true if hooks were installed, false if skipped */\nasync function updateClaudeSettings(directory: string): Promise<boolean> {\n const claudeDir = join(directory, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n // Ensure .claude directory exists\n await mkdir(claudeDir, { recursive: true });\n\n let config: ClaudeSettings = {};\n let existingContent: string | null = null;\n\n // Try to read existing settings\n try {\n existingContent = await readFile(settingsPath, 'utf-8');\n config = JSON.parse(existingContent) as ClaudeSettings;\n } catch (err) {\n if (existingContent !== null) {\n // File exists but is malformed JSON — don't touch it\n return false;\n }\n // File doesn't exist — start fresh\n }\n\n // Ensure hooks.PreToolUse array exists\n if (!config.hooks) {\n config.hooks = {};\n }\n if (!config.hooks.PreToolUse) {\n config.hooks.PreToolUse = [];\n }\n\n // Check if our hook already exists\n const hasRouxHook = config.hooks.PreToolUse.some((entry) =>\n entry.hooks?.some((h) => h.command?.includes(HOOK_MARKER))\n );\n\n if (hasRouxHook) {\n return false; // Already installed, don't duplicate\n }\n\n // Add our hook\n config.hooks.PreToolUse.push(ROUX_HOOK_ENTRY);\n\n // Write back\n await writeFile(settingsPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return true;\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\nimport { SqliteVectorProvider } from '../../providers/vector/sqlite.js';\n\nexport interface StatusResult {\n nodeCount: number;\n edgeCount: number;\n embeddingCount: number;\n embeddingCoverage: number;\n}\n\nexport async function statusCommand(directory: string): Promise<StatusResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const cache = new Cache(cacheDir);\n const vectorProvider = new SqliteVectorProvider(cacheDir);\n\n try {\n const stats = cache.getStats();\n const embeddingCount = vectorProvider.getEmbeddingCount();\n\n const embeddingCoverage =\n stats.nodeCount === 0 ? 1 : embeddingCount / stats.nodeCount;\n\n return {\n nodeCount: stats.nodeCount,\n edgeCount: stats.edgeCount,\n embeddingCount,\n embeddingCoverage,\n };\n } finally {\n cache.close();\n vectorProvider.close();\n }\n}\n","import Database from 'better-sqlite3';\nimport { join } from 'node:path';\nimport { mkdirSync } from 'node:fs';\nimport stringSimilarity from 'string-similarity';\nimport type { Node, SourceRef } from '../../types/node.js';\nimport type {\n TagMode,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\n\nexport interface EmbeddingRecord {\n model: string;\n vector: number[];\n}\n\nexport interface CentralityRecord {\n pagerank: number;\n inDegree: number;\n outDegree: number;\n computedAt: number;\n}\n\ninterface NodeRow {\n id: string;\n title: string;\n content: string;\n tags: string;\n outgoing_links: string;\n properties: string;\n source_type: string;\n source_path: string;\n source_modified: number;\n}\n\ninterface EmbeddingRow {\n node_id: string;\n model: string;\n vector: Buffer;\n}\n\ninterface CentralityRow {\n node_id: string;\n pagerank: number;\n in_degree: number;\n out_degree: number;\n computed_at: number;\n}\n\nexport class Cache {\n private db: Database.Database;\n\n constructor(cacheDir: string) {\n mkdirSync(cacheDir, { recursive: true });\n const dbPath = join(cacheDir, 'cache.db');\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS nodes (\n id TEXT PRIMARY KEY,\n title TEXT,\n content TEXT,\n tags TEXT,\n outgoing_links TEXT,\n properties TEXT,\n source_type TEXT,\n source_path TEXT,\n source_modified INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS embeddings (\n node_id TEXT PRIMARY KEY,\n model TEXT,\n vector BLOB,\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n );\n\n CREATE TABLE IF NOT EXISTS centrality (\n node_id TEXT PRIMARY KEY,\n pagerank REAL,\n in_degree INTEGER,\n out_degree INTEGER,\n computed_at INTEGER,\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_nodes_source_path ON nodes(source_path);\n `);\n // Enable foreign key enforcement for cascade deletes\n this.db.pragma('foreign_keys = ON');\n }\n\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n upsertNode(\n node: Node,\n sourceType: string,\n sourcePath: string,\n sourceModified: number\n ): void {\n const stmt = this.db.prepare(`\n INSERT INTO nodes (id, title, content, tags, outgoing_links, properties, source_type, source_path, source_modified)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n title = excluded.title,\n content = excluded.content,\n tags = excluded.tags,\n outgoing_links = excluded.outgoing_links,\n properties = excluded.properties,\n source_type = excluded.source_type,\n source_path = excluded.source_path,\n source_modified = excluded.source_modified\n `);\n\n stmt.run(\n node.id,\n node.title,\n node.content,\n JSON.stringify(node.tags),\n JSON.stringify(node.outgoingLinks),\n JSON.stringify(node.properties),\n sourceType,\n sourcePath,\n sourceModified\n );\n }\n\n getNode(id: string): Node | null {\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE id = ?')\n .get(id) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getNodes(ids: string[]): Node[] {\n if (ids.length === 0) return [];\n\n // Fetch all nodes then order by input ids\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as NodeRow[];\n\n const nodeMap = new Map<string, Node>();\n for (const row of rows) {\n nodeMap.set(row.id, this.rowToNode(row));\n }\n\n // Return in requested order\n const result: Node[] = [];\n for (const id of ids) {\n const node = nodeMap.get(id);\n if (node) result.push(node);\n }\n return result;\n }\n\n deleteNode(id: string): void {\n this.db.prepare('DELETE FROM nodes WHERE id = ?').run(id);\n }\n\n getAllNodes(): Node[] {\n const rows = this.db.prepare('SELECT * FROM nodes').all() as NodeRow[];\n return rows.map((row) => this.rowToNode(row));\n }\n\n searchByTags(tags: string[], mode: TagMode): Node[] {\n if (tags.length === 0) return [];\n\n const allNodes = this.getAllNodes();\n const lowerTags = tags.map((t) => t.toLowerCase());\n\n return allNodes.filter((node) => {\n const nodeTags = node.tags.map((t) => t.toLowerCase());\n if (mode === 'any') {\n return lowerTags.some((t) => nodeTags.includes(t));\n } else {\n return lowerTags.every((t) => nodeTags.includes(t));\n }\n });\n }\n\n getModifiedTime(sourcePath: string): number | null {\n const row = this.db\n .prepare('SELECT source_modified FROM nodes WHERE source_path = ?')\n .get(sourcePath) as { source_modified: number } | undefined;\n\n return row?.source_modified ?? null;\n }\n\n getNodeByPath(sourcePath: string): Node | null {\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE source_path = ?')\n .get(sourcePath) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getAllTrackedPaths(): Set<string> {\n const rows = this.db\n .prepare('SELECT source_path FROM nodes')\n .all() as Array<{ source_path: string }>;\n\n return new Set(rows.map((r) => r.source_path));\n }\n\n resolveTitles(ids: string[]): Map<string, string> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id, title FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string; title: string }>;\n\n const result = new Map<string, string>();\n for (const row of rows) {\n result.set(row.id, row.title);\n }\n return result;\n }\n\n nodesExist(ids: string[]): Map<string, boolean> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string }>;\n\n const existingIds = new Set(rows.map((r) => r.id));\n const result = new Map<string, boolean>();\n for (const id of ids) {\n result.set(id, existingIds.has(id));\n }\n return result;\n }\n\n listNodes(filter: ListFilter, options?: ListOptions): ListNodesResult {\n const limit = Math.min(options?.limit ?? 100, 1000);\n const offset = options?.offset ?? 0;\n\n // Build query dynamically based on filters\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (filter.tag) {\n // Case-insensitive tag match - tags stored as JSON array\n conditions.push(\"EXISTS (SELECT 1 FROM json_each(tags) WHERE LOWER(json_each.value) = LOWER(?))\");\n params.push(filter.tag);\n }\n\n if (filter.path) {\n conditions.push(\"id LIKE ? || '%'\");\n params.push(filter.path);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Get total count of matching nodes (without limit/offset)\n const countQuery = `SELECT COUNT(*) as count FROM nodes ${whereClause}`;\n const countRow = this.db.prepare(countQuery).get(...params) as { count: number };\n const total = countRow.count;\n\n // Get paginated results\n const query = `SELECT id, title FROM nodes ${whereClause} LIMIT ? OFFSET ?`;\n const rows = this.db.prepare(query).all(...params, limit, offset) as Array<{ id: string; title: string }>;\n\n const nodes = rows.map((row) => ({ id: row.id, title: row.title }));\n return { nodes, total };\n }\n\n resolveNodes(names: string[], options?: ResolveOptions): ResolveResult[] {\n if (names.length === 0) return [];\n\n const strategy = options?.strategy ?? 'fuzzy';\n const threshold = options?.threshold ?? 0.7;\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidate nodes (applying tag/path filters)\n const { nodes: candidates } = this.listNodes(filter, { limit: 1000 });\n\n if (candidates.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const candidateTitles = candidates.map((c) => c.title.toLowerCase());\n const titleToId = new Map<string, string>();\n for (const c of candidates) {\n titleToId.set(c.title.toLowerCase(), c.id);\n }\n\n return names.map((query): ResolveResult => {\n const queryLower = query.toLowerCase();\n\n if (strategy === 'exact') {\n // Exact case-insensitive title match\n const matchedId = titleToId.get(queryLower);\n if (matchedId) {\n return { query, match: matchedId, score: 1 };\n }\n return { query, match: null, score: 0 };\n }\n\n // Fuzzy strategy using string-similarity\n if (strategy === 'fuzzy') {\n const result = stringSimilarity.findBestMatch(queryLower, candidateTitles);\n const bestMatch = result.bestMatch;\n\n if (bestMatch.rating >= threshold) {\n // bestMatch.target is guaranteed to exist in titleToId since both come from candidates\n const matchedId = titleToId.get(bestMatch.target)!;\n return { query, match: matchedId, score: bestMatch.rating };\n }\n return { query, match: null, score: 0 };\n }\n\n // Semantic strategy - not supported at cache level, return no match\n // DocStore will handle semantic by using embedding provider\n return { query, match: null, score: 0 };\n });\n }\n\n updateOutgoingLinks(nodeId: string, links: string[]): void {\n this.db\n .prepare('UPDATE nodes SET outgoing_links = ? WHERE id = ?')\n .run(JSON.stringify(links), nodeId);\n }\n\n storeEmbedding(nodeId: string, vector: number[], model: string): void {\n const buffer = Buffer.from(new Float32Array(vector).buffer);\n this.db\n .prepare(\n `\n INSERT INTO embeddings (node_id, model, vector)\n VALUES (?, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET\n model = excluded.model,\n vector = excluded.vector\n `\n )\n .run(nodeId, model, buffer);\n }\n\n getEmbedding(nodeId: string): EmbeddingRecord | null {\n const row = this.db\n .prepare('SELECT model, vector FROM embeddings WHERE node_id = ?')\n .get(nodeId) as EmbeddingRow | undefined;\n\n if (!row) return null;\n\n const float32 = new Float32Array(\n row.vector.buffer,\n row.vector.byteOffset,\n row.vector.length / 4\n );\n return {\n model: row.model,\n vector: Array.from(float32),\n };\n }\n\n storeCentrality(\n nodeId: string,\n pagerank: number,\n inDegree: number,\n outDegree: number,\n computedAt: number\n ): void {\n this.db\n .prepare(\n `\n INSERT INTO centrality (node_id, pagerank, in_degree, out_degree, computed_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET\n pagerank = excluded.pagerank,\n in_degree = excluded.in_degree,\n out_degree = excluded.out_degree,\n computed_at = excluded.computed_at\n `\n )\n .run(nodeId, pagerank, inDegree, outDegree, computedAt);\n }\n\n getCentrality(nodeId: string): CentralityRecord | null {\n const row = this.db\n .prepare('SELECT * FROM centrality WHERE node_id = ?')\n .get(nodeId) as CentralityRow | undefined;\n\n if (!row) return null;\n\n return {\n pagerank: row.pagerank,\n inDegree: row.in_degree,\n outDegree: row.out_degree,\n computedAt: row.computed_at,\n };\n }\n\n getStats(): { nodeCount: number; embeddingCount: number; edgeCount: number } {\n const nodeCount = this.db\n .prepare('SELECT COUNT(*) as count FROM nodes')\n .get() as { count: number };\n\n const embeddingCount = this.db\n .prepare('SELECT COUNT(*) as count FROM embeddings')\n .get() as { count: number };\n\n // Sum all in_degree values to get edge count\n const edgeSum = this.db\n .prepare('SELECT SUM(in_degree) as total FROM centrality')\n .get() as { total: number | null };\n\n return {\n nodeCount: nodeCount.count,\n embeddingCount: embeddingCount.count,\n edgeCount: edgeSum.total ?? 0,\n };\n }\n\n clear(): void {\n this.db.exec('DELETE FROM centrality');\n this.db.exec('DELETE FROM embeddings');\n this.db.exec('DELETE FROM nodes');\n }\n\n close(): void {\n this.db.close();\n }\n\n private rowToNode(row: NodeRow): Node {\n const sourceRef: SourceRef = {\n type: row.source_type as SourceRef['type'],\n path: row.source_path,\n lastModified: new Date(row.source_modified),\n };\n\n return {\n id: row.id,\n title: row.title,\n content: row.content,\n tags: JSON.parse(row.tags) as string[],\n outgoingLinks: JSON.parse(row.outgoing_links) as string[],\n properties: JSON.parse(row.properties) as Record<string, unknown>,\n sourceRef,\n };\n }\n}\n","import Database from 'better-sqlite3';\nimport type { Database as DatabaseType } from 'better-sqlite3';\nimport { join } from 'node:path';\nimport type { VectorProvider, VectorSearchResult } from '../../types/provider.js';\n\nexport class SqliteVectorProvider implements VectorProvider {\n private db: DatabaseType;\n private ownsDb: boolean;\n\n constructor(pathOrDb: string | DatabaseType) {\n if (typeof pathOrDb === 'string') {\n this.db = new Database(join(pathOrDb, 'vectors.db'));\n this.ownsDb = true;\n } else {\n this.db = pathOrDb;\n this.ownsDb = false;\n }\n this.init();\n }\n\n private init(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS vectors (\n id TEXT PRIMARY KEY,\n model TEXT NOT NULL,\n vector BLOB NOT NULL\n )\n `);\n }\n\n async store(id: string, vector: number[], model: string): Promise<void> {\n if (vector.length === 0) {\n throw new Error('Cannot store empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n\n // Validate dimension consistency (exclude self for overwrites)\n const existing = this.db\n .prepare('SELECT LENGTH(vector) / 4 as dim FROM vectors WHERE id != ? LIMIT 1')\n .get(id) as { dim: number } | undefined;\n\n if (existing && existing.dim !== vector.length) {\n throw new Error(\n `Dimension mismatch: cannot store ${vector.length}-dim vector, existing vectors have ${existing.dim} dimensions`\n );\n }\n\n const blob = Buffer.from(new Float32Array(vector).buffer);\n this.db\n .prepare(\n `INSERT OR REPLACE INTO vectors (id, model, vector) VALUES (?, ?, ?)`\n )\n .run(id, model, blob);\n }\n\n async search(vector: number[], limit: number): Promise<VectorSearchResult[]> {\n if (vector.length === 0) {\n throw new Error('Cannot search with empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n if (limit <= 0) {\n return [];\n }\n\n const rows = this.db\n .prepare('SELECT id, vector FROM vectors')\n .all() as Array<{ id: string; vector: Buffer }>;\n\n if (rows.length === 0) {\n return [];\n }\n\n // Check dimension mismatch against first stored vector\n const firstStoredDim = rows[0]!.vector.byteLength / 4;\n if (vector.length !== firstStoredDim) {\n throw new Error(\n `Dimension mismatch: query has ${vector.length} dimensions, stored vectors have ${firstStoredDim}`\n );\n }\n\n const queryVec = new Float32Array(vector);\n const results: VectorSearchResult[] = [];\n\n for (const row of rows) {\n const storedVec = new Float32Array(\n row.vector.buffer,\n row.vector.byteOffset,\n row.vector.byteLength / 4\n );\n const distance = cosineDistance(queryVec, storedVec);\n results.push({ id: row.id, distance });\n }\n\n results.sort((a, b) => a.distance - b.distance);\n return results.slice(0, limit);\n }\n\n async delete(id: string): Promise<void> {\n this.db.prepare('DELETE FROM vectors WHERE id = ?').run(id);\n }\n\n async getModel(id: string): Promise<string | null> {\n const row = this.db\n .prepare('SELECT model FROM vectors WHERE id = ?')\n .get(id) as { model: string } | undefined;\n return row?.model ?? null;\n }\n\n hasEmbedding(id: string): boolean {\n const row = this.db\n .prepare('SELECT 1 FROM vectors WHERE id = ?')\n .get(id);\n return row !== undefined;\n }\n\n /** For testing: get table names */\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n /** For testing: get vector blob size */\n getVectorBlobSize(id: string): number | null {\n const row = this.db\n .prepare('SELECT LENGTH(vector) as size FROM vectors WHERE id = ?')\n .get(id) as { size: number } | undefined;\n return row?.size ?? null;\n }\n\n /** Get total number of stored embeddings */\n getEmbeddingCount(): number {\n const row = this.db\n .prepare('SELECT COUNT(*) as count FROM vectors')\n .get() as { count: number };\n return row.count;\n }\n\n close(): void {\n if (this.ownsDb) {\n this.db.close();\n }\n }\n}\n\nfunction cosineDistance(a: Float32Array, b: Float32Array): number {\n let dotProduct = 0;\n let magnitudeA = 0;\n let magnitudeB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n magnitudeA += a[i]! * a[i]!;\n magnitudeB += b[i]! * b[i]!;\n }\n\n magnitudeA = Math.sqrt(magnitudeA);\n magnitudeB = Math.sqrt(magnitudeB);\n\n if (magnitudeA === 0 || magnitudeB === 0) {\n return 1; // No similarity for zero vectors\n }\n\n const similarity = dotProduct / (magnitudeA * magnitudeB);\n return 1 - similarity;\n}\n","import { access, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DocStore } from '../../providers/docstore/index.js';\nimport { TransformersEmbeddingProvider } from '../../providers/embedding/transformers.js';\nimport { GraphCoreImpl } from '../../core/graphcore.js';\nimport { McpServer, type TransportFactory } from '../../mcp/server.js';\nimport type { RouxConfig } from '../../types/config.js';\n\nexport interface ServeOptions {\n watch?: boolean;\n transportFactory?: TransportFactory;\n onProgress?: (current: number, total: number) => void;\n}\n\nexport interface ServeHandle {\n stop: () => Promise<void>;\n isWatching: boolean;\n nodeCount: number;\n}\n\nexport async function serveCommand(\n directory: string,\n options: ServeOptions = {}\n): Promise<ServeHandle> {\n const { watch = true, transportFactory, onProgress } = options;\n\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n // Load config\n const configContent = await readFile(configPath, 'utf-8');\n const config = parseYaml(configContent) as RouxConfig;\n\n // Create providers\n const sourcePath = config.source?.path ?? '.';\n const resolvedSourcePath = join(directory, sourcePath);\n const cachePath = config.cache?.path ?? '.roux';\n const resolvedCachePath = join(directory, cachePath);\n\n const store = new DocStore(resolvedSourcePath, resolvedCachePath);\n const embedding = new TransformersEmbeddingProvider(\n config.providers?.embedding?.type === 'local'\n ? config.providers.embedding.model\n : undefined\n );\n\n // Sync cache\n await store.sync();\n\n // Generate embeddings for nodes without them\n const allNodeIds = await store.getAllNodeIds();\n const total = allNodeIds.length;\n\n for (let i = 0; i < allNodeIds.length; i++) {\n const id = allNodeIds[i]!;\n\n if (!hasExistingEmbedding(store, id)) {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n }\n\n if (onProgress) {\n onProgress(i + 1, total);\n }\n }\n\n // Create GraphCore\n const core = new GraphCoreImpl();\n core.registerStore(store);\n core.registerEmbedding(embedding);\n\n // Start MCP server\n const mcpServer = new McpServer({\n core,\n store,\n hasEmbedding: true,\n });\n\n await mcpServer.start(transportFactory);\n\n // Start file watcher if enabled\n if (watch) {\n try {\n await store.startWatching(async (changedIds) => {\n // Generate embeddings for changed nodes\n for (const id of changedIds) {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n }\n });\n } catch (err) {\n console.warn(\n 'File watching disabled:',\n (err as Error).message || 'Unknown error'\n );\n }\n }\n\n return {\n stop: async () => {\n store.stopWatching();\n store.close();\n await mcpServer.close();\n },\n isWatching: store.isWatching(),\n nodeCount: allNodeIds.length,\n };\n}\n\nfunction hasExistingEmbedding(store: DocStore, id: string): boolean {\n return store.hasEmbedding(id);\n}\n","import { readFile, writeFile, stat, readdir, mkdir, rm } from 'node:fs/promises';\nimport { join, relative, dirname, resolve } from 'node:path';\nimport { watch, type FSWatcher } from 'chokidar';\nimport type { DirectedGraph } from 'graphology';\nimport type { Node } from '../../types/node.js';\nimport type {\n StoreProvider,\n NeighborOptions,\n Metric,\n TagMode,\n VectorSearchResult,\n VectorProvider,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\nimport { Cache } from './cache.js';\nimport { SqliteVectorProvider } from '../vector/sqlite.js';\nimport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n serializeToMarkdown,\n} from './parser.js';\nimport { buildGraph } from '../../graph/builder.js';\nimport {\n getNeighborIds,\n findPath as graphFindPath,\n getHubs as graphGetHubs,\n computeCentrality,\n} from '../../graph/operations.js';\n\nexport class DocStore implements StoreProvider {\n private cache: Cache;\n private sourceRoot: string;\n private graph: DirectedGraph | null = null;\n private vectorProvider: VectorProvider;\n private ownsVectorProvider: boolean;\n\n private watcher: FSWatcher | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingChanges: Map<string, 'add' | 'change' | 'unlink'> = new Map();\n private onChangeCallback: ((changedIds: string[]) => void) | undefined;\n\n constructor(\n sourceRoot: string,\n cacheDir: string,\n vectorProvider?: VectorProvider\n ) {\n this.sourceRoot = sourceRoot;\n this.cache = new Cache(cacheDir);\n this.ownsVectorProvider = !vectorProvider;\n this.vectorProvider = vectorProvider ?? new SqliteVectorProvider(cacheDir);\n }\n\n async sync(): Promise<void> {\n const currentPaths = await this.collectMarkdownFiles(this.sourceRoot);\n const trackedPaths = this.cache.getAllTrackedPaths();\n\n // Process new/modified files\n for (const filePath of currentPaths) {\n try {\n const mtime = await this.getFileMtime(filePath);\n const cachedMtime = this.cache.getModifiedTime(filePath);\n\n if (cachedMtime === null || mtime > cachedMtime) {\n const node = await this.fileToNode(filePath);\n this.cache.upsertNode(node, 'file', filePath, mtime);\n }\n } catch (err) {\n // File may have been deleted between readdir and stat — skip it\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue;\n }\n throw err;\n }\n }\n\n // Remove deleted files\n const currentSet = new Set(currentPaths);\n for (const tracked of trackedPaths) {\n if (!currentSet.has(tracked)) {\n const node = this.cache.getNodeByPath(tracked);\n if (node) {\n this.cache.deleteNode(node.id);\n }\n }\n }\n\n // Resolve wiki-links after all nodes are cached\n const filenameIndex = this.buildFilenameIndex();\n this.resolveOutgoingLinks(filenameIndex);\n\n // Rebuild graph from all nodes\n this.rebuildGraph();\n }\n\n async createNode(node: Node): Promise<void> {\n const normalizedId = normalizeId(node.id);\n this.validatePathWithinSource(normalizedId);\n\n const existing = this.cache.getNode(normalizedId);\n if (existing) {\n throw new Error(`Node already exists: ${normalizedId}`);\n }\n\n const filePath = join(this.sourceRoot, normalizedId);\n const dir = dirname(filePath);\n await mkdir(dir, { recursive: true });\n\n const parsed = {\n title: node.title,\n tags: node.tags,\n properties: node.properties,\n content: node.content,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n const mtime = await this.getFileMtime(filePath);\n const normalizedNode = { ...node, id: normalizedId };\n this.cache.upsertNode(normalizedNode, 'file', filePath, mtime);\n\n // Rebuild graph to include new node\n this.rebuildGraph();\n }\n\n async updateNode(id: string, updates: Partial<Node>): Promise<void> {\n const normalizedId = normalizeId(id);\n const existing = this.cache.getNode(normalizedId);\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n // If content is updated, reparse wiki-links\n let outgoingLinks = updates.outgoingLinks;\n if (updates.content !== undefined && outgoingLinks === undefined) {\n const rawLinks = extractWikiLinks(updates.content);\n outgoingLinks = rawLinks.map((link) => this.normalizeWikiLink(link));\n }\n\n const updated: Node = {\n ...existing,\n ...updates,\n outgoingLinks: outgoingLinks ?? existing.outgoingLinks,\n id: existing.id, // ID cannot be changed\n };\n\n const filePath = join(this.sourceRoot, existing.id);\n const parsed = {\n title: updated.title,\n tags: updated.tags,\n properties: updated.properties,\n content: updated.content,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n const mtime = await this.getFileMtime(filePath);\n this.cache.upsertNode(updated, 'file', filePath, mtime);\n\n // Rebuild graph if links changed\n if (outgoingLinks !== undefined || updates.outgoingLinks !== undefined) {\n this.rebuildGraph();\n }\n }\n\n async deleteNode(id: string): Promise<void> {\n const normalizedId = normalizeId(id);\n const existing = this.cache.getNode(normalizedId);\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n const filePath = join(this.sourceRoot, existing.id);\n await rm(filePath);\n this.cache.deleteNode(existing.id);\n await this.vectorProvider.delete(existing.id);\n\n // Rebuild graph without deleted node\n this.rebuildGraph();\n }\n\n async getNode(id: string): Promise<Node | null> {\n // Normalize ID for case-insensitive lookup\n const normalizedId = normalizeId(id);\n return this.cache.getNode(normalizedId);\n }\n\n async getNodes(ids: string[]): Promise<Node[]> {\n const normalizedIds = ids.map(normalizeId);\n return this.cache.getNodes(normalizedIds);\n }\n\n async getAllNodeIds(): Promise<string[]> {\n const nodes = this.cache.getAllNodes();\n return nodes.map((n) => n.id);\n }\n\n async searchByTags(tags: string[], mode: TagMode): Promise<Node[]> {\n return this.cache.searchByTags(tags, mode);\n }\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n let candidates: Node[];\n\n if (tags && tags.length > 0) {\n candidates = await this.searchByTags(tags, 'any');\n } else {\n candidates = this.cache.getAllNodes();\n }\n\n if (candidates.length === 0) {\n return null;\n }\n\n const randomIndex = Math.floor(Math.random() * candidates.length);\n // Safe: randomIndex is always 0 to length-1 when length > 0\n return candidates[randomIndex]!;\n }\n\n async resolveTitles(ids: string[]): Promise<Map<string, string>> {\n return this.cache.resolveTitles(ids);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.cache.listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n // For exact and fuzzy, delegate to cache\n const strategy = options?.strategy ?? 'fuzzy';\n if (strategy === 'exact' || strategy === 'fuzzy') {\n return this.cache.resolveNodes(names, options);\n }\n\n // Semantic strategy: use vector search\n // This requires embedding provider which DocStore doesn't have direct access to\n // Return unmatched for now - GraphCore will handle semantic with embedding provider\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n async nodesExist(ids: string[]): Promise<Map<string, boolean>> {\n const normalizedIds = ids.map(normalizeId);\n return this.cache.nodesExist(normalizedIds);\n }\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n this.ensureGraph();\n const neighborIds = getNeighborIds(this.graph!, id, options);\n return this.cache.getNodes(neighborIds);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n this.ensureGraph();\n return graphFindPath(this.graph!, source, target);\n }\n\n async getHubs(metric: Metric, limit: number): Promise<Array<[string, number]>> {\n this.ensureGraph();\n return graphGetHubs(this.graph!, metric, limit);\n }\n\n async storeEmbedding(\n id: string,\n vector: number[],\n model: string\n ): Promise<void> {\n return this.vectorProvider.store(id, vector, model);\n }\n\n async searchByVector(\n vector: number[],\n limit: number\n ): Promise<VectorSearchResult[]> {\n return this.vectorProvider.search(vector, limit);\n }\n\n hasEmbedding(id: string): boolean {\n return this.vectorProvider.hasEmbedding(id);\n }\n\n close(): void {\n this.stopWatching();\n this.cache.close();\n if (this.ownsVectorProvider && 'close' in this.vectorProvider) {\n (this.vectorProvider as { close: () => void }).close();\n }\n }\n\n startWatching(onChange?: (changedIds: string[]) => void): Promise<void> {\n if (this.watcher) {\n throw new Error('Already watching. Call stopWatching() first.');\n }\n\n this.onChangeCallback = onChange;\n\n return new Promise((resolve, reject) => {\n this.watcher = watch(this.sourceRoot, {\n ignoreInitial: true,\n ignored: [...DocStore.EXCLUDED_DIRS].map((dir) => `**/${dir}/**`),\n awaitWriteFinish: {\n stabilityThreshold: 100,\n },\n followSymlinks: false,\n });\n\n this.watcher\n .on('ready', () => resolve())\n .on('add', (path) => this.queueChange(path, 'add'))\n .on('change', (path) => this.queueChange(path, 'change'))\n .on('unlink', (path) => this.queueChange(path, 'unlink'))\n .on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'EMFILE') {\n console.error(\n 'File watcher hit file descriptor limit. ' +\n 'Try: ulimit -n 65536 or reduce watched files.'\n );\n }\n reject(err);\n });\n });\n }\n\n stopWatching(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.pendingChanges.clear();\n\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n }\n\n isWatching(): boolean {\n return this.watcher !== null;\n }\n\n private queueChange(filePath: string, event: 'add' | 'change' | 'unlink'): void {\n const relativePath = relative(this.sourceRoot, filePath);\n const id = normalizeId(relativePath);\n\n // Check exclusions\n if (!filePath.endsWith('.md')) {\n return;\n }\n\n // Check if path contains any excluded directory\n const pathParts = relativePath.split('/');\n for (const part of pathParts) {\n if (DocStore.EXCLUDED_DIRS.has(part)) {\n return;\n }\n }\n\n // Apply coalescing rules\n const existing = this.pendingChanges.get(id);\n\n if (existing) {\n if (existing === 'add' && event === 'change') {\n // add + change = add (keep as add)\n return;\n } else if (existing === 'add' && event === 'unlink') {\n // add + unlink = remove from queue\n this.pendingChanges.delete(id);\n } else if (existing === 'change' && event === 'unlink') {\n // change + unlink = unlink\n this.pendingChanges.set(id, 'unlink');\n }\n // change + change = change (already set, no action needed)\n } else {\n this.pendingChanges.set(id, event);\n }\n\n // Reset debounce timer\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.processQueue();\n }, 1000);\n }\n\n private async processQueue(): Promise<void> {\n const changes = new Map(this.pendingChanges);\n this.pendingChanges.clear();\n this.debounceTimer = null;\n\n const processedIds: string[] = [];\n\n for (const [id, event] of changes) {\n try {\n if (event === 'unlink') {\n const existing = this.cache.getNode(id);\n if (existing) {\n this.cache.deleteNode(id);\n await this.vectorProvider.delete(id);\n processedIds.push(id);\n }\n } else {\n // add or change\n const filePath = join(this.sourceRoot, id);\n const node = await this.fileToNode(filePath);\n const mtime = await this.getFileMtime(filePath);\n this.cache.upsertNode(node, 'file', filePath, mtime);\n processedIds.push(id);\n }\n } catch (err) {\n console.warn(`Failed to process file change for ${id}:`, err);\n }\n }\n\n // Resolve wiki-links and rebuild graph after processing all changes\n if (processedIds.length > 0) {\n const filenameIndex = this.buildFilenameIndex();\n this.resolveOutgoingLinks(filenameIndex);\n this.rebuildGraph();\n }\n\n // Call callback if provided\n if (this.onChangeCallback && processedIds.length > 0) {\n this.onChangeCallback(processedIds);\n }\n }\n\n private buildFilenameIndex(): Map<string, string[]> {\n const index = new Map<string, string[]>();\n for (const node of this.cache.getAllNodes()) {\n const basename = node.id.split('/').pop()!;\n const existing = index.get(basename) ?? [];\n existing.push(node.id);\n index.set(basename, existing);\n }\n // Sort each array alphabetically for deterministic first-match\n for (const paths of index.values()) {\n paths.sort();\n }\n return index;\n }\n\n private resolveOutgoingLinks(filenameIndex: Map<string, string[]>): void {\n // Build set of valid node IDs for quick lookup\n const validNodeIds = new Set<string>();\n for (const paths of filenameIndex.values()) {\n for (const path of paths) {\n validNodeIds.add(path);\n }\n }\n\n for (const node of this.cache.getAllNodes()) {\n const resolved = node.outgoingLinks.map((link) => {\n // If link already exists as a valid node ID, keep it\n if (validNodeIds.has(link)) {\n return link;\n }\n // Only resolve bare filenames (no path separators)\n // Partial paths like \"folder/target.md\" stay literal\n if (link.includes('/')) {\n return link;\n }\n // Try basename lookup for bare filenames\n const matches = filenameIndex.get(link);\n if (matches && matches.length > 0) {\n return matches[0]!;\n }\n return link;\n });\n\n // Only update if something changed\n if (resolved.some((r, i) => r !== node.outgoingLinks[i])) {\n this.cache.updateOutgoingLinks(node.id, resolved);\n }\n }\n }\n\n private ensureGraph(): void {\n if (!this.graph) {\n this.rebuildGraph();\n }\n }\n\n private rebuildGraph(): void {\n const nodes = this.cache.getAllNodes();\n this.graph = buildGraph(nodes);\n\n // Cache centrality metrics\n const centrality = computeCentrality(this.graph);\n const now = Date.now();\n for (const [id, metrics] of centrality) {\n this.cache.storeCentrality(id, 0, metrics.inDegree, metrics.outDegree, now);\n }\n }\n\n private static readonly EXCLUDED_DIRS = new Set(['.roux', 'node_modules', '.git', '.obsidian']);\n\n private async collectMarkdownFiles(dir: string): Promise<string[]> {\n const results: string[] = [];\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n // Directory doesn't exist yet\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip excluded directories\n if (DocStore.EXCLUDED_DIRS.has(entry.name)) {\n continue;\n }\n const nested = await this.collectMarkdownFiles(fullPath);\n results.push(...nested);\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n results.push(fullPath);\n }\n }\n\n return results;\n }\n\n private async getFileMtime(filePath: string): Promise<number> {\n const stats = await stat(filePath);\n return stats.mtimeMs;\n }\n\n private async fileToNode(filePath: string): Promise<Node> {\n const raw = await readFile(filePath, 'utf-8');\n const parsed = parseMarkdown(raw);\n\n const relativePath = relative(this.sourceRoot, filePath);\n const id = normalizeId(relativePath);\n\n // Derive title from path if not in frontmatter\n const title = parsed.title ?? titleFromPath(id);\n\n // Extract and normalize wiki links\n const rawLinks = extractWikiLinks(parsed.content);\n const outgoingLinks = rawLinks.map((link) => this.normalizeWikiLink(link));\n\n return {\n id,\n title,\n content: parsed.content,\n tags: parsed.tags,\n outgoingLinks,\n properties: parsed.properties,\n sourceRef: {\n type: 'file',\n path: filePath,\n lastModified: new Date(await this.getFileMtime(filePath)),\n },\n };\n }\n\n /**\n * Normalize a wiki-link target to an ID.\n * - If it has a file extension, normalize as-is\n * - If no extension, add .md\n * - Lowercase, forward slashes\n */\n private normalizeWikiLink(target: string): string {\n let normalized = target.toLowerCase().replace(/\\\\/g, '/');\n\n // Add .md if no file extension present\n // File extension = dot followed by 1-4 alphanumeric chars at end\n if (!this.hasFileExtension(normalized)) {\n normalized += '.md';\n }\n\n return normalized;\n }\n\n private hasFileExtension(path: string): boolean {\n // Match common file extensions: .md, .txt, .png, .json, etc.\n // Extension must contain at least one letter (to exclude .2024, .123, etc.)\n const match = path.match(/\\.([a-z0-9]{1,4})$/i);\n if (!match?.[1]) return false;\n // Require at least one letter in the extension\n return /[a-z]/i.test(match[1]);\n }\n\n private validatePathWithinSource(id: string): void {\n const resolvedPath = resolve(this.sourceRoot, id);\n const resolvedRoot = resolve(this.sourceRoot);\n\n if (!resolvedPath.startsWith(resolvedRoot + '/')) {\n throw new Error(`Path traversal detected: ${id} resolves outside source root`);\n }\n }\n}\n\nexport { Cache } from './cache.js';\nexport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n serializeToMarkdown,\n} from './parser.js';\n","import matter from 'gray-matter';\n\nexport interface ParsedMarkdown {\n title: string | undefined;\n tags: string[];\n properties: Record<string, unknown>;\n content: string;\n}\n\n/**\n * Parse markdown with YAML frontmatter.\n * Handles missing/malformed frontmatter gracefully.\n */\nexport function parseMarkdown(raw: string): ParsedMarkdown {\n let parsed: matter.GrayMatterFile<string>;\n try {\n parsed = matter(raw);\n } catch {\n // Malformed frontmatter - return content as-is\n return {\n title: undefined,\n tags: [],\n properties: {},\n content: raw,\n };\n }\n\n const data = parsed.data as Record<string, unknown>;\n\n // Extract title\n const title = typeof data['title'] === 'string' ? data['title'] : undefined;\n\n // Extract tags - must be an array of strings\n let tags: string[] = [];\n if (Array.isArray(data['tags'])) {\n tags = data['tags'].filter((t): t is string => typeof t === 'string');\n }\n\n // Extract other properties (excluding title and tags)\n const properties: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key !== 'title' && key !== 'tags') {\n properties[key] = value;\n }\n }\n\n return {\n title,\n tags,\n properties,\n content: parsed.content.trim(),\n };\n}\n\n/**\n * Extract wiki-link targets from markdown content.\n * Ignores links inside code blocks and inline code.\n * Deduplicates results.\n */\nexport function extractWikiLinks(content: string): string[] {\n // Remove code blocks first\n const withoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, '');\n\n // Remove inline code\n const withoutInlineCode = withoutCodeBlocks.replace(/`[^`]+`/g, '');\n\n // Match wiki links: [[target]] or [[target|display]]\n const linkRegex = /\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]/g;\n const seen = new Set<string>();\n const links: string[] = [];\n\n let match;\n while ((match = linkRegex.exec(withoutInlineCode)) !== null) {\n const target = match[1]?.trim();\n if (target && !seen.has(target)) {\n seen.add(target);\n links.push(target);\n }\n }\n\n return links;\n}\n\n/**\n * Normalize a file path to a consistent ID format.\n * - Lowercased\n * - Forward slashes only\n * - Preserves extension\n */\nexport function normalizeId(path: string): string {\n return path.toLowerCase().replace(/\\\\/g, '/');\n}\n\n/**\n * Derive a human-readable title from a file path.\n * - Removes directory prefix\n * - Removes extension\n * - Replaces hyphens/underscores with spaces\n * - Title-cases words\n */\nexport function titleFromPath(path: string): string {\n // Get filename without directory\n const parts = path.split(/[/\\\\]/);\n // parts is always non-empty (even '' splits to [''])\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const filename = parts.at(-1)!;\n\n // Remove extension\n const withoutExt = filename.replace(/\\.[^.]+$/, '');\n\n // Replace hyphens and underscores with spaces, collapse multiples\n const spaced = withoutExt.replace(/[-_]+/g, ' ').toLowerCase();\n\n // Title-case each word\n return spaced\n .split(' ')\n .filter((w) => w.length > 0)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Serialize parsed markdown back to a string with YAML frontmatter.\n * Omits frontmatter if no metadata is present.\n */\nexport function serializeToMarkdown(parsed: ParsedMarkdown): string {\n const hasFrontmatter =\n parsed.title !== undefined ||\n parsed.tags.length > 0 ||\n Object.keys(parsed.properties).length > 0;\n\n if (!hasFrontmatter) {\n return parsed.content;\n }\n\n // Build frontmatter object\n const frontmatter: Record<string, unknown> = {};\n\n if (parsed.title !== undefined) {\n frontmatter['title'] = parsed.title;\n }\n\n if (parsed.tags.length > 0) {\n frontmatter['tags'] = parsed.tags;\n }\n\n // Add other properties\n for (const [key, value] of Object.entries(parsed.properties)) {\n frontmatter[key] = value;\n }\n\n // Use gray-matter to stringify\n return matter.stringify(parsed.content, frontmatter);\n}\n","import { DirectedGraph } from 'graphology';\nimport type { Node } from '../types/node.js';\n\n/**\n * Build a directed graph from an array of nodes.\n * Edges are derived from each node's outgoingLinks.\n * Links to non-existent nodes are ignored.\n */\nexport function buildGraph(nodes: Node[]): DirectedGraph {\n const graph = new DirectedGraph();\n\n // First pass: add all nodes\n const nodeIds = new Set<string>();\n for (const node of nodes) {\n graph.addNode(node.id);\n nodeIds.add(node.id);\n }\n\n // Second pass: add edges (only to existing nodes)\n for (const node of nodes) {\n const seen = new Set<string>();\n for (const target of node.outgoingLinks) {\n // Skip if target doesn't exist or we've already added this edge\n if (!nodeIds.has(target) || seen.has(target)) {\n continue;\n }\n seen.add(target);\n graph.addDirectedEdge(node.id, target);\n }\n }\n\n return graph;\n}\n","import type { DirectedGraph } from 'graphology';\nimport { bidirectional } from 'graphology-shortest-path';\nimport type {\n NeighborOptions,\n Metric,\n CentralityMetrics,\n} from '../types/provider.js';\n\n/**\n * Get neighbor IDs based on direction.\n * Returns empty array if node doesn't exist.\n */\nexport function getNeighborIds(\n graph: DirectedGraph,\n id: string,\n options: NeighborOptions\n): string[] {\n if (!graph.hasNode(id)) {\n return [];\n }\n\n let neighbors: string[];\n\n switch (options.direction) {\n case 'in':\n neighbors = graph.inNeighbors(id);\n break;\n case 'out':\n neighbors = graph.outNeighbors(id);\n break;\n case 'both':\n neighbors = graph.neighbors(id);\n break;\n }\n\n if (options.limit !== undefined) {\n if (options.limit <= 0) {\n return [];\n }\n if (options.limit < neighbors.length) {\n return neighbors.slice(0, options.limit);\n }\n }\n\n return neighbors;\n}\n\n/**\n * Find shortest path between two nodes.\n * Returns array of node IDs or null if no path exists.\n */\nexport function findPath(\n graph: DirectedGraph,\n source: string,\n target: string\n): string[] | null {\n if (!graph.hasNode(source) || !graph.hasNode(target)) {\n return null;\n }\n\n if (source === target) {\n return [source];\n }\n\n const path = bidirectional(graph, source, target);\n return path;\n}\n\n/**\n * Get top nodes by centrality metric.\n * Returns array of [id, score] tuples sorted descending.\n */\nexport function getHubs(\n graph: DirectedGraph,\n metric: Metric,\n limit: number\n): Array<[string, number]> {\n if (limit <= 0) {\n return [];\n }\n\n const scores: Array<[string, number]> = [];\n\n graph.forEachNode((id) => {\n let score: number;\n switch (metric) {\n case 'in_degree':\n score = graph.inDegree(id);\n break;\n case 'out_degree':\n score = graph.outDegree(id);\n break;\n case 'pagerank':\n // PageRank is post-MVP, use in_degree as fallback\n score = graph.inDegree(id);\n break;\n }\n scores.push([id, score]);\n });\n\n scores.sort((a, b) => b[1] - a[1]);\n return scores.slice(0, limit);\n}\n\n/**\n * Compute centrality metrics for all nodes.\n * For MVP, computes in_degree and out_degree only.\n */\nexport function computeCentrality(\n graph: DirectedGraph\n): Map<string, CentralityMetrics> {\n const result = new Map<string, CentralityMetrics>();\n\n graph.forEachNode((id) => {\n result.set(id, {\n inDegree: graph.inDegree(id),\n outDegree: graph.outDegree(id),\n });\n });\n\n return result;\n}\n","import { pipeline, type FeatureExtractionPipeline } from '@xenova/transformers';\nimport type { EmbeddingProvider } from '../../types/provider.js';\n\nconst DEFAULT_MODEL = 'Xenova/all-MiniLM-L6-v2';\nconst DEFAULT_DIMENSIONS = 384;\n\nexport class TransformersEmbeddingProvider implements EmbeddingProvider {\n private model: string;\n private dims: number;\n private pipe: FeatureExtractionPipeline | null = null;\n\n constructor(model = DEFAULT_MODEL, dimensions = DEFAULT_DIMENSIONS) {\n this.model = model;\n this.dims = dimensions;\n }\n\n private async getPipeline(): Promise<FeatureExtractionPipeline> {\n if (!this.pipe) {\n this.pipe = await pipeline('feature-extraction', this.model);\n }\n return this.pipe;\n }\n\n async embed(text: string): Promise<number[]> {\n const pipe = await this.getPipeline();\n const output = await pipe(text, { pooling: 'mean', normalize: true });\n return Array.from(output.data as Float32Array);\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) {\n return [];\n }\n return Promise.all(texts.map((t) => this.embed(t)));\n }\n\n dimensions(): number {\n return this.dims;\n }\n\n modelId(): string {\n return this.model;\n }\n}\n","import type { Node, NodeWithContext } from '../types/node.js';\nimport type {\n GraphCore,\n SearchOptions,\n} from '../types/graphcore.js';\nimport type {\n StoreProvider,\n EmbeddingProvider,\n Metric,\n TagMode,\n NeighborOptions,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../types/provider.js';\nimport type { RouxConfig } from '../types/config.js';\nimport { DocStore } from '../providers/docstore/index.js';\nimport { TransformersEmbeddingProvider } from '../providers/embedding/transformers.js';\n\nexport class GraphCoreImpl implements GraphCore {\n private store: StoreProvider | null = null;\n private embedding: EmbeddingProvider | null = null;\n\n registerStore(provider: StoreProvider): void {\n if (!provider) {\n throw new Error('Store provider is required');\n }\n this.store = provider;\n }\n\n registerEmbedding(provider: EmbeddingProvider): void {\n if (!provider) {\n throw new Error('Embedding provider is required');\n }\n this.embedding = provider;\n }\n\n private requireStore(): StoreProvider {\n if (!this.store) {\n throw new Error('StoreProvider not registered');\n }\n return this.store;\n }\n\n private requireEmbedding(): EmbeddingProvider {\n if (!this.embedding) {\n throw new Error('EmbeddingProvider not registered');\n }\n return this.embedding;\n }\n\n async search(query: string, options?: SearchOptions): Promise<Node[]> {\n const store = this.requireStore();\n const embedding = this.requireEmbedding();\n\n const limit = options?.limit ?? 10;\n const vector = await embedding.embed(query);\n const results = await store.searchByVector(vector, limit);\n\n // Results are already sorted by distance ascending\n const ids = results.map((r) => r.id);\n return store.getNodes(ids);\n }\n\n async getNode(id: string, depth?: number): Promise<NodeWithContext | null> {\n const store = this.requireStore();\n const node = await store.getNode(id);\n\n if (!node) {\n return null;\n }\n\n if (!depth || depth === 0) {\n return node;\n }\n\n // Fetch neighbors for context\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n store.getNeighbors(id, { direction: 'in' }),\n store.getNeighbors(id, { direction: 'out' }),\n ]);\n\n // Deduplicate neighbors (same node could be both incoming and outgoing)\n const neighborMap = new Map<string, Node>();\n for (const n of [...incomingNeighbors, ...outgoingNeighbors]) {\n neighborMap.set(n.id, n);\n }\n\n const result: NodeWithContext = {\n ...node,\n neighbors: Array.from(neighborMap.values()),\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n\n return result;\n }\n\n async createNode(partial: Partial<Node>): Promise<Node> {\n const store = this.requireStore();\n\n if (!partial.id || partial.id.trim() === '') {\n throw new Error('Node id is required and cannot be empty');\n }\n if (!partial.title) {\n throw new Error('Node title is required');\n }\n\n const node: Node = {\n id: partial.id,\n title: partial.title,\n content: partial.content ?? '',\n tags: partial.tags ?? [],\n outgoingLinks: partial.outgoingLinks ?? [],\n properties: partial.properties ?? {},\n ...(partial.sourceRef && { sourceRef: partial.sourceRef }),\n };\n\n await store.createNode(node);\n return (await store.getNode(node.id)) ?? node;\n }\n\n async updateNode(id: string, updates: Partial<Node>): Promise<Node> {\n const store = this.requireStore();\n await store.updateNode(id, updates);\n const updated = await store.getNode(id);\n if (!updated) {\n throw new Error(`Node not found after update: ${id}`);\n }\n return updated;\n }\n\n async deleteNode(id: string): Promise<boolean> {\n const store = this.requireStore();\n try {\n await store.deleteNode(id);\n return true;\n } catch (err) {\n // Only swallow \"not found\" errors - propagate everything else\n if (err instanceof Error && /not found/i.test(err.message)) {\n return false;\n }\n throw err;\n }\n }\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n const store = this.requireStore();\n return store.getNeighbors(id, options);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n const store = this.requireStore();\n return store.findPath(source, target);\n }\n\n async getHubs(\n metric: Metric,\n limit: number\n ): Promise<Array<[string, number]>> {\n const store = this.requireStore();\n return store.getHubs(metric, limit);\n }\n\n async searchByTags(\n tags: string[],\n mode: TagMode,\n limit?: number\n ): Promise<Node[]> {\n const store = this.requireStore();\n const results = await store.searchByTags(tags, mode);\n if (limit !== undefined) {\n return results.slice(0, limit);\n }\n return results;\n }\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n const store = this.requireStore();\n return store.getRandomNode(tags);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.requireStore().listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n const store = this.requireStore();\n const strategy = options?.strategy ?? 'fuzzy';\n\n // Semantic strategy requires embedding provider\n if (strategy === 'semantic') {\n if (!this.embedding) {\n throw new Error('Semantic resolution requires EmbeddingProvider');\n }\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidates from store with filters\n const { nodes: candidates } = await store.listNodes(filter, { limit: 1000 });\n\n if (candidates.length === 0 || names.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const threshold = options?.threshold ?? 0.7;\n\n // Embed all queries in batch\n const queryVectors = await this.embedding.embedBatch(names);\n\n // Embed all candidate titles in batch\n const candidateTitles = candidates.map((c) => c.title);\n const candidateVectors = await this.embedding.embedBatch(candidateTitles);\n\n // Validate dimensions match\n if (queryVectors.length > 0 && candidateVectors.length > 0) {\n const queryDim = queryVectors[0]!.length;\n const candidateDim = candidateVectors[0]!.length;\n if (queryDim !== candidateDim) {\n throw new Error(\n `Embedding dimension mismatch: query=${queryDim}, candidate=${candidateDim}`\n );\n }\n }\n\n // For each query, find best matching candidate by cosine similarity\n return names.map((query, qIdx): ResolveResult => {\n const queryVector = queryVectors[qIdx]!;\n let bestScore = 0;\n let bestMatch: string | null = null;\n\n for (let cIdx = 0; cIdx < candidates.length; cIdx++) {\n const similarity = this.cosineSimilarity(queryVector, candidateVectors[cIdx]!);\n if (similarity > bestScore) {\n bestScore = similarity;\n bestMatch = candidates[cIdx]!.id;\n }\n }\n\n if (bestScore >= threshold) {\n return { query, match: bestMatch, score: bestScore };\n }\n return { query, match: null, score: 0 };\n });\n }\n\n // Exact and fuzzy delegate to store\n return store.resolveNodes(names, options);\n }\n\n private cosineSimilarity(a: number[], b: number[]): number {\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n if (normA === 0 || normB === 0) return 0;\n return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));\n }\n\n static fromConfig(config: RouxConfig): GraphCoreImpl {\n if (!config.providers?.store) {\n throw new Error('StoreProvider configuration is required');\n }\n\n const core = new GraphCoreImpl();\n\n // Create store based on config\n if (config.providers.store.type === 'docstore') {\n const sourcePath = config.source?.path ?? '.';\n const cachePath = config.cache?.path ?? '.roux';\n const store = new DocStore(sourcePath, cachePath);\n core.registerStore(store);\n } else {\n throw new Error(\n `Unsupported store provider type: ${config.providers.store.type}. Supported: docstore`\n );\n }\n\n // Create embedding provider (defaults to local transformers)\n const embeddingConfig = config.providers.embedding;\n if (!embeddingConfig || embeddingConfig.type === 'local') {\n const model = embeddingConfig?.model;\n const embedding = new TransformersEmbeddingProvider(model);\n core.registerEmbedding(embedding);\n } else {\n throw new Error(\n `Unsupported embedding provider type: ${embeddingConfig.type}. Supported: local`\n );\n }\n\n return core;\n }\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport type { GraphCore } from '../types/graphcore.js';\nimport type { StoreProvider } from '../types/provider.js';\nimport { McpError } from './types.js';\nimport { dispatchTool, type HandlerContext } from './handlers.js';\n\nexport interface McpServerOptions {\n /** GraphCore instance for operations */\n core: GraphCore;\n /** StoreProvider for link resolution and direct store access */\n store: StoreProvider;\n /** Whether embedding provider is available (enables search tool) */\n hasEmbedding: boolean;\n}\n\n/** MCP Transport interface for server connection. */\nexport interface McpTransport {\n start?(): Promise<void>;\n close?(): Promise<void>;\n}\n\n/** Factory function to create a transport. Defaults to StdioServerTransport. */\nexport type TransportFactory = () => McpTransport;\n\n/** Tool input schemas as JSON Schema objects */\nconst TOOL_SCHEMAS = {\n search: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Natural language search query',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['query'],\n },\n\n get_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID (file path for DocStore). ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n depth: {\n type: 'integer',\n minimum: 0,\n maximum: 1,\n default: 0,\n description: '0 = node only, 1 = include neighbors',\n },\n },\n required: ['id'],\n },\n\n get_neighbors: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Source node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n direction: {\n type: 'string',\n enum: ['in', 'out', 'both'],\n default: 'both',\n description: 'in = nodes linking here, out = nodes linked to, both = all',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 20,\n description: 'Maximum neighbors to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['id'],\n },\n\n find_path: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n description:\n 'Start node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n target: {\n type: 'string',\n description:\n 'End node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['source', 'target'],\n },\n\n get_hubs: {\n type: 'object',\n properties: {\n metric: {\n type: 'string',\n enum: ['in_degree', 'out_degree'],\n default: 'in_degree',\n description: 'Centrality metric',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results',\n },\n },\n },\n\n search_by_tags: {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n minItems: 1,\n description: 'Tags to match',\n },\n mode: {\n type: 'string',\n enum: ['any', 'all'],\n default: 'any',\n description: 'any = OR matching, all = AND matching',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 100,\n default: 20,\n description: 'Maximum results',\n },\n },\n required: ['tags'],\n },\n\n random_node: {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional: limit to nodes with these tags (any match)',\n },\n },\n },\n\n create_node: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'Node title (becomes filename for DocStore). Returned ID will be normalized to lowercase.',\n },\n content: {\n type: 'string',\n description: 'Full text content (markdown)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n default: [],\n description: 'Classification tags',\n },\n directory: {\n type: 'string',\n description: \"Optional: subdirectory path (e.g., 'notes/drafts')\",\n },\n },\n required: ['title', 'content'],\n },\n\n update_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to update. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n title: {\n type: 'string',\n description: 'New title (renames file for DocStore)',\n },\n content: {\n type: 'string',\n description: 'New content (replaces entirely)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'New tags (replaces existing)',\n },\n },\n required: ['id'],\n },\n\n delete_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to delete. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['id'],\n },\n\n list_nodes: {\n type: 'object',\n properties: {\n tag: {\n type: 'string',\n description:\n 'Filter by tag from the \"tags\" frontmatter array (case-insensitive). Does NOT search other frontmatter fields like \"type\" or \"category\".',\n },\n path: {\n type: 'string',\n description: 'Filter by path prefix (startsWith, case-insensitive)',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 1000,\n default: 100,\n description: 'Maximum results to return',\n },\n offset: {\n type: 'integer',\n minimum: 0,\n default: 0,\n description: 'Skip this many results (for pagination)',\n },\n },\n },\n\n resolve_nodes: {\n type: 'object',\n properties: {\n names: {\n type: 'array',\n items: { type: 'string' },\n description: 'Names to resolve to existing nodes',\n },\n strategy: {\n type: 'string',\n enum: ['exact', 'fuzzy', 'semantic'],\n default: 'fuzzy',\n description:\n 'How to match names to nodes. \"exact\": case-insensitive title equality. \"fuzzy\": string similarity (Dice coefficient) — use for typos, misspellings, partial matches. \"semantic\": embedding cosine similarity — use for synonyms or related concepts (NOT typos). Misspellings embed poorly because they produce unrelated vectors.',\n },\n threshold: {\n type: 'number',\n minimum: 0,\n maximum: 1,\n default: 0.7,\n description:\n 'Minimum similarity score (0-1). Lower values match more loosely. For typo tolerance, use fuzzy with threshold 0.5-0.6. Ignored for exact strategy.',\n },\n tag: {\n type: 'string',\n description:\n 'Filter candidates by tag from \"tags\" frontmatter array (case-insensitive)',\n },\n path: {\n type: 'string',\n description: 'Filter candidates by path prefix (case-insensitive)',\n },\n },\n required: ['names'],\n },\n\n nodes_exist: {\n type: 'object',\n properties: {\n ids: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Node IDs to check existence. IDs are normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['ids'],\n },\n} as const;\n\n/** Tool definitions for MCP protocol */\nexport function getToolDefinitions(hasEmbedding: boolean): Tool[] {\n const tools: Tool[] = [\n {\n name: 'get_node',\n description:\n 'Retrieve a single node by ID with optional neighbor context',\n inputSchema: TOOL_SCHEMAS.get_node,\n },\n {\n name: 'get_neighbors',\n description: 'Get nodes linked to or from a specific node',\n inputSchema: TOOL_SCHEMAS.get_neighbors,\n },\n {\n name: 'find_path',\n description: 'Find the shortest path between two nodes',\n inputSchema: TOOL_SCHEMAS.find_path,\n },\n {\n name: 'get_hubs',\n description: 'Get the most central nodes by graph metric',\n inputSchema: TOOL_SCHEMAS.get_hubs,\n },\n {\n name: 'search_by_tags',\n description: 'Filter nodes by tags (AND or OR matching)',\n inputSchema: TOOL_SCHEMAS.search_by_tags,\n },\n {\n name: 'random_node',\n description: 'Get a random node for discovery, optionally filtered by tags',\n inputSchema: TOOL_SCHEMAS.random_node,\n },\n {\n name: 'create_node',\n description: 'Create a new node (writes file for DocStore)',\n inputSchema: TOOL_SCHEMAS.create_node,\n },\n {\n name: 'update_node',\n description:\n 'Update an existing node. Title changes rejected if incoming links exist.',\n inputSchema: TOOL_SCHEMAS.update_node,\n },\n {\n name: 'delete_node',\n description: 'Delete a node by ID',\n inputSchema: TOOL_SCHEMAS.delete_node,\n },\n {\n name: 'list_nodes',\n description:\n 'List nodes with optional filters and pagination. Tag filter searches the \"tags\" frontmatter array only. All IDs returned are lowercase.',\n inputSchema: TOOL_SCHEMAS.list_nodes,\n },\n {\n name: 'resolve_nodes',\n description:\n 'Batch resolve names to existing node IDs. Strategy selection: \"exact\" for known titles, \"fuzzy\" for typos/misspellings (e.g., \"chikken\" -> \"chicken\"), \"semantic\" for synonyms/concepts (e.g., \"poultry leg meat\" -> \"chicken thigh\"). Semantic does NOT handle typos — misspellings produce garbage embeddings.',\n inputSchema: TOOL_SCHEMAS.resolve_nodes,\n },\n {\n name: 'nodes_exist',\n description:\n 'Batch check if node IDs exist. IDs are normalized to lowercase before checking.',\n inputSchema: TOOL_SCHEMAS.nodes_exist,\n },\n ];\n\n if (hasEmbedding) {\n tools.unshift({\n name: 'search',\n description: 'Semantic similarity search across all nodes',\n inputSchema: TOOL_SCHEMAS.search,\n });\n }\n\n return tools;\n}\n\nexport class McpServer {\n private server: Server;\n private ctx: HandlerContext;\n\n constructor(options: McpServerOptions) {\n this.ctx = {\n core: options.core,\n store: options.store,\n hasEmbedding: options.hasEmbedding,\n };\n\n this.server = new Server(\n { name: 'roux', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n this.setupHandlers();\n }\n\n /* v8 ignore start - MCP SDK callbacks tested via integration in Phase 11 */\n private setupHandlers(): void {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: getToolDefinitions(this.ctx.hasEmbedding),\n }));\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await dispatchTool(this.ctx, name, args ?? {});\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n };\n } catch (error) {\n if (error instanceof McpError) {\n return {\n content: [\n { type: 'text', text: JSON.stringify(error.toResponse()) },\n ],\n isError: true,\n };\n }\n const mcpError = new McpError(\n 'PROVIDER_ERROR',\n error instanceof Error ? error.message : 'Unknown error'\n );\n return {\n content: [\n { type: 'text', text: JSON.stringify(mcpError.toResponse()) },\n ],\n isError: true,\n };\n }\n });\n }\n /* v8 ignore stop */\n\n /**\n * Start the server with optional transport factory.\n * @param transportFactory Factory to create transport. Defaults to StdioServerTransport.\n */\n async start(transportFactory?: TransportFactory): Promise<void> {\n /* v8 ignore start - Default stdio transport tested via integration */\n const transport = transportFactory\n ? transportFactory()\n : new StdioServerTransport();\n /* v8 ignore stop */\n await this.server.connect(transport as Parameters<typeof this.server.connect>[0]);\n }\n\n async close(): Promise<void> {\n await this.server.close();\n }\n}\n\n/**\n * Create and start an MCP server.\n * @param options Server configuration\n * @param transportFactory Optional transport factory for testing\n */\nexport async function createMcpServer(\n options: McpServerOptions,\n transportFactory?: TransportFactory\n): Promise<McpServer> {\n const server = new McpServer(options);\n await server.start(transportFactory);\n return server;\n}\n","import type { LinkInfo } from '../types/provider.js';\n\n/** Link with resolved human-readable title. Re-export for MCP layer. */\nexport type { LinkInfo };\n\n/** Metadata-only response for browsing operations (search, get_neighbors). */\nexport interface NodeMetadataResponse {\n id: string;\n title: string;\n tags: string[];\n links: LinkInfo[];\n properties: Record<string, unknown>;\n}\n\n/** Full response including content (for get_node, create, update, etc.). */\nexport interface NodeResponse extends NodeMetadataResponse {\n content: string;\n}\n\n/** Extended response for get_node with depth > 0. */\nexport interface NodeWithContextResponse extends NodeResponse {\n incomingNeighbors: NodeResponse[];\n outgoingNeighbors: NodeResponse[];\n incomingCount: number;\n outgoingCount: number;\n}\n\n/** Search results include similarity score. Content optional based on include_content param. */\nexport interface SearchResultResponse extends NodeMetadataResponse {\n score: number;\n content?: string;\n}\n\n/** Hub results pair ID with metric value. */\nexport interface HubResponse {\n id: string;\n title: string;\n score: number;\n}\n\n/** Path results are ordered node IDs. */\nexport interface PathResponse {\n path: string[];\n length: number;\n}\n\n/** Delete operation result. */\nexport interface DeleteResponse {\n deleted: boolean;\n}\n\n/** Standard error response shape. */\nexport interface ErrorResponse {\n error: {\n code: ErrorCode;\n message: string;\n };\n}\n\n/** Known error codes. */\nexport type ErrorCode =\n | 'INVALID_PARAMS'\n | 'NODE_EXISTS'\n | 'NODE_NOT_FOUND'\n | 'LINK_INTEGRITY'\n | 'PROVIDER_ERROR';\n\n/** Custom error class for MCP operations. */\nexport class McpError extends Error {\n constructor(\n public readonly code: ErrorCode,\n message: string\n ) {\n super(message);\n this.name = 'McpError';\n }\n\n toResponse(): ErrorResponse {\n return {\n error: {\n code: this.code,\n message: this.message,\n },\n };\n }\n}\n","/** Content truncation limits by context. */\nexport const TRUNCATION_LIMITS = {\n /** Primary node (get_node, single result) */\n primary: 10_000,\n /** List results (search, neighbors) */\n list: 500,\n /** Neighbor nodes in context */\n neighbor: 200,\n} as const;\n\nexport type TruncationContext = keyof typeof TRUNCATION_LIMITS;\n\nconst TRUNCATION_SUFFIX = '... [truncated]';\n\n/**\n * Truncate content to limit, appending suffix if truncated.\n * Returns original content if within limit.\n */\nexport function truncateContent(\n content: string,\n context: TruncationContext\n): string {\n const limit = TRUNCATION_LIMITS[context];\n\n if (content.length <= limit) {\n return content;\n }\n\n // Account for suffix length when truncating, guard against negative\n const truncatedLength = Math.max(0, limit - TRUNCATION_SUFFIX.length);\n return content.slice(0, truncatedLength) + TRUNCATION_SUFFIX;\n}\n\n/**\n * Check if content was truncated (ends with truncation suffix).\n */\nexport function isTruncated(content: string): boolean {\n return content.endsWith(TRUNCATION_SUFFIX);\n}\n","import type { Node } from '../types/node.js';\nimport type { LinkInfo, StoreProvider } from '../types/provider.js';\nimport type {\n NodeResponse,\n NodeMetadataResponse,\n NodeWithContextResponse,\n SearchResultResponse,\n HubResponse,\n PathResponse,\n} from './types.js';\nimport { truncateContent, type TruncationContext } from './truncate.js';\n\n/** Maximum neighbors to include in NodeWithContextResponse. */\nexport const MAX_NEIGHBORS = 20;\n\n/** Maximum links to resolve titles for (prevents OOM on nodes with massive link counts). */\nexport const MAX_LINKS_TO_RESOLVE = 100;\n\n/**\n * Transform a Node to NodeResponse, resolving link titles.\n */\nexport async function nodeToResponse(\n node: Node,\n store: StoreProvider,\n truncation: TruncationContext\n): Promise<NodeResponse> {\n // Limit links to prevent OOM on nodes with massive outgoing link counts\n const linksToResolve = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n const titles = await store.resolveTitles(linksToResolve);\n\n const links: LinkInfo[] = linksToResolve.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n }));\n\n return {\n id: node.id,\n title: node.title,\n content: truncateContent(node.content, truncation),\n tags: node.tags,\n links,\n properties: node.properties,\n };\n}\n\n/**\n * Transform multiple nodes to responses, resolving all link titles in batch.\n * When includeContent is false, returns NodeMetadataResponse[] (no content field).\n * When includeContent is true, returns NodeResponse[] (with truncated content).\n */\nexport async function nodesToResponses(\n nodes: Node[],\n store: StoreProvider,\n truncation: TruncationContext,\n includeContent: boolean\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n // Collect unique link IDs, limiting per-node to prevent OOM\n const allLinkIds = new Set<string>();\n const nodeLinkLimits = new Map<string, string[]>();\n\n for (const node of nodes) {\n const limitedLinks = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n nodeLinkLimits.set(node.id, limitedLinks);\n for (const linkId of limitedLinks) {\n allLinkIds.add(linkId);\n }\n }\n\n // Batch resolve titles\n const titles = await store.resolveTitles(Array.from(allLinkIds));\n\n // Transform nodes using limited links\n return nodes.map((node) => {\n // Defensive fallback - all node IDs are populated in the loop above\n const limitedLinks = nodeLinkLimits.get(node.id) /* v8 ignore next */ ?? [];\n const base: NodeMetadataResponse = {\n id: node.id,\n title: node.title,\n tags: node.tags,\n links: limitedLinks.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n })),\n properties: node.properties,\n };\n\n if (includeContent) {\n return {\n ...base,\n content: truncateContent(node.content, truncation),\n } as NodeResponse;\n }\n\n return base;\n });\n}\n\n/**\n * Transform a Node with neighbor context to NodeWithContextResponse.\n */\nexport async function nodeToContextResponse(\n node: Node,\n incomingNeighbors: Node[],\n outgoingNeighbors: Node[],\n store: StoreProvider\n): Promise<NodeWithContextResponse> {\n // Get primary node response\n const primary = await nodeToResponse(node, store, 'primary');\n\n // Limit neighbors\n const limitedIncoming = incomingNeighbors.slice(0, MAX_NEIGHBORS);\n const limitedOutgoing = outgoingNeighbors.slice(0, MAX_NEIGHBORS);\n\n // Transform neighbors with neighbor truncation\n // Context responses always include content (truncated for neighbors)\n const [incomingResponses, outgoingResponses] = await Promise.all([\n nodesToResponses(limitedIncoming, store, 'neighbor', true) as Promise<NodeResponse[]>,\n nodesToResponses(limitedOutgoing, store, 'neighbor', true) as Promise<NodeResponse[]>,\n ]);\n\n return {\n ...primary,\n incomingNeighbors: incomingResponses,\n outgoingNeighbors: outgoingResponses,\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n}\n\n/**\n * Transform search results with scores.\n * When includeContent is false, content field is omitted.\n * When includeContent is true, content is truncated and included.\n */\nexport async function nodesToSearchResults(\n nodes: Node[],\n scores: Map<string, number>,\n store: StoreProvider,\n includeContent: boolean\n): Promise<SearchResultResponse[]> {\n const responses = await nodesToResponses(nodes, store, 'list', includeContent);\n\n return responses.map((response) => ({\n ...response,\n score: scores.get(response.id) ?? 0,\n }));\n}\n\n/**\n * Transform hub results (id, score pairs) to HubResponse[].\n */\nexport async function hubsToResponses(\n hubs: Array<[string, number]>,\n store: StoreProvider\n): Promise<HubResponse[]> {\n const ids = hubs.map(([id]) => id);\n const titles = await store.resolveTitles(ids);\n\n return hubs.map(([id, score]) => ({\n id,\n title: titles.get(id) ?? id,\n score,\n }));\n}\n\n/**\n * Transform a path (array of node IDs) to PathResponse.\n */\nexport function pathToResponse(path: string[]): PathResponse {\n return {\n path,\n length: path.length - 1,\n };\n}\n","import type { GraphCore } from '../types/graphcore.js';\nimport type {\n StoreProvider,\n Metric,\n TagMode,\n ListFilter,\n ResolveOptions,\n ResolveStrategy,\n NodeSummary,\n ResolveResult,\n} from '../types/provider.js';\nimport type { Node } from '../types/node.js';\nimport {\n McpError,\n type NodeResponse,\n type NodeMetadataResponse,\n type NodeWithContextResponse,\n type SearchResultResponse,\n type HubResponse,\n type PathResponse,\n type DeleteResponse,\n} from './types.js';\nimport {\n nodeToResponse,\n nodesToResponses,\n nodeToContextResponse,\n nodesToSearchResults,\n hubsToResponses,\n pathToResponse,\n} from './transforms.js';\n\nexport interface HandlerContext {\n core: GraphCore;\n store: StoreProvider;\n hasEmbedding: boolean;\n}\n\nfunction coerceLimit(value: unknown, defaultValue: number): number {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return defaultValue;\n }\n const floored = Math.floor(num);\n if (floored < 1) {\n throw new McpError('INVALID_PARAMS', 'limit must be at least 1');\n }\n return floored;\n}\n\nfunction coerceOffset(value: unknown, defaultValue: number): number {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return defaultValue;\n }\n const floored = Math.floor(num);\n if (floored < 0) {\n throw new McpError('INVALID_PARAMS', 'offset must be at least 0');\n }\n return floored;\n}\n\nexport interface ListNodesResponse {\n nodes: NodeSummary[];\n total: number;\n}\n\nexport interface NodesExistResponse {\n [id: string]: boolean;\n}\n\nexport type ToolResult =\n | NodeResponse\n | NodeMetadataResponse\n | NodeWithContextResponse\n | SearchResultResponse[]\n | NodeResponse[]\n | NodeMetadataResponse[]\n | HubResponse[]\n | PathResponse\n | DeleteResponse\n | ListNodesResponse\n | ResolveResult[]\n | NodesExistResponse\n | null;\n\nexport async function handleSearch(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<SearchResultResponse[]> {\n if (!ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Search requires embedding provider');\n }\n\n const query = args.query;\n const limit = coerceLimit(args.limit, 10);\n const includeContent = args.include_content === true;\n\n if (typeof query !== 'string' || query.trim() === '') {\n throw new McpError('INVALID_PARAMS', 'query is required and must be a non-empty string');\n }\n\n const nodes = await ctx.core.search(query, { limit });\n\n // Approximate scores for display. Results are sorted by actual similarity (distance-based).\n // Score is a UI hint (higher = better match), not the raw similarity metric.\n const scores = new Map<string, number>();\n nodes.forEach((node, index) => {\n scores.set(node.id, Math.max(0, 1 - index * 0.05));\n });\n\n return nodesToSearchResults(nodes, scores, ctx.store, includeContent);\n}\n\nfunction coerceDepth(value: unknown): number {\n if (value === undefined || value === null) {\n return 0;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return 0;\n }\n return num >= 1 ? 1 : 0;\n}\n\nexport async function handleGetNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | NodeWithContextResponse | null> {\n const id = args.id as string;\n const depth = coerceDepth(args.depth);\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n const node = await ctx.core.getNode(id, depth);\n if (!node) {\n return null;\n }\n\n if (depth === 0) {\n return nodeToResponse(node, ctx.store, 'primary');\n }\n\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n ctx.core.getNeighbors(id, { direction: 'in' }),\n ctx.core.getNeighbors(id, { direction: 'out' }),\n ]);\n\n return nodeToContextResponse(node, incomingNeighbors, outgoingNeighbors, ctx.store);\n}\n\nconst VALID_DIRECTIONS = ['in', 'out', 'both'] as const;\n\nexport async function handleGetNeighbors(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n const id = args.id as string;\n const directionRaw = args.direction ?? 'both';\n const limit = coerceLimit(args.limit, 20);\n const includeContent = args.include_content === true;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n if (!VALID_DIRECTIONS.includes(directionRaw as (typeof VALID_DIRECTIONS)[number])) {\n throw new McpError(\n 'INVALID_PARAMS',\n `direction must be one of: ${VALID_DIRECTIONS.join(', ')}`\n );\n }\n const direction = directionRaw as 'in' | 'out' | 'both';\n\n const neighbors = await ctx.core.getNeighbors(id, { direction, limit });\n return nodesToResponses(neighbors, ctx.store, 'list', includeContent);\n}\n\nexport async function handleFindPath(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<PathResponse | null> {\n const source = args.source as string;\n const target = args.target as string;\n\n if (!source || typeof source !== 'string') {\n throw new McpError('INVALID_PARAMS', 'source is required and must be a string');\n }\n if (!target || typeof target !== 'string') {\n throw new McpError('INVALID_PARAMS', 'target is required and must be a string');\n }\n\n const path = await ctx.core.findPath(source, target);\n if (!path) {\n return null;\n }\n\n return pathToResponse(path);\n}\n\nconst VALID_METRICS = ['pagerank', 'in_degree', 'out_degree'] as const;\n\nexport async function handleGetHubs(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<HubResponse[]> {\n const metricRaw = args.metric ?? 'in_degree';\n const limit = coerceLimit(args.limit, 10);\n\n if (!VALID_METRICS.includes(metricRaw as Metric)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `metric must be one of: ${VALID_METRICS.join(', ')}`\n );\n }\n const metric = metricRaw as Metric;\n\n const hubs = await ctx.core.getHubs(metric, limit);\n return hubsToResponses(hubs, ctx.store);\n}\n\nconst VALID_TAG_MODES = ['any', 'all'] as const;\n\nexport async function handleSearchByTags(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[]> {\n const tags = args.tags;\n const modeRaw = args.mode ?? 'any';\n const limit = coerceLimit(args.limit, 20);\n\n if (!Array.isArray(tags) || tags.length === 0) {\n throw new McpError('INVALID_PARAMS', 'tags is required and must be a non-empty array');\n }\n\n // Validate all elements are strings\n if (!tags.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n\n if (!VALID_TAG_MODES.includes(modeRaw as TagMode)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `mode must be one of: ${VALID_TAG_MODES.join(', ')}`\n );\n }\n const mode = modeRaw as TagMode;\n\n const nodes = await ctx.core.searchByTags(tags, mode, limit);\n // searchByTags always includes content (not a browsing operation like search/get_neighbors)\n return nodesToResponses(nodes, ctx.store, 'list', true) as Promise<NodeResponse[]>;\n}\n\nexport async function handleRandomNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | null> {\n const tags = args.tags;\n\n if (tags !== undefined) {\n if (!Array.isArray(tags) || !tags.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n }\n\n const node = await ctx.core.getRandomNode(tags as string[] | undefined);\n if (!node) {\n return null;\n }\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n\nexport async function handleCreateNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const title = args.title as string;\n const content = args.content as string;\n const tagsRaw = args.tags;\n const directory = args.directory as string | undefined;\n\n if (!title || typeof title !== 'string') {\n throw new McpError('INVALID_PARAMS', 'title is required and must be a string');\n }\n if (!content || typeof content !== 'string') {\n throw new McpError('INVALID_PARAMS', 'content is required and must be a string');\n }\n\n let tags: string[] = [];\n if (tagsRaw !== undefined) {\n if (!Array.isArray(tagsRaw) || !tagsRaw.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n tags = tagsRaw;\n }\n\n const filename = sanitizeFilename(title) + '.md';\n const id = directory ? `${directory}/${filename}` : filename;\n\n const existing = await ctx.core.getNode(id);\n if (existing) {\n throw new McpError('NODE_EXISTS', `Node already exists: ${id}`);\n }\n\n const node = await ctx.core.createNode({\n id,\n title,\n content,\n tags,\n });\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n\nexport async function handleUpdateNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const id = args.id as string;\n const title = args.title as string | undefined;\n const content = args.content as string | undefined;\n const tagsRaw = args.tags;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n if (title === undefined && content === undefined && tagsRaw === undefined) {\n throw new McpError(\n 'INVALID_PARAMS',\n 'At least one of title, content, or tags must be provided'\n );\n }\n\n let tags: string[] | undefined;\n if (tagsRaw !== undefined) {\n if (!Array.isArray(tagsRaw) || !tagsRaw.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n tags = tagsRaw;\n }\n\n const existing = await ctx.core.getNode(id);\n if (!existing) {\n throw new McpError('NODE_NOT_FOUND', `Node not found: ${id}`);\n }\n\n if (title !== undefined && title !== existing.title) {\n const incomingNeighbors = await ctx.core.getNeighbors(id, { direction: 'in' });\n if (incomingNeighbors.length > 0) {\n throw new McpError(\n 'LINK_INTEGRITY',\n `Cannot rename node with ${incomingNeighbors.length} incoming links`\n );\n }\n }\n\n const updates: Partial<Node> = {};\n if (title !== undefined) updates.title = title;\n if (content !== undefined) updates.content = content;\n if (tags !== undefined) updates.tags = tags;\n\n const updated = await ctx.core.updateNode(id, updates);\n return nodeToResponse(updated, ctx.store, 'primary');\n}\n\nexport async function handleDeleteNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<DeleteResponse> {\n const id = args.id as string;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n const deleted = await ctx.core.deleteNode(id);\n return { deleted };\n}\n\nconst VALID_STRATEGIES = ['exact', 'fuzzy', 'semantic'] as const;\n\nexport async function handleListNodes(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ListNodesResponse> {\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n const limit = coerceLimit(args.limit, 100);\n const offset = coerceOffset(args.offset, 0);\n\n const filter: ListFilter = {};\n if (tag) filter.tag = tag;\n if (path) filter.path = path;\n\n return ctx.core.listNodes(filter, { limit, offset });\n}\n\nexport async function handleResolveNodes(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ResolveResult[]> {\n const names = args.names;\n const strategy = args.strategy as ResolveStrategy | undefined;\n const threshold = args.threshold as number | undefined;\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n\n if (!Array.isArray(names)) {\n throw new McpError('INVALID_PARAMS', 'names is required and must be an array');\n }\n\n if (strategy !== undefined && !VALID_STRATEGIES.includes(strategy as ResolveStrategy)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `strategy must be one of: ${VALID_STRATEGIES.join(', ')}`\n );\n }\n\n if (strategy === 'semantic' && !ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Semantic resolution requires embedding provider');\n }\n\n const options: ResolveOptions = {};\n if (strategy) options.strategy = strategy;\n if (threshold !== undefined) options.threshold = threshold;\n if (tag) options.tag = tag;\n if (path) options.path = path;\n\n return ctx.core.resolveNodes(names as string[], options);\n}\n\nexport async function handleNodesExist(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodesExistResponse> {\n const ids = args.ids;\n\n if (!Array.isArray(ids)) {\n throw new McpError('INVALID_PARAMS', 'ids is required and must be an array');\n }\n\n const result = await ctx.store.nodesExist(ids as string[]);\n\n // Convert Map to plain object\n const response: NodesExistResponse = {};\n for (const [id, exists] of result) {\n response[id] = exists;\n }\n return response;\n}\n\nexport function sanitizeFilename(title: string): string {\n const sanitized = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return sanitized || 'untitled';\n}\n\nexport async function dispatchTool(\n ctx: HandlerContext,\n name: string,\n args: Record<string, unknown>\n): Promise<ToolResult> {\n switch (name) {\n case 'search':\n return handleSearch(ctx, args);\n case 'get_node':\n return handleGetNode(ctx, args);\n case 'get_neighbors':\n return handleGetNeighbors(ctx, args);\n case 'find_path':\n return handleFindPath(ctx, args);\n case 'get_hubs':\n return handleGetHubs(ctx, args);\n case 'search_by_tags':\n return handleSearchByTags(ctx, args);\n case 'random_node':\n return handleRandomNode(ctx, args);\n case 'create_node':\n return handleCreateNode(ctx, args);\n case 'update_node':\n return handleUpdateNode(ctx, args);\n case 'delete_node':\n return handleDeleteNode(ctx, args);\n case 'list_nodes':\n return handleListNodes(ctx, args);\n case 'resolve_nodes':\n return handleResolveNodes(ctx, args);\n case 'nodes_exist':\n return handleNodesExist(ctx, args);\n default:\n throw new McpError('INVALID_PARAMS', `Unknown tool: ${name}`);\n }\n}\n","import { access, writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\n\nexport interface VizOptions {\n output?: string | undefined;\n open?: boolean | undefined;\n}\n\nexport interface VizResult {\n outputPath: string;\n nodeCount: number;\n edgeCount: number;\n shouldOpen: boolean;\n}\n\ninterface GraphNode {\n id: string;\n title: string;\n inDegree: number;\n}\n\ninterface GraphEdge {\n source: string;\n target: string;\n}\n\nexport async function vizCommand(\n directory: string,\n options: VizOptions = {}\n): Promise<VizResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const outputPath = options.output ?? join(cacheDir, 'graph.html');\n\n const cache = new Cache(cacheDir);\n\n try {\n const nodes = cache.getAllNodes();\n const graphNodes: GraphNode[] = [];\n const graphEdges: GraphEdge[] = [];\n\n // Build set of existing node IDs for edge filtering\n const existingNodeIds = new Set(nodes.map((n) => n.id));\n\n // Build node list with degree info\n for (const node of nodes) {\n const centrality = cache.getCentrality(node.id);\n graphNodes.push({\n id: node.id,\n title: node.title,\n inDegree: centrality?.inDegree ?? 0,\n });\n\n // Build edges from outgoing links, filtering to existing targets only\n for (const target of node.outgoingLinks) {\n if (existingNodeIds.has(target)) {\n graphEdges.push({\n source: node.id,\n target,\n });\n }\n }\n }\n\n // Generate HTML\n const html = generateHtml(graphNodes, graphEdges);\n\n // Ensure output directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, html, 'utf-8');\n\n return {\n outputPath,\n nodeCount: graphNodes.length,\n edgeCount: graphEdges.length,\n shouldOpen: options.open ?? false,\n };\n } finally {\n cache.close();\n }\n}\n\nfunction generateHtml(nodes: GraphNode[], edges: GraphEdge[]): string {\n const nodesJson = JSON.stringify(nodes);\n const edgesJson = JSON.stringify(edges);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Roux Graph Visualization</title>\n <script src=\"https://d3js.org/d3.v7.min.js\"></script>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; background: #1a1a2e; overflow: hidden; }\n svg { display: block; width: 100vw; height: 100vh; }\n .node circle { cursor: pointer; }\n .node text { fill: #e0e0e0; font-size: 10px; pointer-events: none; }\n .link { stroke: #4a4a6a; stroke-opacity: 0.6; fill: none; }\n .tooltip {\n position: absolute;\n background: #16213e;\n color: #e0e0e0;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n border: 1px solid #4a4a6a;\n }\n </style>\n</head>\n<body>\n <div class=\"tooltip\" id=\"tooltip\"></div>\n <svg></svg>\n <script>\n const nodes = ${nodesJson};\n const links = ${edgesJson};\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n const svg = d3.select(\"svg\")\n .attr(\"viewBox\", [0, 0, width, height]);\n\n const g = svg.append(\"g\");\n\n // Zoom behavior\n svg.call(d3.zoom()\n .scaleExtent([0.1, 4])\n .on(\"zoom\", (event) => g.attr(\"transform\", event.transform)));\n\n // Node size based on in-degree\n const maxDegree = Math.max(1, ...nodes.map(n => n.inDegree));\n const nodeRadius = d => 5 + (d.inDegree / maxDegree) * 15;\n\n // Force simulation\n const simulation = d3.forceSimulation(nodes)\n .force(\"link\", d3.forceLink(links).id(d => d.id).distance(100))\n .force(\"charge\", d3.forceManyBody().strength(-200))\n .force(\"center\", d3.forceCenter(width / 2, height / 2))\n .force(\"collision\", d3.forceCollide().radius(d => nodeRadius(d) + 5));\n\n // Draw links with arrows\n svg.append(\"defs\").append(\"marker\")\n .attr(\"id\", \"arrow\")\n .attr(\"viewBox\", \"0 -5 10 10\")\n .attr(\"refX\", 20)\n .attr(\"refY\", 0)\n .attr(\"markerWidth\", 6)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\")\n .append(\"path\")\n .attr(\"fill\", \"#4a4a6a\")\n .attr(\"d\", \"M0,-5L10,0L0,5\");\n\n const link = g.append(\"g\")\n .selectAll(\"line\")\n .data(links)\n .join(\"line\")\n .attr(\"class\", \"link\")\n .attr(\"marker-end\", \"url(#arrow)\");\n\n // Draw nodes\n const node = g.append(\"g\")\n .selectAll(\".node\")\n .data(nodes)\n .join(\"g\")\n .attr(\"class\", \"node\")\n .call(d3.drag()\n .on(\"start\", dragstarted)\n .on(\"drag\", dragged)\n .on(\"end\", dragended));\n\n node.append(\"circle\")\n .attr(\"r\", nodeRadius)\n .attr(\"fill\", \"#0f4c75\")\n .attr(\"stroke\", \"#3282b8\")\n .attr(\"stroke-width\", 2);\n\n node.append(\"text\")\n .attr(\"dx\", d => nodeRadius(d) + 5)\n .attr(\"dy\", 4)\n .text(d => d.title.length > 20 ? d.title.slice(0, 17) + \"...\" : d.title);\n\n // Tooltip\n const tooltip = d3.select(\"#tooltip\");\n node\n .on(\"mouseover\", (event, d) => {\n tooltip\n .style(\"opacity\", 1)\n .html(\\`<strong>\\${d.title}</strong><br>ID: \\${d.id}<br>Incoming links: \\${d.inDegree}\\`);\n })\n .on(\"mousemove\", (event) => {\n tooltip\n .style(\"left\", (event.pageX + 10) + \"px\")\n .style(\"top\", (event.pageY - 10) + \"px\");\n })\n .on(\"mouseout\", () => tooltip.style(\"opacity\", 0));\n\n // Simulation tick\n simulation.on(\"tick\", () => {\n link\n .attr(\"x1\", d => d.source.x)\n .attr(\"y1\", d => d.source.y)\n .attr(\"x2\", d => d.target.x)\n .attr(\"y2\", d => d.target.y);\n\n node.attr(\"transform\", d => \\`translate(\\${d.x},\\${d.y})\\`);\n });\n\n function dragstarted(event, d) {\n if (!event.active) simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n }\n\n function dragged(event, d) {\n d.fx = event.x;\n d.fy = event.y;\n }\n\n function dragended(event, d) {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n }\n </script>\n</body>\n</html>`;\n}\n"],"mappings":"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","names":["resolve","access","join","join","stringSimilarity","Database","join","join","access","access","readFile","join","readFile","writeFile","mkdir","join","join","mkdir","writeFile","resolve","readFile","watch","join","access","readFile","access","writeFile","mkdir","join","dirname","join","access","mkdir","dirname","writeFile","resolve"]}