@ngommans/codefocus 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (40) hide show
  1. package/README.md +124 -0
  2. package/dist/benchmark-43DOYNYR.js +465 -0
  3. package/dist/benchmark-43DOYNYR.js.map +1 -0
  4. package/dist/chunk-6XH2ZLP6.js +127 -0
  5. package/dist/chunk-6XH2ZLP6.js.map +1 -0
  6. package/dist/chunk-7RYHZOYF.js +27 -0
  7. package/dist/chunk-7RYHZOYF.js.map +1 -0
  8. package/dist/chunk-ITVAEU6K.js +250 -0
  9. package/dist/chunk-ITVAEU6K.js.map +1 -0
  10. package/dist/chunk-Q6DOBQ4F.js +231 -0
  11. package/dist/chunk-Q6DOBQ4F.js.map +1 -0
  12. package/dist/chunk-X7DRJUEX.js +543 -0
  13. package/dist/chunk-X7DRJUEX.js.map +1 -0
  14. package/dist/cli.js +111 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands-ICBN54MT.js +64 -0
  17. package/dist/commands-ICBN54MT.js.map +1 -0
  18. package/dist/config-OCBWYENF.js +12 -0
  19. package/dist/config-OCBWYENF.js.map +1 -0
  20. package/dist/extended-benchmark-5RUXDG3D.js +323 -0
  21. package/dist/extended-benchmark-5RUXDG3D.js.map +1 -0
  22. package/dist/find-W5UDE4US.js +63 -0
  23. package/dist/find-W5UDE4US.js.map +1 -0
  24. package/dist/graph-DZNBEATA.js +189 -0
  25. package/dist/graph-DZNBEATA.js.map +1 -0
  26. package/dist/map-6WOMDLCP.js +131 -0
  27. package/dist/map-6WOMDLCP.js.map +1 -0
  28. package/dist/mcp-7WYTXIQS.js +354 -0
  29. package/dist/mcp-7WYTXIQS.js.map +1 -0
  30. package/dist/mcp-server.js +369 -0
  31. package/dist/mcp-server.js.map +1 -0
  32. package/dist/query-DJNWYYJD.js +427 -0
  33. package/dist/query-DJNWYYJD.js.map +1 -0
  34. package/dist/query-PS6QVPXP.js +538 -0
  35. package/dist/query-PS6QVPXP.js.map +1 -0
  36. package/dist/root-ODTOXM2J.js +10 -0
  37. package/dist/root-ODTOXM2J.js.map +1 -0
  38. package/dist/watcher-LFBZAM5E.js +73 -0
  39. package/dist/watcher-LFBZAM5E.js.map +1 -0
  40. package/package.json +61 -0
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveRoot
4
+ } from "./chunk-7RYHZOYF.js";
5
+ import {
6
+ IndexDatabase
7
+ } from "./chunk-Q6DOBQ4F.js";
8
+
9
+ // src/mcp.ts
10
+ import { createRequire } from "module";
11
+ import { resolve } from "path";
12
+ import { existsSync } from "fs";
13
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import { z } from "zod";
16
+ var require2 = createRequire(import.meta.url);
17
+ var pkg = require2("../package.json");
18
+ var db = null;
19
+ var rootDir = "";
20
+ function getDb(root) {
21
+ if (db && rootDir === root) return db;
22
+ const dbPath = resolve(root, ".codefocus", "index.db");
23
+ if (!existsSync(dbPath)) {
24
+ throw new Error(
25
+ `no index found at ${dbPath}
26
+ Run 'codefocus index --root ${root}' first.`
27
+ );
28
+ }
29
+ if (db) db.close();
30
+ db = new IndexDatabase(dbPath);
31
+ rootDir = root;
32
+ return db;
33
+ }
34
+ function createMcpServer(defaultRoot) {
35
+ const server = new McpServer({
36
+ name: "codefocus",
37
+ version: pkg.version
38
+ });
39
+ const effectiveRoot = defaultRoot ?? resolveRoot(void 0);
40
+ server.tool(
41
+ "query",
42
+ "Search the codebase and return ranked, budget-constrained code context. Returns YAML front matter with confidence metadata followed by relevant source code sections.",
43
+ {
44
+ term: z.string().describe("Search term (symbol name or keyword)"),
45
+ budget: z.number().int().positive().default(8e3).describe("Token budget for output (default: 8000)"),
46
+ depth: z.number().int().min(0).default(2).describe("Max graph traversal depth (default: 2)"),
47
+ root: z.string().optional().describe("Root directory of indexed project")
48
+ },
49
+ async ({ term, budget, depth, root }) => {
50
+ const r = root ?? effectiveRoot;
51
+ const output = await captureQueryOutput(r, term, budget, depth);
52
+ return { content: [{ type: "text", text: output }] };
53
+ }
54
+ );
55
+ server.tool(
56
+ "find",
57
+ "Quick symbol lookup by name with optional kind filter. Returns symbol names, kinds, file paths, and line numbers.",
58
+ {
59
+ symbol: z.string().describe("Symbol name to search for"),
60
+ kind: z.enum([
61
+ "function",
62
+ "class",
63
+ "interface",
64
+ "type",
65
+ "enum",
66
+ "variable",
67
+ "method",
68
+ "all"
69
+ ]).default("all").describe("Filter by symbol kind (default: all)"),
70
+ root: z.string().optional().describe("Root directory of indexed project")
71
+ },
72
+ async ({ symbol, kind, root }) => {
73
+ const r = root ?? effectiveRoot;
74
+ const database = getDb(r);
75
+ const results = database.findSymbols(symbol, kind);
76
+ if (results.length === 0) {
77
+ return {
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: `No symbols found matching "${symbol}"${kind !== "all" ? ` (kind: ${kind})` : ""}`
82
+ }
83
+ ]
84
+ };
85
+ }
86
+ const lines = [
87
+ `Found ${results.length} symbol${results.length !== 1 ? "s" : ""} matching "${symbol}"${kind !== "all" ? ` (kind: ${kind})` : ""}:`,
88
+ ""
89
+ ];
90
+ for (const sym of results) {
91
+ const sig = sym.signature ? ` ${sym.signature}` : "";
92
+ lines.push(
93
+ ` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`
94
+ );
95
+ }
96
+ return { content: [{ type: "text", text: lines.join("\n") }] };
97
+ }
98
+ );
99
+ server.tool(
100
+ "graph",
101
+ "Show the dependency graph for a file or symbol. Displays incoming and/or outgoing edges as an indented tree.",
102
+ {
103
+ target: z.string().describe("File path or symbol name to show graph for"),
104
+ direction: z.enum(["both", "incoming", "outgoing"]).default("both").describe("Graph direction (default: both)"),
105
+ root: z.string().optional().describe("Root directory of indexed project")
106
+ },
107
+ async ({ target, direction, root }) => {
108
+ const r = root ?? effectiveRoot;
109
+ const output = captureGraphOutput(r, target, direction);
110
+ return { content: [{ type: "text", text: output }] };
111
+ }
112
+ );
113
+ server.tool(
114
+ "map",
115
+ "High-level codebase overview ranked by PageRank connectivity. Returns file paths with their top symbols and signatures.",
116
+ {
117
+ budget: z.number().int().positive().default(2e3).describe("Token budget for output (default: 2000)"),
118
+ root: z.string().optional().describe("Root directory of indexed project")
119
+ },
120
+ async ({ budget, root }) => {
121
+ const r = root ?? effectiveRoot;
122
+ const output = captureMapOutput(r, budget);
123
+ return { content: [{ type: "text", text: output }] };
124
+ }
125
+ );
126
+ return server;
127
+ }
128
+ async function captureQueryOutput(root, term, budget, depth) {
129
+ const { runQueryCore } = await import("./query-DJNWYYJD.js");
130
+ return runQueryCore(root, term, budget, depth);
131
+ }
132
+ function captureGraphOutput(root, target, direction) {
133
+ const database = getDb(root);
134
+ const isFile = target.includes("/") || target.includes("\\") || /\.\w+$/.test(target);
135
+ if (isFile) {
136
+ return renderFileGraphToString(database, target, direction);
137
+ } else {
138
+ return renderSymbolGraphToString(database, target, direction);
139
+ }
140
+ }
141
+ function renderFileGraphToString(database, target, direction) {
142
+ const require3 = createRequire(import.meta.url);
143
+ const { DirectedGraph } = require3("graphology");
144
+ const graph = new DirectedGraph();
145
+ for (const file of database.getAllFiles()) {
146
+ graph.addNode(file.path);
147
+ }
148
+ for (const edge of database.getFileImportEdges()) {
149
+ const key = `${edge.source_file}->${edge.target_file}`;
150
+ if (graph.hasEdge(key)) {
151
+ const existing = graph.getEdgeAttributes(key);
152
+ graph.setEdgeAttribute(
153
+ key,
154
+ "specifiers",
155
+ `${existing.specifiers}, ${edge.specifiers}`
156
+ );
157
+ } else {
158
+ graph.addEdgeWithKey(key, edge.source_file, edge.target_file, {
159
+ specifiers: edge.specifiers
160
+ });
161
+ }
162
+ }
163
+ if (!graph.hasNode(target)) {
164
+ return `Error: file "${target}" not found in the index`;
165
+ }
166
+ const sections = [];
167
+ if (direction === "outgoing" || direction === "both") {
168
+ const outEdges = [];
169
+ graph.forEachOutEdge(
170
+ target,
171
+ (_edge, attrs, _src, tgt) => {
172
+ outEdges.push(` ${tgt} (imports: ${attrs.specifiers})`);
173
+ }
174
+ );
175
+ sections.push(
176
+ outEdges.length > 0 ? `Dependencies (outgoing):
177
+ ${outEdges.join("\n")}` : "Dependencies (outgoing): (none)"
178
+ );
179
+ }
180
+ if (direction === "incoming" || direction === "both") {
181
+ const inEdges = [];
182
+ graph.forEachInEdge(
183
+ target,
184
+ (_edge, attrs, src) => {
185
+ inEdges.push(` ${src} (imports: ${attrs.specifiers})`);
186
+ }
187
+ );
188
+ sections.push(
189
+ inEdges.length > 0 ? `Dependents (incoming):
190
+ ${inEdges.join("\n")}` : "Dependents (incoming): (none)"
191
+ );
192
+ }
193
+ return `${target}
194
+
195
+ ${sections.join("\n\n")}`;
196
+ }
197
+ function renderSymbolGraphToString(database, target, direction) {
198
+ const symbols = database.findSymbolsByName(target);
199
+ const exactMatch = symbols.find((s) => s.name === target);
200
+ const sym = exactMatch ?? symbols[0];
201
+ if (!sym || !sym.id) {
202
+ return `Error: symbol "${target}" not found in the index`;
203
+ }
204
+ const heading = `${sym.name} (${sym.kind}) \u2014 ${sym.file_path}:${sym.start_line}`;
205
+ const sections = [];
206
+ if (direction === "outgoing" || direction === "both") {
207
+ const refs = database.getOutgoingReferences(sym.id);
208
+ const lines = refs.map(
209
+ (r) => ` ${r.target_name} (${r.target_kind}) \u2014 ${r.target_file}:${r.target_line} [${r.ref_type}]`
210
+ );
211
+ sections.push(
212
+ lines.length > 0 ? `Dependencies (outgoing):
213
+ ${lines.join("\n")}` : "Dependencies (outgoing): (none)"
214
+ );
215
+ }
216
+ if (direction === "incoming" || direction === "both") {
217
+ const refs = database.getIncomingReferences(sym.id);
218
+ const lines = refs.map(
219
+ (r) => ` ${r.source_name} (${r.source_kind}) \u2014 ${r.source_file}:${r.source_line} [${r.ref_type}]`
220
+ );
221
+ sections.push(
222
+ lines.length > 0 ? `Dependents (incoming):
223
+ ${lines.join("\n")}` : "Dependents (incoming): (none)"
224
+ );
225
+ }
226
+ return `${heading}
227
+
228
+ ${sections.join("\n\n")}`;
229
+ }
230
+ function captureMapOutput(root, budget) {
231
+ const require3 = createRequire(import.meta.url);
232
+ const { DirectedGraph } = require3("graphology");
233
+ const pagerank = require3("graphology-metrics/centrality/pagerank");
234
+ const { getEncoding } = require3("js-tiktoken");
235
+ const database = getDb(root);
236
+ const files = database.getAllFiles();
237
+ if (files.length === 0) {
238
+ return "[map] Index is empty \u2014 no files to map.";
239
+ }
240
+ const graph = new DirectedGraph();
241
+ for (const file of files) {
242
+ graph.addNode(file.path);
243
+ }
244
+ for (const edge of database.getFileImportEdges()) {
245
+ const key = `${edge.source_file}->${edge.target_file}`;
246
+ if (!graph.hasEdge(key)) {
247
+ graph.addEdgeWithKey(key, edge.source_file, edge.target_file);
248
+ }
249
+ }
250
+ const ranks = graph.order > 0 ? pagerank(graph, { getEdgeWeight: null }) : {};
251
+ const rankedFiles = files.map((f) => ({ path: f.path, rank: ranks[f.path] ?? 0 })).sort((a, b) => b.rank - a.rank || a.path.localeCompare(b.path));
252
+ const enc = getEncoding("cl100k_base");
253
+ const blocks = [];
254
+ let tokenCount = 0;
255
+ let truncated = false;
256
+ for (const file of rankedFiles) {
257
+ const symbols = database.getSymbolsByFile(file.path);
258
+ symbols.sort((a, b) => a.start_line - b.start_line);
259
+ const lines = [file.path];
260
+ for (const sym of symbols) {
261
+ if (sym.kind === "variable" && !sym.signature) continue;
262
+ const label = sym.signature ?? `${sym.kind} ${sym.name}`;
263
+ lines.push(` ${label}`);
264
+ }
265
+ const block = lines.join("\n");
266
+ const blockTokens = enc.encode(block).length;
267
+ if (tokenCount + blockTokens <= budget) {
268
+ blocks.push(block);
269
+ tokenCount += blockTokens;
270
+ continue;
271
+ }
272
+ const blockLines = block.split("\n");
273
+ let partial = blockLines[0];
274
+ let partialTokens = enc.encode(partial).length;
275
+ if (tokenCount + partialTokens > budget) {
276
+ truncated = true;
277
+ break;
278
+ }
279
+ for (let i = 1; i < blockLines.length; i++) {
280
+ const candidate = partial + "\n" + blockLines[i];
281
+ const candidateTokens = enc.encode(candidate).length;
282
+ if (tokenCount + candidateTokens > budget) break;
283
+ partial = candidate;
284
+ partialTokens = candidateTokens;
285
+ }
286
+ blocks.push(partial);
287
+ tokenCount += partialTokens;
288
+ truncated = true;
289
+ break;
290
+ }
291
+ const shown = blocks.length;
292
+ const total = rankedFiles.length;
293
+ const parts = [`[map] ${shown}/${total} files, ~${tokenCount} tokens`];
294
+ if (truncated) {
295
+ parts.push(`(budget: ${budget}, truncated)`);
296
+ }
297
+ return blocks.join("\n\n") + `
298
+
299
+ ${parts.join(" ")}`;
300
+ }
301
+ async function runServe(_positional, flags) {
302
+ if (flags.help) {
303
+ console.error(`codefocus serve \u2014 Start MCP server (stdio transport)
304
+
305
+ Usage: codefocus serve [options]
306
+
307
+ Options:
308
+ --root <path> Root directory of indexed project (default: auto-detect)
309
+ --help Show this help message
310
+
311
+ The MCP server exposes four tools:
312
+ query Search and return ranked code context
313
+ find Quick symbol lookup
314
+ graph Show dependency graph for a file or symbol
315
+ map High-level codebase overview
316
+
317
+ The server keeps the database connection and tiktoken encoder warm,
318
+ so subsequent tool calls complete in ~10ms instead of ~400ms.
319
+
320
+ Usage with Claude Code:
321
+ Add to .mcp.json:
322
+ {
323
+ "mcpServers": {
324
+ "codefocus": {
325
+ "command": "npx",
326
+ "args": ["codefocus", "serve", "--root", "/path/to/project"]
327
+ }
328
+ }
329
+ }`);
330
+ return;
331
+ }
332
+ const root = resolveRoot(flags.root);
333
+ const dbPath = resolve(root, ".codefocus", "index.db");
334
+ if (!existsSync(dbPath)) {
335
+ console.error(
336
+ `Error: no index found at ${dbPath}
337
+ Run 'codefocus index --root ${root}' first.`
338
+ );
339
+ process.exitCode = 1;
340
+ return;
341
+ }
342
+ getDb(root);
343
+ console.error(`[codefocus] MCP server starting (root: ${root})`);
344
+ console.error(`[codefocus] Database: ${dbPath}`);
345
+ const server = createMcpServer(root);
346
+ const transport = new StdioServerTransport();
347
+ await server.connect(transport);
348
+ console.error(`[codefocus] MCP server ready (4 tools: query, find, graph, map)`);
349
+ }
350
+ export {
351
+ createMcpServer,
352
+ runServe
353
+ };
354
+ //# sourceMappingURL=mcp-7WYTXIQS.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 { 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"]}