@ngommans/codefocus 0.1.0 → 0.1.3

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.

Potentially problematic release.


This version of @ngommans/codefocus might be problematic. Click here for more details.

package/README.md CHANGED
@@ -54,7 +54,7 @@ Add a `.mcp.json` file to your project root:
54
54
  "mcpServers": {
55
55
  "codefocus": {
56
56
  "command": "npx",
57
- "args": ["codefocus", "serve", "--root", "."]
57
+ "args": ["--yes", "@ngommans/codefocus", "serve", "--root", "."]
58
58
  }
59
59
  }
60
60
  }
@@ -72,7 +72,7 @@ Add `.claude/hooks.json` to keep the index fresh on every session:
72
72
  {
73
73
  "hooks": {
74
74
  "SessionStart": [{
75
- "command": "npx codefocus index --root .",
75
+ "command": "npx --yes @ngommans/codefocus index --root .",
76
76
  "timeout": 30000
77
77
  }]
78
78
  }
package/dist/cli.js CHANGED
@@ -91,7 +91,7 @@ async function main() {
91
91
  await runMap(positional, flags);
92
92
  },
93
93
  serve: async () => {
94
- const { runServe } = await import("./mcp-7WYTXIQS.js");
94
+ const { runServe } = await import("./mcp-7EPRSDMT.js");
95
95
  await runServe(positional, flags);
96
96
  },
97
97
  benchmark: async () => {
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ indexProject
4
+ } from "./chunk-X7DRJUEX.js";
2
5
  import {
3
6
  resolveRoot
4
7
  } from "./chunk-7RYHZOYF.js";
@@ -332,12 +335,18 @@ Usage with Claude Code:
332
335
  const root = resolveRoot(flags.root);
333
336
  const dbPath = resolve(root, ".codefocus", "index.db");
334
337
  if (!existsSync(dbPath)) {
338
+ if (!existsSync(root)) {
339
+ console.error(
340
+ `Error: root directory does not exist: ${root}`
341
+ );
342
+ process.exitCode = 1;
343
+ return;
344
+ }
345
+ console.error(`[codefocus] No index found \u2014 auto-indexing ${root} ...`);
346
+ const stats = await indexProject(root, dbPath);
335
347
  console.error(
336
- `Error: no index found at ${dbPath}
337
- Run 'codefocus index --root ${root}' first.`
348
+ `[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`
338
349
  );
339
- process.exitCode = 1;
340
- return;
341
350
  }
342
351
  getDb(root);
343
352
  console.error(`[codefocus] MCP server starting (root: ${root})`);
@@ -351,4 +360,4 @@ export {
351
360
  createMcpServer,
352
361
  runServe
353
362
  };
354
- //# sourceMappingURL=mcp-7WYTXIQS.js.map
363
+ //# sourceMappingURL=mcp-7EPRSDMT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { resolve } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { IndexDatabase } from \"./db.js\";\nimport { indexProject } from \"./indexer.js\";\nimport { resolveRoot } from \"./root.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\");\n\n// ── shared state ────────────────────────────────────────────────────────\n// The MCP server keeps the database connection and tiktoken encoder warm\n// so that subsequent tool calls pay ~10ms instead of ~400ms cold start.\n\nlet db: IndexDatabase | null = null;\nlet rootDir: string = \"\";\n\nfunction getDb(root: string): IndexDatabase {\n if (db && rootDir === root) return db;\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n if (!existsSync(dbPath)) {\n throw new Error(\n `no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n }\n if (db) db.close();\n db = new IndexDatabase(dbPath);\n rootDir = root;\n return db;\n}\n\n// ── MCP server setup ────────────────────────────────────────────────────\n\nexport function createMcpServer(defaultRoot?: string): McpServer {\n const server = new McpServer({\n name: \"codefocus\",\n version: pkg.version,\n });\n\n const effectiveRoot = defaultRoot ?? resolveRoot(undefined);\n\n // ── tool: query ─────────────────────────────────────────────────────\n\n server.tool(\n \"query\",\n \"Search the codebase and return ranked, budget-constrained code context. \" +\n \"Returns YAML front matter with confidence metadata followed by relevant source code sections.\",\n {\n term: z.string().describe(\"Search term (symbol name or keyword)\"),\n budget: z\n .number()\n .int()\n .positive()\n .default(8000)\n .describe(\"Token budget for output (default: 8000)\"),\n depth: z\n .number()\n .int()\n .min(0)\n .default(2)\n .describe(\"Max graph traversal depth (default: 2)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ term, budget, depth, root }) => {\n const r = root ?? effectiveRoot;\n // Import runQuery's internal logic so we can capture output\n const output = await captureQueryOutput(r, term, budget, depth);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: find ──────────────────────────────────────────────────────\n\n server.tool(\n \"find\",\n \"Quick symbol lookup by name with optional kind filter. \" +\n \"Returns symbol names, kinds, file paths, and line numbers.\",\n {\n symbol: z.string().describe(\"Symbol name to search for\"),\n kind: z\n .enum([\n \"function\",\n \"class\",\n \"interface\",\n \"type\",\n \"enum\",\n \"variable\",\n \"method\",\n \"all\",\n ])\n .default(\"all\")\n .describe(\"Filter by symbol kind (default: all)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ symbol, kind, root }) => {\n const r = root ?? effectiveRoot;\n const database = getDb(r);\n const results = database.findSymbols(symbol, kind);\n\n if (results.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `No symbols found matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}`,\n },\n ],\n };\n }\n\n const lines: string[] = [\n `Found ${results.length} symbol${results.length !== 1 ? \"s\" : \"\"} matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}:`,\n \"\",\n ];\n\n for (const sym of results) {\n const sig = sym.signature ? ` ${sym.signature}` : \"\";\n lines.push(\n ` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`,\n );\n }\n\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n },\n );\n\n // ── tool: graph ─────────────────────────────────────────────────────\n\n server.tool(\n \"graph\",\n \"Show the dependency graph for a file or symbol. \" +\n \"Displays incoming and/or outgoing edges as an indented tree.\",\n {\n target: z\n .string()\n .describe(\"File path or symbol name to show graph for\"),\n direction: z\n .enum([\"both\", \"incoming\", \"outgoing\"])\n .default(\"both\")\n .describe(\"Graph direction (default: both)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ target, direction, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureGraphOutput(r, target, direction);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: map ───────────────────────────────────────────────────────\n\n server.tool(\n \"map\",\n \"High-level codebase overview ranked by PageRank connectivity. \" +\n \"Returns file paths with their top symbols and signatures.\",\n {\n budget: z\n .number()\n .int()\n .positive()\n .default(2000)\n .describe(\"Token budget for output (default: 2000)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ budget, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureMapOutput(r, budget);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n return server;\n}\n\n// ── output capture helpers ──────────────────────────────────────────────\n// These re-implement the command logic to return strings instead of\n// writing to stdout, keeping the database connection warm.\n\nasync function captureQueryOutput(\n root: string,\n term: string,\n budget: number,\n depth: number,\n): Promise<string> {\n const { runQueryCore } = await import(\"./commands/query.js\");\n return runQueryCore(root, term, budget, depth);\n}\n\nfunction captureGraphOutput(\n root: string,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const database = getDb(root);\n\n const isFile =\n target.includes(\"/\") || target.includes(\"\\\\\") || /\\.\\w+$/.test(target);\n\n if (isFile) {\n return renderFileGraphToString(database, target, direction);\n } else {\n return renderSymbolGraphToString(database, target, direction);\n }\n}\n\nfunction renderFileGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n\n const graph = new DirectedGraph();\n for (const file of database.getAllFiles()) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (graph.hasEdge(key)) {\n const existing = graph.getEdgeAttributes(key);\n graph.setEdgeAttribute(\n key,\n \"specifiers\",\n `${existing.specifiers}, ${edge.specifiers}`,\n );\n } else {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file, {\n specifiers: edge.specifiers,\n });\n }\n }\n\n if (!graph.hasNode(target)) {\n return `Error: file \"${target}\" not found in the index`;\n }\n\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const outEdges: string[] = [];\n graph.forEachOutEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n _src: string,\n tgt: string,\n ) => {\n outEdges.push(` ${tgt} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n outEdges.length > 0\n ? `Dependencies (outgoing):\\n${outEdges.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const inEdges: string[] = [];\n graph.forEachInEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n src: string,\n ) => {\n inEdges.push(` ${src} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n inEdges.length > 0\n ? `Dependents (incoming):\\n${inEdges.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${target}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction renderSymbolGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const symbols = database.findSymbolsByName(target);\n const exactMatch = symbols.find((s) => s.name === target);\n const sym = exactMatch ?? symbols[0];\n\n if (!sym || !sym.id) {\n return `Error: symbol \"${target}\" not found in the index`;\n }\n\n const heading = `${sym.name} (${sym.kind}) — ${sym.file_path}:${sym.start_line}`;\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const refs = database.getOutgoingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.target_name} (${r.target_kind}) — ${r.target_file}:${r.target_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependencies (outgoing):\\n${lines.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const refs = database.getIncomingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.source_name} (${r.source_kind}) — ${r.source_file}:${r.source_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependents (incoming):\\n${lines.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${heading}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction captureMapOutput(root: string, budget: number): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n const pagerank = require(\"graphology-metrics/centrality/pagerank\");\n const { getEncoding } = require(\"js-tiktoken\");\n\n const database = getDb(root);\n const files = database.getAllFiles();\n\n if (files.length === 0) {\n return \"[map] Index is empty — no files to map.\";\n }\n\n // Compute PageRank\n const graph = new DirectedGraph();\n for (const file of files) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (!graph.hasEdge(key)) {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file);\n }\n }\n const ranks: Record<string, number> =\n graph.order > 0 ? pagerank(graph, { getEdgeWeight: null }) : {};\n\n const rankedFiles = files\n .map((f) => ({ path: f.path, rank: ranks[f.path] ?? 0 }))\n .sort((a, b) => b.rank - a.rank || a.path.localeCompare(b.path));\n\n const enc = getEncoding(\"cl100k_base\");\n const blocks: string[] = [];\n let tokenCount = 0;\n let truncated = false;\n\n for (const file of rankedFiles) {\n const symbols = database.getSymbolsByFile(file.path);\n symbols.sort((a, b) => a.start_line - b.start_line);\n const lines: string[] = [file.path];\n for (const sym of symbols) {\n if (sym.kind === \"variable\" && !sym.signature) continue;\n const label = sym.signature ?? `${sym.kind} ${sym.name}`;\n lines.push(` ${label}`);\n }\n const block = lines.join(\"\\n\");\n const blockTokens = enc.encode(block).length;\n\n if (tokenCount + blockTokens <= budget) {\n blocks.push(block);\n tokenCount += blockTokens;\n continue;\n }\n\n const blockLines = block.split(\"\\n\");\n let partial = blockLines[0];\n let partialTokens = enc.encode(partial).length;\n\n if (tokenCount + partialTokens > budget) {\n truncated = true;\n break;\n }\n\n for (let i = 1; i < blockLines.length; i++) {\n const candidate = partial + \"\\n\" + blockLines[i];\n const candidateTokens = enc.encode(candidate).length;\n if (tokenCount + candidateTokens > budget) break;\n partial = candidate;\n partialTokens = candidateTokens;\n }\n\n blocks.push(partial);\n tokenCount += partialTokens;\n truncated = true;\n break;\n }\n\n const shown = blocks.length;\n const total = rankedFiles.length;\n const parts = [`[map] ${shown}/${total} files, ~${tokenCount} tokens`];\n if (truncated) {\n parts.push(`(budget: ${budget}, truncated)`);\n }\n\n return blocks.join(\"\\n\\n\") + `\\n\\n${parts.join(\" \")}`;\n}\n\n// ── serve command entry point ───────────────────────────────────────────\n\nexport async function runServe(\n _positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.error(`codefocus serve — Start MCP server (stdio transport)\n\nUsage: codefocus serve [options]\n\nOptions:\n --root <path> Root directory of indexed project (default: auto-detect)\n --help Show this help message\n\nThe MCP server exposes four tools:\n query Search and return ranked code context\n find Quick symbol lookup\n graph Show dependency graph for a file or symbol\n map High-level codebase overview\n\nThe server keeps the database connection and tiktoken encoder warm,\nso subsequent tool calls complete in ~10ms instead of ~400ms.\n\nUsage with Claude Code:\n Add to .mcp.json:\n {\n \"mcpServers\": {\n \"codefocus\": {\n \"command\": \"npx\",\n \"args\": [\"codefocus\", \"serve\", \"--root\", \"/path/to/project\"]\n }\n }\n }`);\n return;\n }\n\n const root = resolveRoot(flags.root);\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n\n if (!existsSync(dbPath)) {\n if (!existsSync(root)) {\n console.error(\n `Error: root directory does not exist: ${root}`,\n );\n process.exitCode = 1;\n return;\n }\n console.error(`[codefocus] No index found — auto-indexing ${root} ...`);\n const stats = await indexProject(root, dbPath);\n console.error(\n `[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`,\n );\n }\n\n // Pre-warm the database connection\n getDb(root);\n console.error(`[codefocus] MCP server starting (root: ${root})`);\n console.error(`[codefocus] Database: ${dbPath}`);\n\n const server = createMcpServer(root);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(`[codefocus] MCP server ready (4 tools: query, find, graph, map)`);\n}\n"],"mappings":"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","names":["require"]}
@@ -5,19 +5,555 @@ import {
5
5
  } from "./chunk-ITVAEU6K.js";
6
6
 
7
7
  // src/mcp.ts
8
- import { createRequire } from "module";
9
- import { resolve } from "path";
8
+ import { createRequire as createRequire3 } from "module";
9
+ import { resolve as resolve2 } from "path";
10
10
  import { existsSync } from "fs";
11
11
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
13
  import { z } from "zod";
14
+
15
+ // src/indexer.ts
16
+ import { createRequire as createRequire2 } from "module";
17
+ import { readFileSync } from "fs";
18
+ import { resolve, relative, dirname, extname } from "path";
19
+ import { createHash } from "crypto";
20
+
21
+ // src/parser.ts
22
+ import { createRequire } from "module";
14
23
  var require2 = createRequire(import.meta.url);
15
- var pkg = require2("../package.json");
24
+ var Parser = require2("tree-sitter");
25
+ var TypeScriptLang = require2("tree-sitter-typescript").typescript;
26
+ var parser = null;
27
+ function getParser() {
28
+ if (!parser) {
29
+ parser = new Parser();
30
+ parser.setLanguage(TypeScriptLang);
31
+ }
32
+ return parser;
33
+ }
34
+ function extractSignature(node, source) {
35
+ const kind = node.type;
36
+ if (kind === "function_declaration" || kind === "arrow_function") {
37
+ const nameNode = node.childForFieldName("name");
38
+ const paramsNode = node.childForFieldName("parameters");
39
+ const returnType = node.childForFieldName("return_type");
40
+ if (nameNode && paramsNode) {
41
+ let sig = `function ${nameNode.text}${paramsNode.text}`;
42
+ if (returnType) sig += returnType.text;
43
+ return sig;
44
+ }
45
+ }
46
+ if (kind === "class_declaration") {
47
+ const nameNode = node.childForFieldName("name");
48
+ if (nameNode) return `class ${nameNode.text}`;
49
+ }
50
+ if (kind === "interface_declaration") {
51
+ const nameNode = node.childForFieldName("name");
52
+ if (nameNode) return `interface ${nameNode.text}`;
53
+ }
54
+ if (kind === "type_alias_declaration") {
55
+ const text = node.text;
56
+ const semiIdx = text.indexOf(";");
57
+ return semiIdx > 0 ? text.slice(0, semiIdx) : text;
58
+ }
59
+ if (kind === "enum_declaration") {
60
+ const nameNode = node.childForFieldName("name");
61
+ if (nameNode) return `enum ${nameNode.text}`;
62
+ }
63
+ if (kind === "method_definition") {
64
+ const nameNode = node.childForFieldName("name");
65
+ const paramsNode = node.childForFieldName("parameters");
66
+ const returnType = node.childForFieldName("return_type");
67
+ if (nameNode && paramsNode) {
68
+ let sig = `${nameNode.text}${paramsNode.text}`;
69
+ if (returnType) sig += returnType.text;
70
+ return sig;
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ function getSymbolName(node) {
76
+ const nameNode = node.childForFieldName("name") ?? node.childForFieldName("declarator");
77
+ return nameNode?.text ?? null;
78
+ }
79
+ function extractDeclaration(node, source, exported) {
80
+ const kindMap = {
81
+ function_declaration: "function",
82
+ class_declaration: "class",
83
+ interface_declaration: "interface",
84
+ type_alias_declaration: "type",
85
+ enum_declaration: "enum"
86
+ };
87
+ const kind = kindMap[node.type];
88
+ if (!kind) return null;
89
+ const name = getSymbolName(node);
90
+ if (!name) return null;
91
+ return {
92
+ name,
93
+ kind,
94
+ startByte: node.startIndex,
95
+ endByte: node.endIndex,
96
+ startLine: node.startPosition.row + 1,
97
+ endLine: node.endPosition.row + 1,
98
+ startColumn: node.startPosition.column,
99
+ endColumn: node.endPosition.column,
100
+ signature: extractSignature(node, source),
101
+ exported
102
+ };
103
+ }
104
+ function extractMethods(classNode, source, exported) {
105
+ const methods = [];
106
+ const body = classNode.childForFieldName("body");
107
+ if (!body) return methods;
108
+ for (const child of body.namedChildren) {
109
+ if (child.type === "method_definition") {
110
+ const nameNode = child.childForFieldName("name");
111
+ if (!nameNode) continue;
112
+ methods.push({
113
+ name: nameNode.text,
114
+ kind: "method",
115
+ startByte: child.startIndex,
116
+ endByte: child.endIndex,
117
+ startLine: child.startPosition.row + 1,
118
+ endLine: child.endPosition.row + 1,
119
+ startColumn: child.startPosition.column,
120
+ endColumn: child.endPosition.column,
121
+ signature: extractSignature(child, source),
122
+ exported
123
+ });
124
+ }
125
+ }
126
+ return methods;
127
+ }
128
+ function extractVariableDeclarations(node, exported) {
129
+ const symbols = [];
130
+ for (const child of node.namedChildren) {
131
+ if (child.type === "variable_declarator") {
132
+ const nameNode = child.childForFieldName("name");
133
+ if (nameNode) {
134
+ symbols.push({
135
+ name: nameNode.text,
136
+ kind: "variable",
137
+ startByte: node.startIndex,
138
+ endByte: node.endIndex,
139
+ startLine: node.startPosition.row + 1,
140
+ endLine: node.endPosition.row + 1,
141
+ startColumn: node.startPosition.column,
142
+ endColumn: node.endPosition.column,
143
+ signature: null,
144
+ exported
145
+ });
146
+ }
147
+ }
148
+ }
149
+ return symbols;
150
+ }
151
+ function extractImport(node) {
152
+ let sourceNode = null;
153
+ for (const child of node.children) {
154
+ if (child.type === "string") {
155
+ sourceNode = child;
156
+ break;
157
+ }
158
+ }
159
+ if (!sourceNode) return null;
160
+ const rawSource = sourceNode.text;
161
+ const source = rawSource.replace(/^['"]|['"]$/g, "");
162
+ const specifiers = [];
163
+ const isTypeOnly = node.children.some(
164
+ (c) => c.type === "type" && c.text === "type"
165
+ );
166
+ for (const child of node.children) {
167
+ if (child.type === "import_clause") {
168
+ for (const clause of child.namedChildren) {
169
+ if (clause.type === "named_imports") {
170
+ for (const spec of clause.namedChildren) {
171
+ if (spec.type === "import_specifier") {
172
+ const nameNode = spec.childForFieldName("name");
173
+ const aliasNode = spec.childForFieldName("alias");
174
+ specifiers.push(aliasNode?.text ?? nameNode?.text ?? spec.text);
175
+ }
176
+ }
177
+ } else if (clause.type === "identifier") {
178
+ specifiers.push(clause.text);
179
+ } else if (clause.type === "namespace_import") {
180
+ const nameNode = clause.namedChildren.find(
181
+ (c) => c.type === "identifier"
182
+ );
183
+ if (nameNode) specifiers.push(`* as ${nameNode.text}`);
184
+ }
185
+ }
186
+ }
187
+ }
188
+ return { specifiers, source, isTypeOnly };
189
+ }
190
+ function extractReExportSpecifiers(node) {
191
+ const specifiers = [];
192
+ for (const child of node.children) {
193
+ if (child.type === "export_clause") {
194
+ for (const spec of child.namedChildren) {
195
+ if (spec.type === "export_specifier") {
196
+ const nameNode = spec.childForFieldName("name");
197
+ if (nameNode) specifiers.push(nameNode.text);
198
+ }
199
+ }
200
+ }
201
+ }
202
+ return specifiers;
203
+ }
204
+ function findIdentifierUsages(source, names) {
205
+ if (names.size === 0) return [];
206
+ const p = getParser();
207
+ const tree = p.parse(source);
208
+ const root = tree.rootNode;
209
+ const usages = [];
210
+ function walk(node) {
211
+ if ((node.type === "identifier" || node.type === "type_identifier") && names.has(node.text)) {
212
+ let parent = node.parent;
213
+ let isImportExport = false;
214
+ while (parent) {
215
+ if (parent.type === "import_statement" || parent.type === "import_clause" || parent.type === "import_specifier" || parent.type === "export_statement" && parent.children.some((c) => c.type === "from")) {
216
+ isImportExport = true;
217
+ break;
218
+ }
219
+ parent = parent.parent;
220
+ }
221
+ if (!isImportExport) {
222
+ usages.push({
223
+ name: node.text,
224
+ line: node.startPosition.row + 1,
225
+ column: node.startPosition.column
226
+ });
227
+ }
228
+ }
229
+ for (const child of node.children) {
230
+ walk(child);
231
+ }
232
+ }
233
+ walk(root);
234
+ return usages;
235
+ }
236
+ function extractDynamicImports(root) {
237
+ const imports = [];
238
+ function walk(node) {
239
+ if (node.type === "call_expression") {
240
+ const fn = node.child(0);
241
+ if (fn && fn.type === "import") {
242
+ const argsNode = node.childForFieldName("arguments");
243
+ if (argsNode) {
244
+ const strArg = argsNode.namedChildren.find(
245
+ (c) => c.type === "string"
246
+ );
247
+ if (strArg) {
248
+ const modulePath = strArg.text.replace(/^['"]|['"]$/g, "");
249
+ const specifiers = extractDynamicImportSpecifiers(node);
250
+ imports.push({ specifiers, source: modulePath, isTypeOnly: false });
251
+ }
252
+ }
253
+ return;
254
+ }
255
+ }
256
+ for (const child of node.children) {
257
+ walk(child);
258
+ }
259
+ }
260
+ walk(root);
261
+ return imports;
262
+ }
263
+ function extractDynamicImportSpecifiers(importCall) {
264
+ let current = importCall.parent;
265
+ if (current && current.type === "await_expression") {
266
+ current = current.parent;
267
+ }
268
+ if (current && current.type === "variable_declarator") {
269
+ const pattern = current.childForFieldName("name");
270
+ if (pattern && pattern.type === "object_pattern") {
271
+ const specs = [];
272
+ for (const child of pattern.namedChildren) {
273
+ if (child.type === "shorthand_property_identifier_pattern") {
274
+ specs.push(child.text);
275
+ } else if (child.type === "pair_pattern") {
276
+ const value = child.childForFieldName("value");
277
+ if (value) specs.push(value.text);
278
+ }
279
+ }
280
+ return specs;
281
+ }
282
+ }
283
+ return [];
284
+ }
285
+ function parseSource(source) {
286
+ const p = getParser();
287
+ const tree = p.parse(source);
288
+ const root = tree.rootNode;
289
+ const symbols = [];
290
+ const imports = [];
291
+ for (const node of root.namedChildren) {
292
+ if (node.type === "import_statement") {
293
+ const imp = extractImport(node);
294
+ if (imp) imports.push(imp);
295
+ continue;
296
+ }
297
+ if (node.type === "export_statement") {
298
+ const hasFrom = node.children.some(
299
+ (c) => c.type === "from"
300
+ );
301
+ if (hasFrom) {
302
+ let sourceNode = null;
303
+ for (const child of node.children) {
304
+ if (child.type === "string") {
305
+ sourceNode = child;
306
+ break;
307
+ }
308
+ }
309
+ if (sourceNode) {
310
+ const rawSource = sourceNode.text;
311
+ const source2 = rawSource.replace(/^['"]|['"]$/g, "");
312
+ const specifiers = extractReExportSpecifiers(node);
313
+ const isTypeOnly = node.children.some(
314
+ (c) => c.type === "type" && c.text === "type"
315
+ );
316
+ imports.push({ specifiers, source: source2, isTypeOnly });
317
+ }
318
+ continue;
319
+ }
320
+ for (const child of node.namedChildren) {
321
+ const decl2 = extractDeclaration(child, source, true);
322
+ if (decl2) {
323
+ symbols.push(decl2);
324
+ if (child.type === "class_declaration") {
325
+ symbols.push(...extractMethods(child, source, true));
326
+ }
327
+ }
328
+ if (child.type === "lexical_declaration" || child.type === "variable_declaration") {
329
+ symbols.push(...extractVariableDeclarations(child, true));
330
+ }
331
+ }
332
+ continue;
333
+ }
334
+ const decl = extractDeclaration(node, source, false);
335
+ if (decl) {
336
+ symbols.push(decl);
337
+ if (node.type === "class_declaration") {
338
+ symbols.push(...extractMethods(node, source, false));
339
+ }
340
+ }
341
+ if (node.type === "lexical_declaration" || node.type === "variable_declaration") {
342
+ symbols.push(...extractVariableDeclarations(node, false));
343
+ }
344
+ }
345
+ imports.push(...extractDynamicImports(root));
346
+ return { symbols, imports };
347
+ }
348
+
349
+ // src/indexer.ts
350
+ var require3 = createRequire2(import.meta.url);
351
+ var fg = require3("fast-glob");
352
+ var TS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
353
+ var IGNORE_PATTERNS = [
354
+ "**/node_modules/**",
355
+ "**/dist/**",
356
+ "**/.codefocus/**",
357
+ "**/__tests__/**",
358
+ "**/*.test.*",
359
+ "**/*.spec.*",
360
+ "**/*.d.ts"
361
+ ];
362
+ function hashContent(content) {
363
+ return createHash("sha256").update(content).digest("hex").slice(0, 16);
364
+ }
365
+ function resolveImportPath(importSource, fromFile, rootDir2, allFiles) {
366
+ if (!importSource.startsWith(".")) return null;
367
+ const fromDir = dirname(fromFile);
368
+ const basePath = resolve(rootDir2, fromDir, importSource);
369
+ const ext = extname(basePath);
370
+ const strippedBase = ext === ".js" || ext === ".jsx" ? basePath.slice(0, -ext.length) : null;
371
+ const candidates = [
372
+ basePath,
373
+ ...Array.from(TS_EXTENSIONS).map((e) => basePath + e),
374
+ ...Array.from(TS_EXTENSIONS).map((e) => resolve(basePath, "index" + e))
375
+ ];
376
+ if (strippedBase) {
377
+ candidates.push(
378
+ ...Array.from(TS_EXTENSIONS).map((e) => strippedBase + e),
379
+ ...Array.from(TS_EXTENSIONS).map(
380
+ (e) => resolve(strippedBase, "index" + e)
381
+ )
382
+ );
383
+ }
384
+ for (const candidate of candidates) {
385
+ const rel = relative(rootDir2, candidate);
386
+ if (allFiles.has(rel)) return rel;
387
+ }
388
+ return null;
389
+ }
390
+ async function indexProject(rootDir2, dbPath) {
391
+ const startTime = Date.now();
392
+ const absRoot = resolve(rootDir2);
393
+ const files = await fg("**/*.{ts,tsx,js,jsx}", {
394
+ cwd: absRoot,
395
+ ignore: IGNORE_PATTERNS,
396
+ absolute: false
397
+ });
398
+ const allFiles = new Set(files);
399
+ const db2 = new IndexDatabase(dbPath);
400
+ let filesIndexed = 0;
401
+ let filesSkipped = 0;
402
+ let symbolsExtracted = 0;
403
+ let importsFound = 0;
404
+ let referencesCreated = 0;
405
+ const symbolIdMap = /* @__PURE__ */ new Map();
406
+ const fileImports = /* @__PURE__ */ new Map();
407
+ const fileContents = /* @__PURE__ */ new Map();
408
+ db2.transaction(() => {
409
+ for (const filePath of files) {
410
+ const absPath = resolve(absRoot, filePath);
411
+ let content;
412
+ try {
413
+ content = readFileSync(absPath, "utf-8");
414
+ } catch {
415
+ filesSkipped++;
416
+ continue;
417
+ }
418
+ const hash = hashContent(content);
419
+ const existingHash = db2.getFileHash(filePath);
420
+ const result = parseSource(content);
421
+ if (result.imports.length > 0) {
422
+ fileImports.set(filePath, result.imports);
423
+ fileContents.set(filePath, content);
424
+ }
425
+ if (existingHash === hash) {
426
+ filesSkipped++;
427
+ for (const sym of db2.getSymbolsByFile(filePath)) {
428
+ symbolIdMap.set(`${filePath}:${sym.name}`, sym.id);
429
+ }
430
+ continue;
431
+ }
432
+ db2.clearFile(filePath);
433
+ const ext = extname(filePath);
434
+ const language = ext === ".js" || ext === ".jsx" ? "javascript" : "typescript";
435
+ db2.upsertFile({
436
+ path: filePath,
437
+ content_hash: hash,
438
+ language,
439
+ last_indexed: Date.now()
440
+ });
441
+ db2.upsertFileContent(filePath, content);
442
+ for (const sym of result.symbols) {
443
+ const id = db2.insertSymbol({
444
+ file_path: filePath,
445
+ name: sym.name,
446
+ kind: sym.kind,
447
+ start_byte: sym.startByte,
448
+ end_byte: sym.endByte,
449
+ start_line: sym.startLine,
450
+ end_line: sym.endLine,
451
+ start_column: sym.startColumn,
452
+ end_column: sym.endColumn,
453
+ signature: sym.signature
454
+ });
455
+ symbolIdMap.set(`${filePath}:${sym.name}`, id);
456
+ symbolsExtracted++;
457
+ }
458
+ filesIndexed++;
459
+ }
460
+ db2.clearAllImports();
461
+ db2.clearAllReferences();
462
+ for (const [filePath, imports] of fileImports) {
463
+ const fileSymbolRows = db2.getSymbolsByFile(filePath);
464
+ const sourceSymbols = fileSymbolRows.map((sym) => ({
465
+ name: sym.name,
466
+ id: sym.id,
467
+ startLine: sym.start_line,
468
+ endLine: sym.end_line
469
+ }));
470
+ const allImportedNames = /* @__PURE__ */ new Set();
471
+ for (const imp of imports) {
472
+ for (const specName of imp.specifiers) {
473
+ const cleanName = specName.startsWith("* as ") ? specName.slice(5) : specName;
474
+ allImportedNames.add(cleanName);
475
+ }
476
+ }
477
+ const content = fileContents.get(filePath);
478
+ const usages = content ? findIdentifierUsages(content, allImportedNames) : [];
479
+ for (const imp of imports) {
480
+ const targetFile = resolveImportPath(
481
+ imp.source,
482
+ filePath,
483
+ absRoot,
484
+ allFiles
485
+ );
486
+ for (const specName of imp.specifiers) {
487
+ const cleanName = specName.startsWith("* as ") ? specName.slice(5) : specName;
488
+ db2.insertImport({
489
+ file_path: filePath,
490
+ specifier: cleanName,
491
+ source_path: targetFile,
492
+ raw_module: imp.source,
493
+ is_type_only: imp.isTypeOnly ? 1 : 0
494
+ });
495
+ importsFound++;
496
+ }
497
+ if (!targetFile) continue;
498
+ const refType = imp.isTypeOnly ? "type_ref" : "import";
499
+ for (const specName of imp.specifiers) {
500
+ const cleanName = specName.startsWith("* as ") ? specName.slice(5) : specName;
501
+ const targetKey = `${targetFile}:${cleanName}`;
502
+ const targetId = symbolIdMap.get(targetKey);
503
+ if (!targetId) continue;
504
+ const nameUsages = usages.filter((u) => u.name === cleanName);
505
+ if (nameUsages.length > 0) {
506
+ const referencingSymbolIds = /* @__PURE__ */ new Set();
507
+ for (const usage of nameUsages) {
508
+ for (const srcSym of sourceSymbols) {
509
+ if (srcSym.id !== targetId && usage.line >= srcSym.startLine && usage.line <= srcSym.endLine) {
510
+ referencingSymbolIds.add(srcSym.id);
511
+ }
512
+ }
513
+ }
514
+ for (const sourceId of referencingSymbolIds) {
515
+ db2.insertReference({
516
+ source_symbol_id: sourceId,
517
+ target_symbol_id: targetId,
518
+ ref_type: refType
519
+ });
520
+ referencesCreated++;
521
+ }
522
+ } else {
523
+ for (const srcSym of sourceSymbols) {
524
+ if (srcSym.id !== targetId) {
525
+ db2.insertReference({
526
+ source_symbol_id: srcSym.id,
527
+ target_symbol_id: targetId,
528
+ ref_type: refType
529
+ });
530
+ referencesCreated++;
531
+ }
532
+ }
533
+ }
534
+ }
535
+ }
536
+ }
537
+ });
538
+ db2.close();
539
+ return {
540
+ filesIndexed,
541
+ filesSkipped,
542
+ symbolsExtracted,
543
+ importsFound,
544
+ referencesCreated,
545
+ timeMs: Date.now() - startTime
546
+ };
547
+ }
548
+
549
+ // src/mcp.ts
550
+ var require4 = createRequire3(import.meta.url);
551
+ var pkg = require4("../package.json");
16
552
  var db = null;
17
553
  var rootDir = "";
18
554
  function getDb(root) {
19
555
  if (db && rootDir === root) return db;
20
- const dbPath = resolve(root, ".codefocus", "index.db");
556
+ const dbPath = resolve2(root, ".codefocus", "index.db");
21
557
  if (!existsSync(dbPath)) {
22
558
  throw new Error(
23
559
  `no index found at ${dbPath}
@@ -137,8 +673,8 @@ function captureGraphOutput(root, target, direction) {
137
673
  }
138
674
  }
139
675
  function renderFileGraphToString(database, target, direction) {
140
- const require3 = createRequire(import.meta.url);
141
- const { DirectedGraph } = require3("graphology");
676
+ const require5 = createRequire3(import.meta.url);
677
+ const { DirectedGraph } = require5("graphology");
142
678
  const graph = new DirectedGraph();
143
679
  for (const file of database.getAllFiles()) {
144
680
  graph.addNode(file.path);
@@ -226,10 +762,10 @@ ${lines.join("\n")}` : "Dependents (incoming): (none)"
226
762
  ${sections.join("\n\n")}`;
227
763
  }
228
764
  function captureMapOutput(root, budget) {
229
- const require3 = createRequire(import.meta.url);
230
- const { DirectedGraph } = require3("graphology");
231
- const pagerank = require3("graphology-metrics/centrality/pagerank");
232
- const { getEncoding } = require3("js-tiktoken");
765
+ const require5 = createRequire3(import.meta.url);
766
+ const { DirectedGraph } = require5("graphology");
767
+ const pagerank = require5("graphology-metrics/centrality/pagerank");
768
+ const { getEncoding } = require5("js-tiktoken");
233
769
  const database = getDb(root);
234
770
  const files = database.getAllFiles();
235
771
  if (files.length === 0) {
@@ -328,14 +864,20 @@ Usage with Claude Code:
328
864
  return;
329
865
  }
330
866
  const root = resolveRoot(flags2.root);
331
- const dbPath = resolve(root, ".codefocus", "index.db");
867
+ const dbPath = resolve2(root, ".codefocus", "index.db");
332
868
  if (!existsSync(dbPath)) {
869
+ if (!existsSync(root)) {
870
+ console.error(
871
+ `Error: root directory does not exist: ${root}`
872
+ );
873
+ process.exitCode = 1;
874
+ return;
875
+ }
876
+ console.error(`[codefocus] No index found \u2014 auto-indexing ${root} ...`);
877
+ const stats = await indexProject(root, dbPath);
333
878
  console.error(
334
- `Error: no index found at ${dbPath}
335
- Run 'codefocus index --root ${root}' first.`
879
+ `[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`
336
880
  );
337
- process.exitCode = 1;
338
- return;
339
881
  }
340
882
  getDb(root);
341
883
  console.error(`[codefocus] MCP server starting (root: ${root})`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.ts","../src/mcp-entry.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { resolve } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { IndexDatabase } from \"./db.js\";\nimport { resolveRoot } from \"./root.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\");\n\n// ── shared state ────────────────────────────────────────────────────────\n// The MCP server keeps the database connection and tiktoken encoder warm\n// so that subsequent tool calls pay ~10ms instead of ~400ms cold start.\n\nlet db: IndexDatabase | null = null;\nlet rootDir: string = \"\";\n\nfunction getDb(root: string): IndexDatabase {\n if (db && rootDir === root) return db;\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n if (!existsSync(dbPath)) {\n throw new Error(\n `no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n }\n if (db) db.close();\n db = new IndexDatabase(dbPath);\n rootDir = root;\n return db;\n}\n\n// ── MCP server setup ────────────────────────────────────────────────────\n\nexport function createMcpServer(defaultRoot?: string): McpServer {\n const server = new McpServer({\n name: \"codefocus\",\n version: pkg.version,\n });\n\n const effectiveRoot = defaultRoot ?? resolveRoot(undefined);\n\n // ── tool: query ─────────────────────────────────────────────────────\n\n server.tool(\n \"query\",\n \"Search the codebase and return ranked, budget-constrained code context. \" +\n \"Returns YAML front matter with confidence metadata followed by relevant source code sections.\",\n {\n term: z.string().describe(\"Search term (symbol name or keyword)\"),\n budget: z\n .number()\n .int()\n .positive()\n .default(8000)\n .describe(\"Token budget for output (default: 8000)\"),\n depth: z\n .number()\n .int()\n .min(0)\n .default(2)\n .describe(\"Max graph traversal depth (default: 2)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ term, budget, depth, root }) => {\n const r = root ?? effectiveRoot;\n // Import runQuery's internal logic so we can capture output\n const output = await captureQueryOutput(r, term, budget, depth);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: find ──────────────────────────────────────────────────────\n\n server.tool(\n \"find\",\n \"Quick symbol lookup by name with optional kind filter. \" +\n \"Returns symbol names, kinds, file paths, and line numbers.\",\n {\n symbol: z.string().describe(\"Symbol name to search for\"),\n kind: z\n .enum([\n \"function\",\n \"class\",\n \"interface\",\n \"type\",\n \"enum\",\n \"variable\",\n \"method\",\n \"all\",\n ])\n .default(\"all\")\n .describe(\"Filter by symbol kind (default: all)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ symbol, kind, root }) => {\n const r = root ?? effectiveRoot;\n const database = getDb(r);\n const results = database.findSymbols(symbol, kind);\n\n if (results.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `No symbols found matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}`,\n },\n ],\n };\n }\n\n const lines: string[] = [\n `Found ${results.length} symbol${results.length !== 1 ? \"s\" : \"\"} matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}:`,\n \"\",\n ];\n\n for (const sym of results) {\n const sig = sym.signature ? ` ${sym.signature}` : \"\";\n lines.push(\n ` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`,\n );\n }\n\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n },\n );\n\n // ── tool: graph ─────────────────────────────────────────────────────\n\n server.tool(\n \"graph\",\n \"Show the dependency graph for a file or symbol. \" +\n \"Displays incoming and/or outgoing edges as an indented tree.\",\n {\n target: z\n .string()\n .describe(\"File path or symbol name to show graph for\"),\n direction: z\n .enum([\"both\", \"incoming\", \"outgoing\"])\n .default(\"both\")\n .describe(\"Graph direction (default: both)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ target, direction, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureGraphOutput(r, target, direction);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: map ───────────────────────────────────────────────────────\n\n server.tool(\n \"map\",\n \"High-level codebase overview ranked by PageRank connectivity. \" +\n \"Returns file paths with their top symbols and signatures.\",\n {\n budget: z\n .number()\n .int()\n .positive()\n .default(2000)\n .describe(\"Token budget for output (default: 2000)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ budget, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureMapOutput(r, budget);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n return server;\n}\n\n// ── output capture helpers ──────────────────────────────────────────────\n// These re-implement the command logic to return strings instead of\n// writing to stdout, keeping the database connection warm.\n\nasync function captureQueryOutput(\n root: string,\n term: string,\n budget: number,\n depth: number,\n): Promise<string> {\n const { runQueryCore } = await import(\"./commands/query.js\");\n return runQueryCore(root, term, budget, depth);\n}\n\nfunction captureGraphOutput(\n root: string,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const database = getDb(root);\n\n const isFile =\n target.includes(\"/\") || target.includes(\"\\\\\") || /\\.\\w+$/.test(target);\n\n if (isFile) {\n return renderFileGraphToString(database, target, direction);\n } else {\n return renderSymbolGraphToString(database, target, direction);\n }\n}\n\nfunction renderFileGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n\n const graph = new DirectedGraph();\n for (const file of database.getAllFiles()) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (graph.hasEdge(key)) {\n const existing = graph.getEdgeAttributes(key);\n graph.setEdgeAttribute(\n key,\n \"specifiers\",\n `${existing.specifiers}, ${edge.specifiers}`,\n );\n } else {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file, {\n specifiers: edge.specifiers,\n });\n }\n }\n\n if (!graph.hasNode(target)) {\n return `Error: file \"${target}\" not found in the index`;\n }\n\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const outEdges: string[] = [];\n graph.forEachOutEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n _src: string,\n tgt: string,\n ) => {\n outEdges.push(` ${tgt} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n outEdges.length > 0\n ? `Dependencies (outgoing):\\n${outEdges.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const inEdges: string[] = [];\n graph.forEachInEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n src: string,\n ) => {\n inEdges.push(` ${src} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n inEdges.length > 0\n ? `Dependents (incoming):\\n${inEdges.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${target}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction renderSymbolGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const symbols = database.findSymbolsByName(target);\n const exactMatch = symbols.find((s) => s.name === target);\n const sym = exactMatch ?? symbols[0];\n\n if (!sym || !sym.id) {\n return `Error: symbol \"${target}\" not found in the index`;\n }\n\n const heading = `${sym.name} (${sym.kind}) — ${sym.file_path}:${sym.start_line}`;\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const refs = database.getOutgoingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.target_name} (${r.target_kind}) — ${r.target_file}:${r.target_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependencies (outgoing):\\n${lines.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const refs = database.getIncomingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.source_name} (${r.source_kind}) — ${r.source_file}:${r.source_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependents (incoming):\\n${lines.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${heading}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction captureMapOutput(root: string, budget: number): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n const pagerank = require(\"graphology-metrics/centrality/pagerank\");\n const { getEncoding } = require(\"js-tiktoken\");\n\n const database = getDb(root);\n const files = database.getAllFiles();\n\n if (files.length === 0) {\n return \"[map] Index is empty — no files to map.\";\n }\n\n // Compute PageRank\n const graph = new DirectedGraph();\n for (const file of files) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (!graph.hasEdge(key)) {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file);\n }\n }\n const ranks: Record<string, number> =\n graph.order > 0 ? pagerank(graph, { getEdgeWeight: null }) : {};\n\n const rankedFiles = files\n .map((f) => ({ path: f.path, rank: ranks[f.path] ?? 0 }))\n .sort((a, b) => b.rank - a.rank || a.path.localeCompare(b.path));\n\n const enc = getEncoding(\"cl100k_base\");\n const blocks: string[] = [];\n let tokenCount = 0;\n let truncated = false;\n\n for (const file of rankedFiles) {\n const symbols = database.getSymbolsByFile(file.path);\n symbols.sort((a, b) => a.start_line - b.start_line);\n const lines: string[] = [file.path];\n for (const sym of symbols) {\n if (sym.kind === \"variable\" && !sym.signature) continue;\n const label = sym.signature ?? `${sym.kind} ${sym.name}`;\n lines.push(` ${label}`);\n }\n const block = lines.join(\"\\n\");\n const blockTokens = enc.encode(block).length;\n\n if (tokenCount + blockTokens <= budget) {\n blocks.push(block);\n tokenCount += blockTokens;\n continue;\n }\n\n const blockLines = block.split(\"\\n\");\n let partial = blockLines[0];\n let partialTokens = enc.encode(partial).length;\n\n if (tokenCount + partialTokens > budget) {\n truncated = true;\n break;\n }\n\n for (let i = 1; i < blockLines.length; i++) {\n const candidate = partial + \"\\n\" + blockLines[i];\n const candidateTokens = enc.encode(candidate).length;\n if (tokenCount + candidateTokens > budget) break;\n partial = candidate;\n partialTokens = candidateTokens;\n }\n\n blocks.push(partial);\n tokenCount += partialTokens;\n truncated = true;\n break;\n }\n\n const shown = blocks.length;\n const total = rankedFiles.length;\n const parts = [`[map] ${shown}/${total} files, ~${tokenCount} tokens`];\n if (truncated) {\n parts.push(`(budget: ${budget}, truncated)`);\n }\n\n return blocks.join(\"\\n\\n\") + `\\n\\n${parts.join(\" \")}`;\n}\n\n// ── serve command entry point ───────────────────────────────────────────\n\nexport async function runServe(\n _positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.error(`codefocus serve — Start MCP server (stdio transport)\n\nUsage: codefocus serve [options]\n\nOptions:\n --root <path> Root directory of indexed project (default: auto-detect)\n --help Show this help message\n\nThe MCP server exposes four tools:\n query Search and return ranked code context\n find Quick symbol lookup\n graph Show dependency graph for a file or symbol\n map High-level codebase overview\n\nThe server keeps the database connection and tiktoken encoder warm,\nso subsequent tool calls complete in ~10ms instead of ~400ms.\n\nUsage with Claude Code:\n Add to .mcp.json:\n {\n \"mcpServers\": {\n \"codefocus\": {\n \"command\": \"npx\",\n \"args\": [\"codefocus\", \"serve\", \"--root\", \"/path/to/project\"]\n }\n }\n }`);\n return;\n }\n\n const root = resolveRoot(flags.root);\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n\n if (!existsSync(dbPath)) {\n console.error(\n `Error: no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n process.exitCode = 1;\n return;\n }\n\n // Pre-warm the database connection\n getDb(root);\n console.error(`[codefocus] MCP server starting (root: ${root})`);\n console.error(`[codefocus] Database: ${dbPath}`);\n\n const server = createMcpServer(root);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(`[codefocus] MCP server ready (4 tools: query, find, graph, map)`);\n}\n","#!/usr/bin/env node\n/**\n * Standalone MCP server entry point.\n * Can be used directly: node dist/mcp-server.js --root /path/to/project\n * Or via the CLI: codefocus serve --root /path/to/project\n */\nimport { runServe } from \"./mcp.js\";\n\nconst args = process.argv.slice(2);\nconst flags: Record<string, string | boolean> = {};\n\nfor (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--\")) {\n const key = arg.slice(2);\n const next = args[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n}\n\nrunServe([], flags).catch((err) => {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n});\n"],"mappings":"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","names":["require","flags"]}
1
+ {"version":3,"sources":["../src/mcp.ts","../src/indexer.ts","../src/parser.ts","../src/mcp-entry.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { resolve } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { IndexDatabase } from \"./db.js\";\nimport { indexProject } from \"./indexer.js\";\nimport { resolveRoot } from \"./root.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\");\n\n// ── shared state ────────────────────────────────────────────────────────\n// The MCP server keeps the database connection and tiktoken encoder warm\n// so that subsequent tool calls pay ~10ms instead of ~400ms cold start.\n\nlet db: IndexDatabase | null = null;\nlet rootDir: string = \"\";\n\nfunction getDb(root: string): IndexDatabase {\n if (db && rootDir === root) return db;\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n if (!existsSync(dbPath)) {\n throw new Error(\n `no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n }\n if (db) db.close();\n db = new IndexDatabase(dbPath);\n rootDir = root;\n return db;\n}\n\n// ── MCP server setup ────────────────────────────────────────────────────\n\nexport function createMcpServer(defaultRoot?: string): McpServer {\n const server = new McpServer({\n name: \"codefocus\",\n version: pkg.version,\n });\n\n const effectiveRoot = defaultRoot ?? resolveRoot(undefined);\n\n // ── tool: query ─────────────────────────────────────────────────────\n\n server.tool(\n \"query\",\n \"Search the codebase and return ranked, budget-constrained code context. \" +\n \"Returns YAML front matter with confidence metadata followed by relevant source code sections.\",\n {\n term: z.string().describe(\"Search term (symbol name or keyword)\"),\n budget: z\n .number()\n .int()\n .positive()\n .default(8000)\n .describe(\"Token budget for output (default: 8000)\"),\n depth: z\n .number()\n .int()\n .min(0)\n .default(2)\n .describe(\"Max graph traversal depth (default: 2)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ term, budget, depth, root }) => {\n const r = root ?? effectiveRoot;\n // Import runQuery's internal logic so we can capture output\n const output = await captureQueryOutput(r, term, budget, depth);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: find ──────────────────────────────────────────────────────\n\n server.tool(\n \"find\",\n \"Quick symbol lookup by name with optional kind filter. \" +\n \"Returns symbol names, kinds, file paths, and line numbers.\",\n {\n symbol: z.string().describe(\"Symbol name to search for\"),\n kind: z\n .enum([\n \"function\",\n \"class\",\n \"interface\",\n \"type\",\n \"enum\",\n \"variable\",\n \"method\",\n \"all\",\n ])\n .default(\"all\")\n .describe(\"Filter by symbol kind (default: all)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ symbol, kind, root }) => {\n const r = root ?? effectiveRoot;\n const database = getDb(r);\n const results = database.findSymbols(symbol, kind);\n\n if (results.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `No symbols found matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}`,\n },\n ],\n };\n }\n\n const lines: string[] = [\n `Found ${results.length} symbol${results.length !== 1 ? \"s\" : \"\"} matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}:`,\n \"\",\n ];\n\n for (const sym of results) {\n const sig = sym.signature ? ` ${sym.signature}` : \"\";\n lines.push(\n ` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`,\n );\n }\n\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n },\n );\n\n // ── tool: graph ─────────────────────────────────────────────────────\n\n server.tool(\n \"graph\",\n \"Show the dependency graph for a file or symbol. \" +\n \"Displays incoming and/or outgoing edges as an indented tree.\",\n {\n target: z\n .string()\n .describe(\"File path or symbol name to show graph for\"),\n direction: z\n .enum([\"both\", \"incoming\", \"outgoing\"])\n .default(\"both\")\n .describe(\"Graph direction (default: both)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ target, direction, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureGraphOutput(r, target, direction);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: map ───────────────────────────────────────────────────────\n\n server.tool(\n \"map\",\n \"High-level codebase overview ranked by PageRank connectivity. \" +\n \"Returns file paths with their top symbols and signatures.\",\n {\n budget: z\n .number()\n .int()\n .positive()\n .default(2000)\n .describe(\"Token budget for output (default: 2000)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ budget, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureMapOutput(r, budget);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n return server;\n}\n\n// ── output capture helpers ──────────────────────────────────────────────\n// These re-implement the command logic to return strings instead of\n// writing to stdout, keeping the database connection warm.\n\nasync function captureQueryOutput(\n root: string,\n term: string,\n budget: number,\n depth: number,\n): Promise<string> {\n const { runQueryCore } = await import(\"./commands/query.js\");\n return runQueryCore(root, term, budget, depth);\n}\n\nfunction captureGraphOutput(\n root: string,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const database = getDb(root);\n\n const isFile =\n target.includes(\"/\") || target.includes(\"\\\\\") || /\\.\\w+$/.test(target);\n\n if (isFile) {\n return renderFileGraphToString(database, target, direction);\n } else {\n return renderSymbolGraphToString(database, target, direction);\n }\n}\n\nfunction renderFileGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n\n const graph = new DirectedGraph();\n for (const file of database.getAllFiles()) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (graph.hasEdge(key)) {\n const existing = graph.getEdgeAttributes(key);\n graph.setEdgeAttribute(\n key,\n \"specifiers\",\n `${existing.specifiers}, ${edge.specifiers}`,\n );\n } else {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file, {\n specifiers: edge.specifiers,\n });\n }\n }\n\n if (!graph.hasNode(target)) {\n return `Error: file \"${target}\" not found in the index`;\n }\n\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const outEdges: string[] = [];\n graph.forEachOutEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n _src: string,\n tgt: string,\n ) => {\n outEdges.push(` ${tgt} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n outEdges.length > 0\n ? `Dependencies (outgoing):\\n${outEdges.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const inEdges: string[] = [];\n graph.forEachInEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n src: string,\n ) => {\n inEdges.push(` ${src} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n inEdges.length > 0\n ? `Dependents (incoming):\\n${inEdges.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${target}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction renderSymbolGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const symbols = database.findSymbolsByName(target);\n const exactMatch = symbols.find((s) => s.name === target);\n const sym = exactMatch ?? symbols[0];\n\n if (!sym || !sym.id) {\n return `Error: symbol \"${target}\" not found in the index`;\n }\n\n const heading = `${sym.name} (${sym.kind}) — ${sym.file_path}:${sym.start_line}`;\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const refs = database.getOutgoingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.target_name} (${r.target_kind}) — ${r.target_file}:${r.target_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependencies (outgoing):\\n${lines.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const refs = database.getIncomingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.source_name} (${r.source_kind}) — ${r.source_file}:${r.source_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependents (incoming):\\n${lines.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${heading}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction captureMapOutput(root: string, budget: number): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n const pagerank = require(\"graphology-metrics/centrality/pagerank\");\n const { getEncoding } = require(\"js-tiktoken\");\n\n const database = getDb(root);\n const files = database.getAllFiles();\n\n if (files.length === 0) {\n return \"[map] Index is empty — no files to map.\";\n }\n\n // Compute PageRank\n const graph = new DirectedGraph();\n for (const file of files) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (!graph.hasEdge(key)) {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file);\n }\n }\n const ranks: Record<string, number> =\n graph.order > 0 ? pagerank(graph, { getEdgeWeight: null }) : {};\n\n const rankedFiles = files\n .map((f) => ({ path: f.path, rank: ranks[f.path] ?? 0 }))\n .sort((a, b) => b.rank - a.rank || a.path.localeCompare(b.path));\n\n const enc = getEncoding(\"cl100k_base\");\n const blocks: string[] = [];\n let tokenCount = 0;\n let truncated = false;\n\n for (const file of rankedFiles) {\n const symbols = database.getSymbolsByFile(file.path);\n symbols.sort((a, b) => a.start_line - b.start_line);\n const lines: string[] = [file.path];\n for (const sym of symbols) {\n if (sym.kind === \"variable\" && !sym.signature) continue;\n const label = sym.signature ?? `${sym.kind} ${sym.name}`;\n lines.push(` ${label}`);\n }\n const block = lines.join(\"\\n\");\n const blockTokens = enc.encode(block).length;\n\n if (tokenCount + blockTokens <= budget) {\n blocks.push(block);\n tokenCount += blockTokens;\n continue;\n }\n\n const blockLines = block.split(\"\\n\");\n let partial = blockLines[0];\n let partialTokens = enc.encode(partial).length;\n\n if (tokenCount + partialTokens > budget) {\n truncated = true;\n break;\n }\n\n for (let i = 1; i < blockLines.length; i++) {\n const candidate = partial + \"\\n\" + blockLines[i];\n const candidateTokens = enc.encode(candidate).length;\n if (tokenCount + candidateTokens > budget) break;\n partial = candidate;\n partialTokens = candidateTokens;\n }\n\n blocks.push(partial);\n tokenCount += partialTokens;\n truncated = true;\n break;\n }\n\n const shown = blocks.length;\n const total = rankedFiles.length;\n const parts = [`[map] ${shown}/${total} files, ~${tokenCount} tokens`];\n if (truncated) {\n parts.push(`(budget: ${budget}, truncated)`);\n }\n\n return blocks.join(\"\\n\\n\") + `\\n\\n${parts.join(\" \")}`;\n}\n\n// ── serve command entry point ───────────────────────────────────────────\n\nexport async function runServe(\n _positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.error(`codefocus serve — Start MCP server (stdio transport)\n\nUsage: codefocus serve [options]\n\nOptions:\n --root <path> Root directory of indexed project (default: auto-detect)\n --help Show this help message\n\nThe MCP server exposes four tools:\n query Search and return ranked code context\n find Quick symbol lookup\n graph Show dependency graph for a file or symbol\n map High-level codebase overview\n\nThe server keeps the database connection and tiktoken encoder warm,\nso subsequent tool calls complete in ~10ms instead of ~400ms.\n\nUsage with Claude Code:\n Add to .mcp.json:\n {\n \"mcpServers\": {\n \"codefocus\": {\n \"command\": \"npx\",\n \"args\": [\"codefocus\", \"serve\", \"--root\", \"/path/to/project\"]\n }\n }\n }`);\n return;\n }\n\n const root = resolveRoot(flags.root);\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n\n if (!existsSync(dbPath)) {\n if (!existsSync(root)) {\n console.error(\n `Error: root directory does not exist: ${root}`,\n );\n process.exitCode = 1;\n return;\n }\n console.error(`[codefocus] No index found — auto-indexing ${root} ...`);\n const stats = await indexProject(root, dbPath);\n console.error(\n `[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`,\n );\n }\n\n // Pre-warm the database connection\n getDb(root);\n console.error(`[codefocus] MCP server starting (root: ${root})`);\n console.error(`[codefocus] Database: ${dbPath}`);\n\n const server = createMcpServer(root);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(`[codefocus] MCP server ready (4 tools: query, find, graph, map)`);\n}\n","import { createRequire } from \"node:module\";\nimport { readFileSync } from \"node:fs\";\nimport { resolve, relative, dirname, extname } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { IndexDatabase } from \"./db.js\";\nimport { parseSource, findIdentifierUsages, type ImportDef } from \"./parser.js\";\n\nconst require = createRequire(import.meta.url);\nconst fg = require(\"fast-glob\");\n\nconst TS_EXTENSIONS = new Set([\".ts\", \".tsx\", \".js\", \".jsx\"]);\nconst IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/.codefocus/**\",\n \"**/__tests__/**\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.d.ts\",\n];\n\nexport interface IndexStats {\n filesIndexed: number;\n filesSkipped: number;\n symbolsExtracted: number;\n importsFound: number;\n referencesCreated: number;\n timeMs: number;\n}\n\nfunction hashContent(content: string): string {\n return createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction resolveImportPath(\n importSource: string,\n fromFile: string,\n rootDir: string,\n allFiles: Set<string>,\n): string | null {\n if (!importSource.startsWith(\".\")) return null;\n\n const fromDir = dirname(fromFile);\n const basePath = resolve(rootDir, fromDir, importSource);\n\n // Strip .js/.jsx extension for ESM-style imports (import from \"./foo.js\" → foo.ts)\n const ext = extname(basePath);\n const strippedBase =\n ext === \".js\" || ext === \".jsx\" ? basePath.slice(0, -ext.length) : null;\n\n const candidates = [\n basePath,\n ...Array.from(TS_EXTENSIONS).map((e) => basePath + e),\n ...Array.from(TS_EXTENSIONS).map((e) => resolve(basePath, \"index\" + e)),\n ];\n\n // Also try stripped base with TS extensions (.js → .ts, .tsx, etc.)\n if (strippedBase) {\n candidates.push(\n ...Array.from(TS_EXTENSIONS).map((e) => strippedBase + e),\n ...Array.from(TS_EXTENSIONS).map((e) =>\n resolve(strippedBase, \"index\" + e),\n ),\n );\n }\n\n for (const candidate of candidates) {\n const rel = relative(rootDir, candidate);\n if (allFiles.has(rel)) return rel;\n }\n\n return null;\n}\n\nexport async function indexProject(\n rootDir: string,\n dbPath: string,\n): Promise<IndexStats> {\n const startTime = Date.now();\n const absRoot = resolve(rootDir);\n\n const files: string[] = await fg(\"**/*.{ts,tsx,js,jsx}\", {\n cwd: absRoot,\n ignore: IGNORE_PATTERNS,\n absolute: false,\n });\n\n const allFiles = new Set(files);\n const db = new IndexDatabase(dbPath);\n\n let filesIndexed = 0;\n let filesSkipped = 0;\n let symbolsExtracted = 0;\n let importsFound = 0;\n let referencesCreated = 0;\n\n // Map from \"filePath:symbolName\" → symbolId for reference creation\n const symbolIdMap = new Map<string, number>();\n // Cache imports per file for the reference-creation pass\n const fileImports = new Map<string, ImportDef[]>();\n // Cache file contents for identifier usage extraction\n const fileContents = new Map<string, string>();\n\n db.transaction(() => {\n // Pass 1: parse files, extract and store symbols, collect imports\n for (const filePath of files) {\n const absPath = resolve(absRoot, filePath);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n filesSkipped++;\n continue;\n }\n\n const hash = hashContent(content);\n const existingHash = db.getFileHash(filePath);\n\n // Always parse to collect imports for reference creation\n const result = parseSource(content);\n if (result.imports.length > 0) {\n fileImports.set(filePath, result.imports);\n fileContents.set(filePath, content);\n }\n\n if (existingHash === hash) {\n filesSkipped++;\n // Load existing symbol IDs into the map\n for (const sym of db.getSymbolsByFile(filePath)) {\n symbolIdMap.set(`${filePath}:${sym.name}`, sym.id!);\n }\n continue;\n }\n\n // Clear old data for this file\n db.clearFile(filePath);\n\n const ext = extname(filePath);\n const language =\n ext === \".js\" || ext === \".jsx\" ? \"javascript\" : \"typescript\";\n\n db.upsertFile({\n path: filePath,\n content_hash: hash,\n language,\n last_indexed: Date.now(),\n });\n\n // Populate FTS5 content index\n db.upsertFileContent(filePath, content);\n\n for (const sym of result.symbols) {\n const id = db.insertSymbol({\n file_path: filePath,\n name: sym.name,\n kind: sym.kind,\n start_byte: sym.startByte,\n end_byte: sym.endByte,\n start_line: sym.startLine,\n end_line: sym.endLine,\n start_column: sym.startColumn,\n end_column: sym.endColumn,\n signature: sym.signature,\n });\n symbolIdMap.set(`${filePath}:${sym.name}`, id);\n symbolsExtracted++;\n }\n\n filesIndexed++;\n }\n\n // Pass 2: clear all imports/references and recreate from cached data\n db.clearAllImports();\n db.clearAllReferences();\n\n for (const [filePath, imports] of fileImports) {\n // Cache symbols for this file once (avoid repeated DB queries)\n const fileSymbolRows = db.getSymbolsByFile(filePath);\n const sourceSymbols = fileSymbolRows.map((sym) => ({\n name: sym.name,\n id: sym.id!,\n startLine: sym.start_line,\n endLine: sym.end_line,\n }));\n\n // Collect all imported names across all imports for this file\n const allImportedNames = new Set<string>();\n for (const imp of imports) {\n for (const specName of imp.specifiers) {\n const cleanName = specName.startsWith(\"* as \")\n ? specName.slice(5)\n : specName;\n allImportedNames.add(cleanName);\n }\n }\n\n // Find identifier usages once per file for all imported names\n const content = fileContents.get(filePath);\n const usages = content\n ? findIdentifierUsages(content, allImportedNames)\n : [];\n\n for (const imp of imports) {\n const targetFile = resolveImportPath(\n imp.source,\n filePath,\n absRoot,\n allFiles,\n );\n\n // Store every import declaration in the imports table\n for (const specName of imp.specifiers) {\n const cleanName = specName.startsWith(\"* as \")\n ? specName.slice(5)\n : specName;\n db.insertImport({\n file_path: filePath,\n specifier: cleanName,\n source_path: targetFile,\n raw_module: imp.source,\n is_type_only: imp.isTypeOnly ? 1 : 0,\n });\n importsFound++;\n }\n\n if (!targetFile) continue;\n\n const refType = imp.isTypeOnly ? \"type_ref\" : \"import\";\n\n for (const specName of imp.specifiers) {\n const cleanName = specName.startsWith(\"* as \")\n ? specName.slice(5)\n : specName;\n\n const targetKey = `${targetFile}:${cleanName}`;\n const targetId = symbolIdMap.get(targetKey);\n if (!targetId) continue;\n\n // Find usages of this specific imported name\n const nameUsages = usages.filter((u) => u.name === cleanName);\n\n if (nameUsages.length > 0) {\n // Create references only from symbols that actually use the import\n const referencingSymbolIds = new Set<number>();\n\n for (const usage of nameUsages) {\n for (const srcSym of sourceSymbols) {\n if (\n srcSym.id !== targetId &&\n usage.line >= srcSym.startLine &&\n usage.line <= srcSym.endLine\n ) {\n referencingSymbolIds.add(srcSym.id);\n }\n }\n }\n\n for (const sourceId of referencingSymbolIds) {\n db.insertReference({\n source_symbol_id: sourceId,\n target_symbol_id: targetId,\n ref_type: refType,\n });\n referencesCreated++;\n }\n } else {\n // No usages in code bodies (e.g., re-exports, or type-only imports\n // used only in type positions). Create references from all source\n // symbols for graph connectivity.\n for (const srcSym of sourceSymbols) {\n if (srcSym.id !== targetId) {\n db.insertReference({\n source_symbol_id: srcSym.id,\n target_symbol_id: targetId,\n ref_type: refType,\n });\n referencesCreated++;\n }\n }\n }\n }\n }\n }\n });\n\n db.close();\n\n return {\n filesIndexed,\n filesSkipped,\n symbolsExtracted,\n importsFound,\n referencesCreated,\n timeMs: Date.now() - startTime,\n };\n}\n","import { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\nconst Parser = require(\"tree-sitter\");\nconst TypeScriptLang = require(\"tree-sitter-typescript\").typescript;\n\nexport interface SymbolDef {\n name: string;\n kind: string;\n startByte: number;\n endByte: number;\n startLine: number;\n endLine: number;\n startColumn: number;\n endColumn: number;\n signature: string | null;\n exported: boolean;\n}\n\nexport interface ImportDef {\n specifiers: string[];\n source: string;\n isTypeOnly: boolean;\n}\n\nexport interface IdentifierUsage {\n name: string;\n line: number;\n column: number;\n}\n\nexport interface ParseResult {\n symbols: SymbolDef[];\n imports: ImportDef[];\n}\n\ninterface TreeSitterNode {\n type: string;\n text: string;\n startPosition: { row: number; column: number };\n endPosition: { row: number; column: number };\n startIndex: number;\n endIndex: number;\n childCount: number;\n namedChildCount: number;\n child(index: number): TreeSitterNode | null;\n namedChild(index: number): TreeSitterNode | null;\n childForFieldName(name: string): TreeSitterNode | null;\n children: TreeSitterNode[];\n namedChildren: TreeSitterNode[];\n parent: TreeSitterNode | null;\n}\n\nlet parser: InstanceType<typeof Parser> | null = null;\n\nfunction getParser(): InstanceType<typeof Parser> {\n if (!parser) {\n parser = new Parser();\n parser.setLanguage(TypeScriptLang);\n }\n return parser;\n}\n\nfunction extractSignature(node: TreeSitterNode, source: string): string | null {\n const kind = node.type;\n\n if (kind === \"function_declaration\" || kind === \"arrow_function\") {\n const nameNode = node.childForFieldName(\"name\");\n const paramsNode = node.childForFieldName(\"parameters\");\n const returnType = node.childForFieldName(\"return_type\");\n if (nameNode && paramsNode) {\n let sig = `function ${nameNode.text}${paramsNode.text}`;\n if (returnType) sig += returnType.text;\n return sig;\n }\n }\n\n if (kind === \"class_declaration\") {\n const nameNode = node.childForFieldName(\"name\");\n if (nameNode) return `class ${nameNode.text}`;\n }\n\n if (kind === \"interface_declaration\") {\n const nameNode = node.childForFieldName(\"name\");\n if (nameNode) return `interface ${nameNode.text}`;\n }\n\n if (kind === \"type_alias_declaration\") {\n const text = node.text;\n const semiIdx = text.indexOf(\";\");\n return semiIdx > 0 ? text.slice(0, semiIdx) : text;\n }\n\n if (kind === \"enum_declaration\") {\n const nameNode = node.childForFieldName(\"name\");\n if (nameNode) return `enum ${nameNode.text}`;\n }\n\n if (kind === \"method_definition\") {\n const nameNode = node.childForFieldName(\"name\");\n const paramsNode = node.childForFieldName(\"parameters\");\n const returnType = node.childForFieldName(\"return_type\");\n if (nameNode && paramsNode) {\n let sig = `${nameNode.text}${paramsNode.text}`;\n if (returnType) sig += returnType.text;\n return sig;\n }\n }\n\n return null;\n}\n\nfunction getSymbolName(node: TreeSitterNode): string | null {\n const nameNode =\n node.childForFieldName(\"name\") ?? node.childForFieldName(\"declarator\");\n return nameNode?.text ?? null;\n}\n\nfunction extractDeclaration(node: TreeSitterNode, source: string, exported: boolean): SymbolDef | null {\n const kindMap: Record<string, string> = {\n function_declaration: \"function\",\n class_declaration: \"class\",\n interface_declaration: \"interface\",\n type_alias_declaration: \"type\",\n enum_declaration: \"enum\",\n };\n\n const kind = kindMap[node.type];\n if (!kind) return null;\n\n const name = getSymbolName(node);\n if (!name) return null;\n\n return {\n name,\n kind,\n startByte: node.startIndex,\n endByte: node.endIndex,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n signature: extractSignature(node, source),\n exported,\n };\n}\n\nfunction extractMethods(classNode: TreeSitterNode, source: string, exported: boolean): SymbolDef[] {\n const methods: SymbolDef[] = [];\n const body = classNode.childForFieldName(\"body\");\n if (!body) return methods;\n\n for (const child of body.namedChildren) {\n if (child.type === \"method_definition\") {\n const nameNode = child.childForFieldName(\"name\");\n if (!nameNode) continue;\n methods.push({\n name: nameNode.text,\n kind: \"method\",\n startByte: child.startIndex,\n endByte: child.endIndex,\n startLine: child.startPosition.row + 1,\n endLine: child.endPosition.row + 1,\n startColumn: child.startPosition.column,\n endColumn: child.endPosition.column,\n signature: extractSignature(child, source),\n exported,\n });\n }\n }\n return methods;\n}\n\nfunction extractVariableDeclarations(node: TreeSitterNode, exported: boolean): SymbolDef[] {\n const symbols: SymbolDef[] = [];\n for (const child of node.namedChildren) {\n if (child.type === \"variable_declarator\") {\n const nameNode = child.childForFieldName(\"name\");\n if (nameNode) {\n symbols.push({\n name: nameNode.text,\n kind: \"variable\",\n startByte: node.startIndex,\n endByte: node.endIndex,\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n startColumn: node.startPosition.column,\n endColumn: node.endPosition.column,\n signature: null,\n exported,\n });\n }\n }\n }\n return symbols;\n}\n\nfunction extractImport(node: TreeSitterNode): ImportDef | null {\n let sourceNode: TreeSitterNode | null = null;\n for (const child of node.children) {\n if (child.type === \"string\") {\n sourceNode = child;\n break;\n }\n }\n if (!sourceNode) return null;\n\n const rawSource = sourceNode.text;\n const source = rawSource.replace(/^['\"]|['\"]$/g, \"\");\n\n const specifiers: string[] = [];\n const isTypeOnly = node.children.some(\n (c: TreeSitterNode) => c.type === \"type\" && c.text === \"type\",\n );\n\n for (const child of node.children) {\n if (child.type === \"import_clause\") {\n for (const clause of child.namedChildren) {\n if (clause.type === \"named_imports\") {\n for (const spec of clause.namedChildren) {\n if (spec.type === \"import_specifier\") {\n const nameNode = spec.childForFieldName(\"name\");\n const aliasNode = spec.childForFieldName(\"alias\");\n specifiers.push(aliasNode?.text ?? nameNode?.text ?? spec.text);\n }\n }\n } else if (clause.type === \"identifier\") {\n specifiers.push(clause.text);\n } else if (clause.type === \"namespace_import\") {\n const nameNode = clause.namedChildren.find(\n (c: TreeSitterNode) => c.type === \"identifier\",\n );\n if (nameNode) specifiers.push(`* as ${nameNode.text}`);\n }\n }\n }\n }\n\n return { specifiers, source, isTypeOnly };\n}\n\nfunction extractReExportSpecifiers(node: TreeSitterNode): string[] {\n const specifiers: string[] = [];\n for (const child of node.children) {\n if (child.type === \"export_clause\") {\n for (const spec of child.namedChildren) {\n if (spec.type === \"export_specifier\") {\n const nameNode = spec.childForFieldName(\"name\");\n if (nameNode) specifiers.push(nameNode.text);\n }\n }\n }\n }\n return specifiers;\n}\n\n/**\n * Find all identifier nodes in the AST that match any of the given names.\n * Used to determine which symbols in a file actually reference imported names.\n */\nexport function findIdentifierUsages(source: string, names: Set<string>): IdentifierUsage[] {\n if (names.size === 0) return [];\n\n const p = getParser();\n const tree = p.parse(source);\n const root: TreeSitterNode = tree.rootNode;\n const usages: IdentifierUsage[] = [];\n\n function walk(node: TreeSitterNode): void {\n if (\n (node.type === \"identifier\" || node.type === \"type_identifier\") &&\n names.has(node.text)\n ) {\n // Skip identifiers that are part of import/export declarations\n let parent = node.parent;\n let isImportExport = false;\n while (parent) {\n if (\n parent.type === \"import_statement\" ||\n parent.type === \"import_clause\" ||\n parent.type === \"import_specifier\" ||\n (parent.type === \"export_statement\" &&\n parent.children.some((c: TreeSitterNode) => c.type === \"from\"))\n ) {\n isImportExport = true;\n break;\n }\n parent = parent.parent;\n }\n if (!isImportExport) {\n usages.push({\n name: node.text,\n line: node.startPosition.row + 1,\n column: node.startPosition.column,\n });\n }\n }\n for (const child of node.children) {\n walk(child);\n }\n }\n\n walk(root);\n return usages;\n}\n\n/**\n * Walk the full AST to find dynamic import() calls and extract them as imports.\n * Handles patterns like:\n * const { foo } = await import('./module.js')\n * import('./module.js').then(m => ...)\n */\nfunction extractDynamicImports(root: TreeSitterNode): ImportDef[] {\n const imports: ImportDef[] = [];\n\n function walk(node: TreeSitterNode): void {\n if (node.type === \"call_expression\") {\n // Check if this is an import() call — first child is the `import` keyword\n const fn = node.child(0);\n if (fn && fn.type === \"import\") {\n const argsNode = node.childForFieldName(\"arguments\");\n if (argsNode) {\n // Extract the module path from the first string argument\n const strArg = argsNode.namedChildren.find(\n (c: TreeSitterNode) => c.type === \"string\",\n );\n if (strArg) {\n const modulePath = strArg.text.replace(/^['\"]|['\"]$/g, \"\");\n const specifiers = extractDynamicImportSpecifiers(node);\n imports.push({ specifiers, source: modulePath, isTypeOnly: false });\n }\n }\n // Don't walk into the import() call's children\n return;\n }\n }\n for (const child of node.children) {\n walk(child);\n }\n }\n\n walk(root);\n return imports;\n}\n\n/**\n * Try to extract destructured specifiers from the context around a\n * dynamic import() call_expression, e.g.:\n * const { runIndex } = await import('./commands/index.js')\n * ^^^^^^^^^^^^ these specifiers\n */\nfunction extractDynamicImportSpecifiers(importCall: TreeSitterNode): string[] {\n // Walk up: import() → await_expression → variable_declarator\n let current: TreeSitterNode | null = importCall.parent;\n\n // Skip await_expression wrapper if present\n if (current && current.type === \"await_expression\") {\n current = current.parent;\n }\n\n if (current && current.type === \"variable_declarator\") {\n const pattern = current.childForFieldName(\"name\");\n if (pattern && pattern.type === \"object_pattern\") {\n const specs: string[] = [];\n for (const child of pattern.namedChildren) {\n if (child.type === \"shorthand_property_identifier_pattern\") {\n specs.push(child.text);\n } else if (child.type === \"pair_pattern\") {\n // { original: alias } destructuring — use the alias\n const value = child.childForFieldName(\"value\");\n if (value) specs.push(value.text);\n }\n }\n return specs;\n }\n }\n\n return [];\n}\n\nexport function parseSource(source: string): ParseResult {\n const p = getParser();\n const tree = p.parse(source);\n const root: TreeSitterNode = tree.rootNode;\n\n const symbols: SymbolDef[] = [];\n const imports: ImportDef[] = [];\n\n for (const node of root.namedChildren) {\n if (node.type === \"import_statement\") {\n const imp = extractImport(node);\n if (imp) imports.push(imp);\n continue;\n }\n\n if (node.type === \"export_statement\") {\n // Re-export: export { X } from './foo'\n const hasFrom = node.children.some(\n (c: TreeSitterNode) => c.type === \"from\",\n );\n if (hasFrom) {\n // Treat re-exports as imports from the source module\n let sourceNode: TreeSitterNode | null = null;\n for (const child of node.children) {\n if (child.type === \"string\") {\n sourceNode = child;\n break;\n }\n }\n if (sourceNode) {\n const rawSource = sourceNode.text;\n const source = rawSource.replace(/^['\"]|['\"]$/g, \"\");\n const specifiers = extractReExportSpecifiers(node);\n const isTypeOnly = node.children.some(\n (c: TreeSitterNode) => c.type === \"type\" && c.text === \"type\",\n );\n imports.push({ specifiers, source, isTypeOnly });\n }\n continue;\n }\n\n // Exported declaration: export function foo() {}\n for (const child of node.namedChildren) {\n const decl = extractDeclaration(child, source, true);\n if (decl) {\n symbols.push(decl);\n if (child.type === \"class_declaration\") {\n symbols.push(...extractMethods(child, source, true));\n }\n }\n\n if (\n child.type === \"lexical_declaration\" ||\n child.type === \"variable_declaration\"\n ) {\n symbols.push(...extractVariableDeclarations(child, true));\n }\n }\n continue;\n }\n\n // Top-level declarations (not exported)\n const decl = extractDeclaration(node, source, false);\n if (decl) {\n symbols.push(decl);\n if (node.type === \"class_declaration\") {\n symbols.push(...extractMethods(node, source, false));\n }\n }\n\n if (\n node.type === \"lexical_declaration\" ||\n node.type === \"variable_declaration\"\n ) {\n symbols.push(...extractVariableDeclarations(node, false));\n }\n }\n\n // Extract dynamic import() calls from the full AST\n imports.push(...extractDynamicImports(root));\n\n return { symbols, imports };\n}\n","#!/usr/bin/env node\n/**\n * Standalone MCP server entry point.\n * Can be used directly: node dist/mcp-server.js --root /path/to/project\n * Or via the CLI: codefocus serve --root /path/to/project\n */\nimport { runServe } from \"./mcp.js\";\n\nconst args = process.argv.slice(2);\nconst flags: Record<string, string | boolean> = {};\n\nfor (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--\")) {\n const key = arg.slice(2);\n const next = args[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n}\n\nrunServe([], flags).catch((err) => {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAAA,SAAS,iBAAAA,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AACxB,SAAS,kBAAgC;AACzC,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,SAAS;;;ACLlB,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,oBAAoB;AAC7B,SAAS,SAAS,UAAU,SAAS,eAAe;AACpD,SAAS,kBAAkB;;;ACH3B,SAAS,qBAAqB;AAE9B,IAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,SAASA,SAAQ,aAAa;AACpC,IAAM,iBAAiBA,SAAQ,wBAAwB,EAAE;AAiDzD,IAAI,SAA6C;AAEjD,SAAS,YAAyC;AAChD,MAAI,CAAC,QAAQ;AACX,aAAS,IAAI,OAAO;AACpB,WAAO,YAAY,cAAc;AAAA,EACnC;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAsB,QAA+B;AAC7E,QAAM,OAAO,KAAK;AAElB,MAAI,SAAS,0BAA0B,SAAS,kBAAkB;AAChE,UAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,UAAM,aAAa,KAAK,kBAAkB,YAAY;AACtD,UAAM,aAAa,KAAK,kBAAkB,aAAa;AACvD,QAAI,YAAY,YAAY;AAC1B,UAAI,MAAM,YAAY,SAAS,IAAI,GAAG,WAAW,IAAI;AACrD,UAAI,WAAY,QAAO,WAAW;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,SAAS,qBAAqB;AAChC,UAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,QAAI,SAAU,QAAO,SAAS,SAAS,IAAI;AAAA,EAC7C;AAEA,MAAI,SAAS,yBAAyB;AACpC,UAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,QAAI,SAAU,QAAO,aAAa,SAAS,IAAI;AAAA,EACjD;AAEA,MAAI,SAAS,0BAA0B;AACrC,UAAM,OAAO,KAAK;AAClB,UAAM,UAAU,KAAK,QAAQ,GAAG;AAChC,WAAO,UAAU,IAAI,KAAK,MAAM,GAAG,OAAO,IAAI;AAAA,EAChD;AAEA,MAAI,SAAS,oBAAoB;AAC/B,UAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,QAAI,SAAU,QAAO,QAAQ,SAAS,IAAI;AAAA,EAC5C;AAEA,MAAI,SAAS,qBAAqB;AAChC,UAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,UAAM,aAAa,KAAK,kBAAkB,YAAY;AACtD,UAAM,aAAa,KAAK,kBAAkB,aAAa;AACvD,QAAI,YAAY,YAAY;AAC1B,UAAI,MAAM,GAAG,SAAS,IAAI,GAAG,WAAW,IAAI;AAC5C,UAAI,WAAY,QAAO,WAAW;AAClC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,MAAqC;AAC1D,QAAM,WACJ,KAAK,kBAAkB,MAAM,KAAK,KAAK,kBAAkB,YAAY;AACvE,SAAO,UAAU,QAAQ;AAC3B;AAEA,SAAS,mBAAmB,MAAsB,QAAgB,UAAqC;AACrG,QAAM,UAAkC;AAAA,IACtC,sBAAsB;AAAA,IACtB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,IACxB,kBAAkB;AAAA,EACpB;AAEA,QAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,OAAO,cAAc,IAAI;AAC/B,MAAI,CAAC,KAAM,QAAO;AAElB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,KAAK;AAAA,IAChB,SAAS,KAAK;AAAA,IACd,WAAW,KAAK,cAAc,MAAM;AAAA,IACpC,SAAS,KAAK,YAAY,MAAM;AAAA,IAChC,aAAa,KAAK,cAAc;AAAA,IAChC,WAAW,KAAK,YAAY;AAAA,IAC5B,WAAW,iBAAiB,MAAM,MAAM;AAAA,IACxC;AAAA,EACF;AACF;AAEA,SAAS,eAAe,WAA2B,QAAgB,UAAgC;AACjG,QAAM,UAAuB,CAAC;AAC9B,QAAM,OAAO,UAAU,kBAAkB,MAAM;AAC/C,MAAI,CAAC,KAAM,QAAO;AAElB,aAAW,SAAS,KAAK,eAAe;AACtC,QAAI,MAAM,SAAS,qBAAqB;AACtC,YAAM,WAAW,MAAM,kBAAkB,MAAM;AAC/C,UAAI,CAAC,SAAU;AACf,cAAQ,KAAK;AAAA,QACX,MAAM,SAAS;AAAA,QACf,MAAM;AAAA,QACN,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,WAAW,MAAM,cAAc,MAAM;AAAA,QACrC,SAAS,MAAM,YAAY,MAAM;AAAA,QACjC,aAAa,MAAM,cAAc;AAAA,QACjC,WAAW,MAAM,YAAY;AAAA,QAC7B,WAAW,iBAAiB,OAAO,MAAM;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,4BAA4B,MAAsB,UAAgC;AACzF,QAAM,UAAuB,CAAC;AAC9B,aAAW,SAAS,KAAK,eAAe;AACtC,QAAI,MAAM,SAAS,uBAAuB;AACxC,YAAM,WAAW,MAAM,kBAAkB,MAAM;AAC/C,UAAI,UAAU;AACZ,gBAAQ,KAAK;AAAA,UACX,MAAM,SAAS;AAAA,UACf,MAAM;AAAA,UACN,WAAW,KAAK;AAAA,UAChB,SAAS,KAAK;AAAA,UACd,WAAW,KAAK,cAAc,MAAM;AAAA,UACpC,SAAS,KAAK,YAAY,MAAM;AAAA,UAChC,aAAa,KAAK,cAAc;AAAA,UAChC,WAAW,KAAK,YAAY;AAAA,UAC5B,WAAW;AAAA,UACX;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,cAAc,MAAwC;AAC7D,MAAI,aAAoC;AACxC,aAAW,SAAS,KAAK,UAAU;AACjC,QAAI,MAAM,SAAS,UAAU;AAC3B,mBAAa;AACb;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,YAAY,WAAW;AAC7B,QAAM,SAAS,UAAU,QAAQ,gBAAgB,EAAE;AAEnD,QAAM,aAAuB,CAAC;AAC9B,QAAM,aAAa,KAAK,SAAS;AAAA,IAC/B,CAAC,MAAsB,EAAE,SAAS,UAAU,EAAE,SAAS;AAAA,EACzD;AAEA,aAAW,SAAS,KAAK,UAAU;AACjC,QAAI,MAAM,SAAS,iBAAiB;AAClC,iBAAW,UAAU,MAAM,eAAe;AACxC,YAAI,OAAO,SAAS,iBAAiB;AACnC,qBAAW,QAAQ,OAAO,eAAe;AACvC,gBAAI,KAAK,SAAS,oBAAoB;AACpC,oBAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,oBAAM,YAAY,KAAK,kBAAkB,OAAO;AAChD,yBAAW,KAAK,WAAW,QAAQ,UAAU,QAAQ,KAAK,IAAI;AAAA,YAChE;AAAA,UACF;AAAA,QACF,WAAW,OAAO,SAAS,cAAc;AACvC,qBAAW,KAAK,OAAO,IAAI;AAAA,QAC7B,WAAW,OAAO,SAAS,oBAAoB;AAC7C,gBAAM,WAAW,OAAO,cAAc;AAAA,YACpC,CAAC,MAAsB,EAAE,SAAS;AAAA,UACpC;AACA,cAAI,SAAU,YAAW,KAAK,QAAQ,SAAS,IAAI,EAAE;AAAA,QACvD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,YAAY,QAAQ,WAAW;AAC1C;AAEA,SAAS,0BAA0B,MAAgC;AACjE,QAAM,aAAuB,CAAC;AAC9B,aAAW,SAAS,KAAK,UAAU;AACjC,QAAI,MAAM,SAAS,iBAAiB;AAClC,iBAAW,QAAQ,MAAM,eAAe;AACtC,YAAI,KAAK,SAAS,oBAAoB;AACpC,gBAAM,WAAW,KAAK,kBAAkB,MAAM;AAC9C,cAAI,SAAU,YAAW,KAAK,SAAS,IAAI;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,qBAAqB,QAAgB,OAAuC;AAC1F,MAAI,MAAM,SAAS,EAAG,QAAO,CAAC;AAE9B,QAAM,IAAI,UAAU;AACpB,QAAM,OAAO,EAAE,MAAM,MAAM;AAC3B,QAAM,OAAuB,KAAK;AAClC,QAAM,SAA4B,CAAC;AAEnC,WAAS,KAAK,MAA4B;AACxC,SACG,KAAK,SAAS,gBAAgB,KAAK,SAAS,sBAC7C,MAAM,IAAI,KAAK,IAAI,GACnB;AAEA,UAAI,SAAS,KAAK;AAClB,UAAI,iBAAiB;AACrB,aAAO,QAAQ;AACb,YACE,OAAO,SAAS,sBAChB,OAAO,SAAS,mBAChB,OAAO,SAAS,sBACf,OAAO,SAAS,sBACf,OAAO,SAAS,KAAK,CAAC,MAAsB,EAAE,SAAS,MAAM,GAC/D;AACA,2BAAiB;AACjB;AAAA,QACF;AACA,iBAAS,OAAO;AAAA,MAClB;AACA,UAAI,CAAC,gBAAgB;AACnB,eAAO,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,MAAM,KAAK,cAAc,MAAM;AAAA,UAC/B,QAAQ,KAAK,cAAc;AAAA,QAC7B,CAAC;AAAA,MACH;AAAA,IACF;AACA,eAAW,SAAS,KAAK,UAAU;AACjC,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAEA,OAAK,IAAI;AACT,SAAO;AACT;AAQA,SAAS,sBAAsB,MAAmC;AAChE,QAAM,UAAuB,CAAC;AAE9B,WAAS,KAAK,MAA4B;AACxC,QAAI,KAAK,SAAS,mBAAmB;AAEnC,YAAM,KAAK,KAAK,MAAM,CAAC;AACvB,UAAI,MAAM,GAAG,SAAS,UAAU;AAC9B,cAAM,WAAW,KAAK,kBAAkB,WAAW;AACnD,YAAI,UAAU;AAEZ,gBAAM,SAAS,SAAS,cAAc;AAAA,YACpC,CAAC,MAAsB,EAAE,SAAS;AAAA,UACpC;AACA,cAAI,QAAQ;AACV,kBAAM,aAAa,OAAO,KAAK,QAAQ,gBAAgB,EAAE;AACzD,kBAAM,aAAa,+BAA+B,IAAI;AACtD,oBAAQ,KAAK,EAAE,YAAY,QAAQ,YAAY,YAAY,MAAM,CAAC;AAAA,UACpE;AAAA,QACF;AAEA;AAAA,MACF;AAAA,IACF;AACA,eAAW,SAAS,KAAK,UAAU;AACjC,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAEA,OAAK,IAAI;AACT,SAAO;AACT;AAQA,SAAS,+BAA+B,YAAsC;AAE5E,MAAI,UAAiC,WAAW;AAGhD,MAAI,WAAW,QAAQ,SAAS,oBAAoB;AAClD,cAAU,QAAQ;AAAA,EACpB;AAEA,MAAI,WAAW,QAAQ,SAAS,uBAAuB;AACrD,UAAM,UAAU,QAAQ,kBAAkB,MAAM;AAChD,QAAI,WAAW,QAAQ,SAAS,kBAAkB;AAChD,YAAM,QAAkB,CAAC;AACzB,iBAAW,SAAS,QAAQ,eAAe;AACzC,YAAI,MAAM,SAAS,yCAAyC;AAC1D,gBAAM,KAAK,MAAM,IAAI;AAAA,QACvB,WAAW,MAAM,SAAS,gBAAgB;AAExC,gBAAM,QAAQ,MAAM,kBAAkB,OAAO;AAC7C,cAAI,MAAO,OAAM,KAAK,MAAM,IAAI;AAAA,QAClC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,CAAC;AACV;AAEO,SAAS,YAAY,QAA6B;AACvD,QAAM,IAAI,UAAU;AACpB,QAAM,OAAO,EAAE,MAAM,MAAM;AAC3B,QAAM,OAAuB,KAAK;AAElC,QAAM,UAAuB,CAAC;AAC9B,QAAM,UAAuB,CAAC;AAE9B,aAAW,QAAQ,KAAK,eAAe;AACrC,QAAI,KAAK,SAAS,oBAAoB;AACpC,YAAM,MAAM,cAAc,IAAI;AAC9B,UAAI,IAAK,SAAQ,KAAK,GAAG;AACzB;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,oBAAoB;AAEpC,YAAM,UAAU,KAAK,SAAS;AAAA,QAC5B,CAAC,MAAsB,EAAE,SAAS;AAAA,MACpC;AACA,UAAI,SAAS;AAEX,YAAI,aAAoC;AACxC,mBAAW,SAAS,KAAK,UAAU;AACjC,cAAI,MAAM,SAAS,UAAU;AAC3B,yBAAa;AACb;AAAA,UACF;AAAA,QACF;AACA,YAAI,YAAY;AACd,gBAAM,YAAY,WAAW;AAC7B,gBAAMC,UAAS,UAAU,QAAQ,gBAAgB,EAAE;AACnD,gBAAM,aAAa,0BAA0B,IAAI;AACjD,gBAAM,aAAa,KAAK,SAAS;AAAA,YAC/B,CAAC,MAAsB,EAAE,SAAS,UAAU,EAAE,SAAS;AAAA,UACzD;AACA,kBAAQ,KAAK,EAAE,YAAY,QAAAA,SAAQ,WAAW,CAAC;AAAA,QACjD;AACA;AAAA,MACF;AAGA,iBAAW,SAAS,KAAK,eAAe;AACtC,cAAMC,QAAO,mBAAmB,OAAO,QAAQ,IAAI;AACnD,YAAIA,OAAM;AACR,kBAAQ,KAAKA,KAAI;AACjB,cAAI,MAAM,SAAS,qBAAqB;AACtC,oBAAQ,KAAK,GAAG,eAAe,OAAO,QAAQ,IAAI,CAAC;AAAA,UACrD;AAAA,QACF;AAEA,YACE,MAAM,SAAS,yBACf,MAAM,SAAS,wBACf;AACA,kBAAQ,KAAK,GAAG,4BAA4B,OAAO,IAAI,CAAC;AAAA,QAC1D;AAAA,MACF;AACA;AAAA,IACF;AAGA,UAAM,OAAO,mBAAmB,MAAM,QAAQ,KAAK;AACnD,QAAI,MAAM;AACR,cAAQ,KAAK,IAAI;AACjB,UAAI,KAAK,SAAS,qBAAqB;AACrC,gBAAQ,KAAK,GAAG,eAAe,MAAM,QAAQ,KAAK,CAAC;AAAA,MACrD;AAAA,IACF;AAEA,QACE,KAAK,SAAS,yBACd,KAAK,SAAS,wBACd;AACA,cAAQ,KAAK,GAAG,4BAA4B,MAAM,KAAK,CAAC;AAAA,IAC1D;AAAA,EACF;AAGA,UAAQ,KAAK,GAAG,sBAAsB,IAAI,CAAC;AAE3C,SAAO,EAAE,SAAS,QAAQ;AAC5B;;;ADvcA,IAAMC,WAAUC,eAAc,YAAY,GAAG;AAC7C,IAAM,KAAKD,SAAQ,WAAW;AAE9B,IAAM,gBAAgB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,MAAM,CAAC;AAC5D,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWA,SAAS,YAAY,SAAyB;AAC5C,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACvE;AAEA,SAAS,kBACP,cACA,UACAE,UACA,UACe;AACf,MAAI,CAAC,aAAa,WAAW,GAAG,EAAG,QAAO;AAE1C,QAAM,UAAU,QAAQ,QAAQ;AAChC,QAAM,WAAW,QAAQA,UAAS,SAAS,YAAY;AAGvD,QAAM,MAAM,QAAQ,QAAQ;AAC5B,QAAM,eACJ,QAAQ,SAAS,QAAQ,SAAS,SAAS,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI;AAErE,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,GAAG,MAAM,KAAK,aAAa,EAAE,IAAI,CAAC,MAAM,WAAW,CAAC;AAAA,IACpD,GAAG,MAAM,KAAK,aAAa,EAAE,IAAI,CAAC,MAAM,QAAQ,UAAU,UAAU,CAAC,CAAC;AAAA,EACxE;AAGA,MAAI,cAAc;AAChB,eAAW;AAAA,MACT,GAAG,MAAM,KAAK,aAAa,EAAE,IAAI,CAAC,MAAM,eAAe,CAAC;AAAA,MACxD,GAAG,MAAM,KAAK,aAAa,EAAE;AAAA,QAAI,CAAC,MAChC,QAAQ,cAAc,UAAU,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,aAAW,aAAa,YAAY;AAClC,UAAM,MAAM,SAASA,UAAS,SAAS;AACvC,QAAI,SAAS,IAAI,GAAG,EAAG,QAAO;AAAA,EAChC;AAEA,SAAO;AACT;AAEA,eAAsB,aACpBA,UACA,QACqB;AACrB,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,UAAU,QAAQA,QAAO;AAE/B,QAAM,QAAkB,MAAM,GAAG,wBAAwB;AAAA,IACvD,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,WAAW,IAAI,IAAI,KAAK;AAC9B,QAAMC,MAAK,IAAI,cAAc,MAAM;AAEnC,MAAI,eAAe;AACnB,MAAI,eAAe;AACnB,MAAI,mBAAmB;AACvB,MAAI,eAAe;AACnB,MAAI,oBAAoB;AAGxB,QAAM,cAAc,oBAAI,IAAoB;AAE5C,QAAM,cAAc,oBAAI,IAAyB;AAEjD,QAAM,eAAe,oBAAI,IAAoB;AAE7C,EAAAA,IAAG,YAAY,MAAM;AAEnB,eAAW,YAAY,OAAO;AAC5B,YAAM,UAAU,QAAQ,SAAS,QAAQ;AACzC,UAAI;AACJ,UAAI;AACF,kBAAU,aAAa,SAAS,OAAO;AAAA,MACzC,QAAQ;AACN;AACA;AAAA,MACF;AAEA,YAAM,OAAO,YAAY,OAAO;AAChC,YAAM,eAAeA,IAAG,YAAY,QAAQ;AAG5C,YAAM,SAAS,YAAY,OAAO;AAClC,UAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,oBAAY,IAAI,UAAU,OAAO,OAAO;AACxC,qBAAa,IAAI,UAAU,OAAO;AAAA,MACpC;AAEA,UAAI,iBAAiB,MAAM;AACzB;AAEA,mBAAW,OAAOA,IAAG,iBAAiB,QAAQ,GAAG;AAC/C,sBAAY,IAAI,GAAG,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,EAAG;AAAA,QACpD;AACA;AAAA,MACF;AAGA,MAAAA,IAAG,UAAU,QAAQ;AAErB,YAAM,MAAM,QAAQ,QAAQ;AAC5B,YAAM,WACJ,QAAQ,SAAS,QAAQ,SAAS,eAAe;AAEnD,MAAAA,IAAG,WAAW;AAAA,QACZ,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,cAAc,KAAK,IAAI;AAAA,MACzB,CAAC;AAGD,MAAAA,IAAG,kBAAkB,UAAU,OAAO;AAEtC,iBAAW,OAAO,OAAO,SAAS;AAChC,cAAM,KAAKA,IAAG,aAAa;AAAA,UACzB,WAAW;AAAA,UACX,MAAM,IAAI;AAAA,UACV,MAAM,IAAI;AAAA,UACV,YAAY,IAAI;AAAA,UAChB,UAAU,IAAI;AAAA,UACd,YAAY,IAAI;AAAA,UAChB,UAAU,IAAI;AAAA,UACd,cAAc,IAAI;AAAA,UAClB,YAAY,IAAI;AAAA,UAChB,WAAW,IAAI;AAAA,QACjB,CAAC;AACD,oBAAY,IAAI,GAAG,QAAQ,IAAI,IAAI,IAAI,IAAI,EAAE;AAC7C;AAAA,MACF;AAEA;AAAA,IACF;AAGA,IAAAA,IAAG,gBAAgB;AACnB,IAAAA,IAAG,mBAAmB;AAEtB,eAAW,CAAC,UAAU,OAAO,KAAK,aAAa;AAE7C,YAAM,iBAAiBA,IAAG,iBAAiB,QAAQ;AACnD,YAAM,gBAAgB,eAAe,IAAI,CAAC,SAAS;AAAA,QACjD,MAAM,IAAI;AAAA,QACV,IAAI,IAAI;AAAA,QACR,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,MACf,EAAE;AAGF,YAAM,mBAAmB,oBAAI,IAAY;AACzC,iBAAW,OAAO,SAAS;AACzB,mBAAW,YAAY,IAAI,YAAY;AACrC,gBAAM,YAAY,SAAS,WAAW,OAAO,IACzC,SAAS,MAAM,CAAC,IAChB;AACJ,2BAAiB,IAAI,SAAS;AAAA,QAChC;AAAA,MACF;AAGA,YAAM,UAAU,aAAa,IAAI,QAAQ;AACzC,YAAM,SAAS,UACX,qBAAqB,SAAS,gBAAgB,IAC9C,CAAC;AAEL,iBAAW,OAAO,SAAS;AACzB,cAAM,aAAa;AAAA,UACjB,IAAI;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAGA,mBAAW,YAAY,IAAI,YAAY;AACrC,gBAAM,YAAY,SAAS,WAAW,OAAO,IACzC,SAAS,MAAM,CAAC,IAChB;AACJ,UAAAA,IAAG,aAAa;AAAA,YACd,WAAW;AAAA,YACX,WAAW;AAAA,YACX,aAAa;AAAA,YACb,YAAY,IAAI;AAAA,YAChB,cAAc,IAAI,aAAa,IAAI;AAAA,UACrC,CAAC;AACD;AAAA,QACF;AAEA,YAAI,CAAC,WAAY;AAEjB,cAAM,UAAU,IAAI,aAAa,aAAa;AAE9C,mBAAW,YAAY,IAAI,YAAY;AACrC,gBAAM,YAAY,SAAS,WAAW,OAAO,IACzC,SAAS,MAAM,CAAC,IAChB;AAEJ,gBAAM,YAAY,GAAG,UAAU,IAAI,SAAS;AAC5C,gBAAM,WAAW,YAAY,IAAI,SAAS;AAC1C,cAAI,CAAC,SAAU;AAGf,gBAAM,aAAa,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAE5D,cAAI,WAAW,SAAS,GAAG;AAEzB,kBAAM,uBAAuB,oBAAI,IAAY;AAE7C,uBAAW,SAAS,YAAY;AAC9B,yBAAW,UAAU,eAAe;AAClC,oBACE,OAAO,OAAO,YACd,MAAM,QAAQ,OAAO,aACrB,MAAM,QAAQ,OAAO,SACrB;AACA,uCAAqB,IAAI,OAAO,EAAE;AAAA,gBACpC;AAAA,cACF;AAAA,YACF;AAEA,uBAAW,YAAY,sBAAsB;AAC3C,cAAAA,IAAG,gBAAgB;AAAA,gBACjB,kBAAkB;AAAA,gBAClB,kBAAkB;AAAA,gBAClB,UAAU;AAAA,cACZ,CAAC;AACD;AAAA,YACF;AAAA,UACF,OAAO;AAIL,uBAAW,UAAU,eAAe;AAClC,kBAAI,OAAO,OAAO,UAAU;AAC1B,gBAAAA,IAAG,gBAAgB;AAAA,kBACjB,kBAAkB,OAAO;AAAA,kBACzB,kBAAkB;AAAA,kBAClB,UAAU;AAAA,gBACZ,CAAC;AACD;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,EAAAA,IAAG,MAAM;AAET,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,KAAK,IAAI,IAAI;AAAA,EACvB;AACF;;;AD7RA,IAAMC,WAAUC,eAAc,YAAY,GAAG;AAC7C,IAAM,MAAMD,SAAQ,iBAAiB;AAMrC,IAAI,KAA2B;AAC/B,IAAI,UAAkB;AAEtB,SAAS,MAAM,MAA6B;AAC1C,MAAI,MAAM,YAAY,KAAM,QAAO;AACnC,QAAM,SAASE,SAAQ,MAAM,cAAc,UAAU;AACrD,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,qBAAqB,MAAM;AAAA,8BAAiC,IAAI;AAAA,IAClE;AAAA,EACF;AACA,MAAI,GAAI,IAAG,MAAM;AACjB,OAAK,IAAI,cAAc,MAAM;AAC7B,YAAU;AACV,SAAO;AACT;AAIO,SAAS,gBAAgB,aAAiC;AAC/D,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS,IAAI;AAAA,EACf,CAAC;AAED,QAAM,gBAAgB,eAAe,YAAY,MAAS;AAI1D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IAEA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,sCAAsC;AAAA,MAChE,QAAQ,EACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,GAAI,EACZ,SAAS,yCAAyC;AAAA,MACrD,OAAO,EACJ,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,QAAQ,CAAC,EACT,SAAS,wCAAwC;AAAA,MACpD,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mCAAmC;AAAA,IACjD;AAAA,IACA,OAAO,EAAE,MAAM,QAAQ,OAAO,KAAK,MAAM;AACvC,YAAM,IAAI,QAAQ;AAElB,YAAM,SAAS,MAAM,mBAAmB,GAAG,MAAM,QAAQ,KAAK;AAC9D,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,CAAC,EAAE;AAAA,IAC9D;AAAA,EACF;AAIA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IAEA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,MACvD,MAAM,EACH,KAAK;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC,EACA,QAAQ,KAAK,EACb,SAAS,sCAAsC;AAAA,MAClD,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mCAAmC;AAAA,IACjD;AAAA,IACA,OAAO,EAAE,QAAQ,MAAM,KAAK,MAAM;AAChC,YAAM,IAAI,QAAQ;AAClB,YAAM,WAAW,MAAM,CAAC;AACxB,YAAM,UAAU,SAAS,YAAY,QAAQ,IAAI;AAEjD,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,8BAA8B,MAAM,IAAI,SAAS,QAAQ,WAAW,IAAI,MAAM,EAAE;AAAA,YACxF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,QAAkB;AAAA,QACtB,SAAS,QAAQ,MAAM,UAAU,QAAQ,WAAW,IAAI,MAAM,EAAE,cAAc,MAAM,IAAI,SAAS,QAAQ,WAAW,IAAI,MAAM,EAAE;AAAA,QAChI;AAAA,MACF;AAEA,iBAAW,OAAO,SAAS;AACzB,cAAM,MAAM,IAAI,YAAY,KAAK,IAAI,SAAS,KAAK;AACnD,cAAM;AAAA,UACJ,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,IAAI,IAAI,UAAU,GAAG,GAAG;AAAA,QACtE;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,MAAM,KAAK,IAAI,EAAE,CAAC,EAAE;AAAA,IACxE;AAAA,EACF;AAIA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IAEA;AAAA,MACE,QAAQ,EACL,OAAO,EACP,SAAS,4CAA4C;AAAA,MACxD,WAAW,EACR,KAAK,CAAC,QAAQ,YAAY,UAAU,CAAC,EACrC,QAAQ,MAAM,EACd,SAAS,iCAAiC;AAAA,MAC7C,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mCAAmC;AAAA,IACjD;AAAA,IACA,OAAO,EAAE,QAAQ,WAAW,KAAK,MAAM;AACrC,YAAM,IAAI,QAAQ;AAClB,YAAM,SAAS,mBAAmB,GAAG,QAAQ,SAAS;AACtD,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,CAAC,EAAE;AAAA,IAC9D;AAAA,EACF;AAIA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IAEA;AAAA,MACE,QAAQ,EACL,OAAO,EACP,IAAI,EACJ,SAAS,EACT,QAAQ,GAAI,EACZ,SAAS,yCAAyC;AAAA,MACrD,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,mCAAmC;AAAA,IACjD;AAAA,IACA,OAAO,EAAE,QAAQ,KAAK,MAAM;AAC1B,YAAM,IAAI,QAAQ;AAClB,YAAM,SAAS,iBAAiB,GAAG,MAAM;AACzC,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,CAAC,EAAE;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAe,mBACb,MACA,MACA,QACA,OACiB;AACjB,QAAM,EAAE,aAAa,IAAI,MAAM,OAAO,qBAAqB;AAC3D,SAAO,aAAa,MAAM,MAAM,QAAQ,KAAK;AAC/C;AAEA,SAAS,mBACP,MACA,QACA,WACQ;AACR,QAAM,WAAW,MAAM,IAAI;AAE3B,QAAM,SACJ,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM;AAEvE,MAAI,QAAQ;AACV,WAAO,wBAAwB,UAAU,QAAQ,SAAS;AAAA,EAC5D,OAAO;AACL,WAAO,0BAA0B,UAAU,QAAQ,SAAS;AAAA,EAC9D;AACF;AAEA,SAAS,wBACP,UACA,QACA,WACQ;AACR,QAAMF,WAAUC,eAAc,YAAY,GAAG;AAC7C,QAAM,EAAE,cAAc,IAAID,SAAQ,YAAY;AAE9C,QAAM,QAAQ,IAAI,cAAc;AAChC,aAAW,QAAQ,SAAS,YAAY,GAAG;AACzC,UAAM,QAAQ,KAAK,IAAI;AAAA,EACzB;AACA,aAAW,QAAQ,SAAS,mBAAmB,GAAG;AAChD,UAAM,MAAM,GAAG,KAAK,WAAW,KAAK,KAAK,WAAW;AACpD,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,YAAM,WAAW,MAAM,kBAAkB,GAAG;AAC5C,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,GAAG,SAAS,UAAU,KAAK,KAAK,UAAU;AAAA,MAC5C;AAAA,IACF,OAAO;AACL,YAAM,eAAe,KAAK,KAAK,aAAa,KAAK,aAAa;AAAA,QAC5D,YAAY,KAAK;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,WAAO,gBAAgB,MAAM;AAAA,EAC/B;AAEA,QAAM,WAAqB,CAAC;AAE5B,MAAI,cAAc,cAAc,cAAc,QAAQ;AACpD,UAAM,WAAqB,CAAC;AAC5B,UAAM;AAAA,MACJ;AAAA,MACA,CACE,OACA,OACA,MACA,QACG;AACH,iBAAS,KAAK,KAAK,GAAG,eAAe,MAAM,UAAU,GAAG;AAAA,MAC1D;AAAA,IACF;AACA,aAAS;AAAA,MACP,SAAS,SAAS,IACd;AAAA,EAA6B,SAAS,KAAK,IAAI,CAAC,KAChD;AAAA,IACN;AAAA,EACF;AAEA,MAAI,cAAc,cAAc,cAAc,QAAQ;AACpD,UAAM,UAAoB,CAAC;AAC3B,UAAM;AAAA,MACJ;AAAA,MACA,CACE,OACA,OACA,QACG;AACH,gBAAQ,KAAK,KAAK,GAAG,eAAe,MAAM,UAAU,GAAG;AAAA,MACzD;AAAA,IACF;AACA,aAAS;AAAA,MACP,QAAQ,SAAS,IACb;AAAA,EAA2B,QAAQ,KAAK,IAAI,CAAC,KAC7C;AAAA,IACN;AAAA,EACF;AAEA,SAAO,GAAG,MAAM;AAAA;AAAA,EAAO,SAAS,KAAK,MAAM,CAAC;AAC9C;AAEA,SAAS,0BACP,UACA,QACA,WACQ;AACR,QAAM,UAAU,SAAS,kBAAkB,MAAM;AACjD,QAAM,aAAa,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AACxD,QAAM,MAAM,cAAc,QAAQ,CAAC;AAEnC,MAAI,CAAC,OAAO,CAAC,IAAI,IAAI;AACnB,WAAO,kBAAkB,MAAM;AAAA,EACjC;AAEA,QAAM,UAAU,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,YAAO,IAAI,SAAS,IAAI,IAAI,UAAU;AAC9E,QAAM,WAAqB,CAAC;AAE5B,MAAI,cAAc,cAAc,cAAc,QAAQ;AACpD,UAAM,OAAO,SAAS,sBAAsB,IAAI,EAAE;AAClD,UAAM,QAAQ,KAAK;AAAA,MACjB,CAAC,MACC,KAAK,EAAE,WAAW,KAAK,EAAE,WAAW,YAAO,EAAE,WAAW,IAAI,EAAE,WAAW,MAAM,EAAE,QAAQ;AAAA,IAC7F;AACA,aAAS;AAAA,MACP,MAAM,SAAS,IACX;AAAA,EAA6B,MAAM,KAAK,IAAI,CAAC,KAC7C;AAAA,IACN;AAAA,EACF;AAEA,MAAI,cAAc,cAAc,cAAc,QAAQ;AACpD,UAAM,OAAO,SAAS,sBAAsB,IAAI,EAAE;AAClD,UAAM,QAAQ,KAAK;AAAA,MACjB,CAAC,MACC,KAAK,EAAE,WAAW,KAAK,EAAE,WAAW,YAAO,EAAE,WAAW,IAAI,EAAE,WAAW,MAAM,EAAE,QAAQ;AAAA,IAC7F;AACA,aAAS;AAAA,MACP,MAAM,SAAS,IACX;AAAA,EAA2B,MAAM,KAAK,IAAI,CAAC,KAC3C;AAAA,IACN;AAAA,EACF;AAEA,SAAO,GAAG,OAAO;AAAA;AAAA,EAAO,SAAS,KAAK,MAAM,CAAC;AAC/C;AAEA,SAAS,iBAAiB,MAAc,QAAwB;AAC9D,QAAMA,WAAUC,eAAc,YAAY,GAAG;AAC7C,QAAM,EAAE,cAAc,IAAID,SAAQ,YAAY;AAC9C,QAAM,WAAWA,SAAQ,wCAAwC;AACjE,QAAM,EAAE,YAAY,IAAIA,SAAQ,aAAa;AAE7C,QAAM,WAAW,MAAM,IAAI;AAC3B,QAAM,QAAQ,SAAS,YAAY;AAEnC,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,IAAI,cAAc;AAChC,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,IAAI;AAAA,EACzB;AACA,aAAW,QAAQ,SAAS,mBAAmB,GAAG;AAChD,UAAM,MAAM,GAAG,KAAK,WAAW,KAAK,KAAK,WAAW;AACpD,QAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,YAAM,eAAe,KAAK,KAAK,aAAa,KAAK,WAAW;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,QACJ,MAAM,QAAQ,IAAI,SAAS,OAAO,EAAE,eAAe,KAAK,CAAC,IAAI,CAAC;AAEhE,QAAM,cAAc,MACjB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,MAAM,EAAE,IAAI,KAAK,EAAE,EAAE,EACvD,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAEjE,QAAM,MAAM,YAAY,aAAa;AACrC,QAAM,SAAmB,CAAC;AAC1B,MAAI,aAAa;AACjB,MAAI,YAAY;AAEhB,aAAW,QAAQ,aAAa;AAC9B,UAAM,UAAU,SAAS,iBAAiB,KAAK,IAAI;AACnD,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAClD,UAAM,QAAkB,CAAC,KAAK,IAAI;AAClC,eAAW,OAAO,SAAS;AACzB,UAAI,IAAI,SAAS,cAAc,CAAC,IAAI,UAAW;AAC/C,YAAM,QAAQ,IAAI,aAAa,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AACtD,YAAM,KAAK,KAAK,KAAK,EAAE;AAAA,IACzB;AACA,UAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,UAAM,cAAc,IAAI,OAAO,KAAK,EAAE;AAEtC,QAAI,aAAa,eAAe,QAAQ;AACtC,aAAO,KAAK,KAAK;AACjB,oBAAc;AACd;AAAA,IACF;AAEA,UAAM,aAAa,MAAM,MAAM,IAAI;AACnC,QAAI,UAAU,WAAW,CAAC;AAC1B,QAAI,gBAAgB,IAAI,OAAO,OAAO,EAAE;AAExC,QAAI,aAAa,gBAAgB,QAAQ;AACvC,kBAAY;AACZ;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,YAAM,YAAY,UAAU,OAAO,WAAW,CAAC;AAC/C,YAAM,kBAAkB,IAAI,OAAO,SAAS,EAAE;AAC9C,UAAI,aAAa,kBAAkB,OAAQ;AAC3C,gBAAU;AACV,sBAAgB;AAAA,IAClB;AAEA,WAAO,KAAK,OAAO;AACnB,kBAAc;AACd,gBAAY;AACZ;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO;AACrB,QAAM,QAAQ,YAAY;AAC1B,QAAM,QAAQ,CAAC,SAAS,KAAK,IAAI,KAAK,YAAY,UAAU,SAAS;AACrE,MAAI,WAAW;AACb,UAAM,KAAK,YAAY,MAAM,cAAc;AAAA,EAC7C;AAEA,SAAO,OAAO,KAAK,MAAM,IAAI;AAAA;AAAA,EAAO,MAAM,KAAK,GAAG,CAAC;AACrD;AAIA,eAAsB,SACpB,aACAG,QACe;AACf,MAAIA,OAAM,MAAM;AACd,YAAQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA0Bd;AACA;AAAA,EACF;AAEA,QAAM,OAAO,YAAYA,OAAM,IAAI;AACnC,QAAM,SAASD,SAAQ,MAAM,cAAc,UAAU;AAErD,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,QAAI,CAAC,WAAW,IAAI,GAAG;AACrB,cAAQ;AAAA,QACN,yCAAyC,IAAI;AAAA,MAC/C;AACA,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,YAAQ,MAAM,mDAA8C,IAAI,MAAM;AACtE,UAAM,QAAQ,MAAM,aAAa,MAAM,MAAM;AAC7C,YAAQ;AAAA,MACN,+BAA+B,MAAM,YAAY,WAAW,MAAM,gBAAgB,aAAa,MAAM,MAAM;AAAA,IAC7G;AAAA,EACF;AAGA,QAAM,IAAI;AACV,UAAQ,MAAM,0CAA0C,IAAI,GAAG;AAC/D,UAAQ,MAAM,yBAAyB,MAAM,EAAE;AAE/C,QAAM,SAAS,gBAAgB,IAAI;AACnC,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,UAAQ,MAAM,iEAAiE;AACjF;;;AGreA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,QAA0C,CAAC;AAEjD,SAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAM,MAAM,KAAK,CAAC;AAClB,MAAI,IAAI,WAAW,IAAI,GAAG;AACxB,UAAM,MAAM,IAAI,MAAM,CAAC;AACvB,UAAM,OAAO,KAAK,IAAI,CAAC;AACvB,QAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAClC,YAAM,GAAG,IAAI;AACb;AAAA,IACF,OAAO;AACL,YAAM,GAAG,IAAI;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,CAAC,GAAG,KAAK,EAAE,MAAM,CAAC,QAAQ;AACjC,UAAQ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC1E,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["createRequire","resolve","createRequire","require","source","decl","require","createRequire","rootDir","db","require","createRequire","resolve","flags"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngommans/codefocus",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Smart code context aggregator — AST-powered search that returns structured, ranked code context for LLM agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { resolve } from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport { IndexDatabase } from \"./db.js\";\nimport { resolveRoot } from \"./root.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\");\n\n// ── shared state ────────────────────────────────────────────────────────\n// The MCP server keeps the database connection and tiktoken encoder warm\n// so that subsequent tool calls pay ~10ms instead of ~400ms cold start.\n\nlet db: IndexDatabase | null = null;\nlet rootDir: string = \"\";\n\nfunction getDb(root: string): IndexDatabase {\n if (db && rootDir === root) return db;\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n if (!existsSync(dbPath)) {\n throw new Error(\n `no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n }\n if (db) db.close();\n db = new IndexDatabase(dbPath);\n rootDir = root;\n return db;\n}\n\n// ── MCP server setup ────────────────────────────────────────────────────\n\nexport function createMcpServer(defaultRoot?: string): McpServer {\n const server = new McpServer({\n name: \"codefocus\",\n version: pkg.version,\n });\n\n const effectiveRoot = defaultRoot ?? resolveRoot(undefined);\n\n // ── tool: query ─────────────────────────────────────────────────────\n\n server.tool(\n \"query\",\n \"Search the codebase and return ranked, budget-constrained code context. \" +\n \"Returns YAML front matter with confidence metadata followed by relevant source code sections.\",\n {\n term: z.string().describe(\"Search term (symbol name or keyword)\"),\n budget: z\n .number()\n .int()\n .positive()\n .default(8000)\n .describe(\"Token budget for output (default: 8000)\"),\n depth: z\n .number()\n .int()\n .min(0)\n .default(2)\n .describe(\"Max graph traversal depth (default: 2)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ term, budget, depth, root }) => {\n const r = root ?? effectiveRoot;\n // Import runQuery's internal logic so we can capture output\n const output = await captureQueryOutput(r, term, budget, depth);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: find ──────────────────────────────────────────────────────\n\n server.tool(\n \"find\",\n \"Quick symbol lookup by name with optional kind filter. \" +\n \"Returns symbol names, kinds, file paths, and line numbers.\",\n {\n symbol: z.string().describe(\"Symbol name to search for\"),\n kind: z\n .enum([\n \"function\",\n \"class\",\n \"interface\",\n \"type\",\n \"enum\",\n \"variable\",\n \"method\",\n \"all\",\n ])\n .default(\"all\")\n .describe(\"Filter by symbol kind (default: all)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ symbol, kind, root }) => {\n const r = root ?? effectiveRoot;\n const database = getDb(r);\n const results = database.findSymbols(symbol, kind);\n\n if (results.length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `No symbols found matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}`,\n },\n ],\n };\n }\n\n const lines: string[] = [\n `Found ${results.length} symbol${results.length !== 1 ? \"s\" : \"\"} matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}:`,\n \"\",\n ];\n\n for (const sym of results) {\n const sig = sym.signature ? ` ${sym.signature}` : \"\";\n lines.push(\n ` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`,\n );\n }\n\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n },\n );\n\n // ── tool: graph ─────────────────────────────────────────────────────\n\n server.tool(\n \"graph\",\n \"Show the dependency graph for a file or symbol. \" +\n \"Displays incoming and/or outgoing edges as an indented tree.\",\n {\n target: z\n .string()\n .describe(\"File path or symbol name to show graph for\"),\n direction: z\n .enum([\"both\", \"incoming\", \"outgoing\"])\n .default(\"both\")\n .describe(\"Graph direction (default: both)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ target, direction, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureGraphOutput(r, target, direction);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n // ── tool: map ───────────────────────────────────────────────────────\n\n server.tool(\n \"map\",\n \"High-level codebase overview ranked by PageRank connectivity. \" +\n \"Returns file paths with their top symbols and signatures.\",\n {\n budget: z\n .number()\n .int()\n .positive()\n .default(2000)\n .describe(\"Token budget for output (default: 2000)\"),\n root: z\n .string()\n .optional()\n .describe(\"Root directory of indexed project\"),\n },\n async ({ budget, root }) => {\n const r = root ?? effectiveRoot;\n const output = captureMapOutput(r, budget);\n return { content: [{ type: \"text\" as const, text: output }] };\n },\n );\n\n return server;\n}\n\n// ── output capture helpers ──────────────────────────────────────────────\n// These re-implement the command logic to return strings instead of\n// writing to stdout, keeping the database connection warm.\n\nasync function captureQueryOutput(\n root: string,\n term: string,\n budget: number,\n depth: number,\n): Promise<string> {\n const { runQueryCore } = await import(\"./commands/query.js\");\n return runQueryCore(root, term, budget, depth);\n}\n\nfunction captureGraphOutput(\n root: string,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const database = getDb(root);\n\n const isFile =\n target.includes(\"/\") || target.includes(\"\\\\\") || /\\.\\w+$/.test(target);\n\n if (isFile) {\n return renderFileGraphToString(database, target, direction);\n } else {\n return renderSymbolGraphToString(database, target, direction);\n }\n}\n\nfunction renderFileGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n\n const graph = new DirectedGraph();\n for (const file of database.getAllFiles()) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (graph.hasEdge(key)) {\n const existing = graph.getEdgeAttributes(key);\n graph.setEdgeAttribute(\n key,\n \"specifiers\",\n `${existing.specifiers}, ${edge.specifiers}`,\n );\n } else {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file, {\n specifiers: edge.specifiers,\n });\n }\n }\n\n if (!graph.hasNode(target)) {\n return `Error: file \"${target}\" not found in the index`;\n }\n\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const outEdges: string[] = [];\n graph.forEachOutEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n _src: string,\n tgt: string,\n ) => {\n outEdges.push(` ${tgt} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n outEdges.length > 0\n ? `Dependencies (outgoing):\\n${outEdges.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const inEdges: string[] = [];\n graph.forEachInEdge(\n target,\n (\n _edge: string,\n attrs: { specifiers: string },\n src: string,\n ) => {\n inEdges.push(` ${src} (imports: ${attrs.specifiers})`);\n },\n );\n sections.push(\n inEdges.length > 0\n ? `Dependents (incoming):\\n${inEdges.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${target}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction renderSymbolGraphToString(\n database: IndexDatabase,\n target: string,\n direction: \"both\" | \"incoming\" | \"outgoing\",\n): string {\n const symbols = database.findSymbolsByName(target);\n const exactMatch = symbols.find((s) => s.name === target);\n const sym = exactMatch ?? symbols[0];\n\n if (!sym || !sym.id) {\n return `Error: symbol \"${target}\" not found in the index`;\n }\n\n const heading = `${sym.name} (${sym.kind}) — ${sym.file_path}:${sym.start_line}`;\n const sections: string[] = [];\n\n if (direction === \"outgoing\" || direction === \"both\") {\n const refs = database.getOutgoingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.target_name} (${r.target_kind}) — ${r.target_file}:${r.target_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependencies (outgoing):\\n${lines.join(\"\\n\")}`\n : \"Dependencies (outgoing): (none)\",\n );\n }\n\n if (direction === \"incoming\" || direction === \"both\") {\n const refs = database.getIncomingReferences(sym.id);\n const lines = refs.map(\n (r) =>\n ` ${r.source_name} (${r.source_kind}) — ${r.source_file}:${r.source_line} [${r.ref_type}]`,\n );\n sections.push(\n lines.length > 0\n ? `Dependents (incoming):\\n${lines.join(\"\\n\")}`\n : \"Dependents (incoming): (none)\",\n );\n }\n\n return `${heading}\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n\nfunction captureMapOutput(root: string, budget: number): string {\n const require = createRequire(import.meta.url);\n const { DirectedGraph } = require(\"graphology\");\n const pagerank = require(\"graphology-metrics/centrality/pagerank\");\n const { getEncoding } = require(\"js-tiktoken\");\n\n const database = getDb(root);\n const files = database.getAllFiles();\n\n if (files.length === 0) {\n return \"[map] Index is empty — no files to map.\";\n }\n\n // Compute PageRank\n const graph = new DirectedGraph();\n for (const file of files) {\n graph.addNode(file.path);\n }\n for (const edge of database.getFileImportEdges()) {\n const key = `${edge.source_file}->${edge.target_file}`;\n if (!graph.hasEdge(key)) {\n graph.addEdgeWithKey(key, edge.source_file, edge.target_file);\n }\n }\n const ranks: Record<string, number> =\n graph.order > 0 ? pagerank(graph, { getEdgeWeight: null }) : {};\n\n const rankedFiles = files\n .map((f) => ({ path: f.path, rank: ranks[f.path] ?? 0 }))\n .sort((a, b) => b.rank - a.rank || a.path.localeCompare(b.path));\n\n const enc = getEncoding(\"cl100k_base\");\n const blocks: string[] = [];\n let tokenCount = 0;\n let truncated = false;\n\n for (const file of rankedFiles) {\n const symbols = database.getSymbolsByFile(file.path);\n symbols.sort((a, b) => a.start_line - b.start_line);\n const lines: string[] = [file.path];\n for (const sym of symbols) {\n if (sym.kind === \"variable\" && !sym.signature) continue;\n const label = sym.signature ?? `${sym.kind} ${sym.name}`;\n lines.push(` ${label}`);\n }\n const block = lines.join(\"\\n\");\n const blockTokens = enc.encode(block).length;\n\n if (tokenCount + blockTokens <= budget) {\n blocks.push(block);\n tokenCount += blockTokens;\n continue;\n }\n\n const blockLines = block.split(\"\\n\");\n let partial = blockLines[0];\n let partialTokens = enc.encode(partial).length;\n\n if (tokenCount + partialTokens > budget) {\n truncated = true;\n break;\n }\n\n for (let i = 1; i < blockLines.length; i++) {\n const candidate = partial + \"\\n\" + blockLines[i];\n const candidateTokens = enc.encode(candidate).length;\n if (tokenCount + candidateTokens > budget) break;\n partial = candidate;\n partialTokens = candidateTokens;\n }\n\n blocks.push(partial);\n tokenCount += partialTokens;\n truncated = true;\n break;\n }\n\n const shown = blocks.length;\n const total = rankedFiles.length;\n const parts = [`[map] ${shown}/${total} files, ~${tokenCount} tokens`];\n if (truncated) {\n parts.push(`(budget: ${budget}, truncated)`);\n }\n\n return blocks.join(\"\\n\\n\") + `\\n\\n${parts.join(\" \")}`;\n}\n\n// ── serve command entry point ───────────────────────────────────────────\n\nexport async function runServe(\n _positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.error(`codefocus serve — Start MCP server (stdio transport)\n\nUsage: codefocus serve [options]\n\nOptions:\n --root <path> Root directory of indexed project (default: auto-detect)\n --help Show this help message\n\nThe MCP server exposes four tools:\n query Search and return ranked code context\n find Quick symbol lookup\n graph Show dependency graph for a file or symbol\n map High-level codebase overview\n\nThe server keeps the database connection and tiktoken encoder warm,\nso subsequent tool calls complete in ~10ms instead of ~400ms.\n\nUsage with Claude Code:\n Add to .mcp.json:\n {\n \"mcpServers\": {\n \"codefocus\": {\n \"command\": \"npx\",\n \"args\": [\"codefocus\", \"serve\", \"--root\", \"/path/to/project\"]\n }\n }\n }`);\n return;\n }\n\n const root = resolveRoot(flags.root);\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n\n if (!existsSync(dbPath)) {\n console.error(\n `Error: no index found at ${dbPath}\\nRun 'codefocus index --root ${root}' first.`,\n );\n process.exitCode = 1;\n return;\n }\n\n // Pre-warm the database connection\n getDb(root);\n console.error(`[codefocus] MCP server starting (root: ${root})`);\n console.error(`[codefocus] Database: ${dbPath}`);\n\n const server = createMcpServer(root);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error(`[codefocus] MCP server ready (4 tools: query, find, graph, map)`);\n}\n"],"mappings":"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","names":["require"]}