@ngommans/codefocus 0.1.3 → 0.1.4
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/dist/{chunk-ITVAEU6K.js → chunk-472RLVFC.js} +2 -23
- package/dist/chunk-472RLVFC.js.map +1 -0
- package/dist/chunk-FQ3L6YEU.js +542 -0
- package/dist/chunk-FQ3L6YEU.js.map +1 -0
- package/dist/chunk-ZIVIJRW3.js +24 -0
- package/dist/chunk-ZIVIJRW3.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{mcp-7EPRSDMT.js → mcp-3YW6JWHG.js} +32 -14
- package/dist/{mcp-7EPRSDMT.js.map → mcp-3YW6JWHG.js.map} +1 -1
- package/dist/mcp-server.js +50 -563
- package/dist/mcp-server.js.map +1 -1
- package/dist/{query-PS6QVPXP.js → query-64CFCXTD.js} +5 -3
- package/dist/{query-PS6QVPXP.js.map → query-64CFCXTD.js.map} +1 -1
- package/dist/watcher-6WHIBMPS.js +72 -0
- package/dist/watcher-6WHIBMPS.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-ITVAEU6K.js.map +0 -1
package/dist/mcp-server.js
CHANGED
|
@@ -1,559 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
IndexDatabase,
|
|
4
3
|
resolveRoot
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZIVIJRW3.js";
|
|
5
|
+
import {
|
|
6
|
+
indexProject
|
|
7
|
+
} from "./chunk-FQ3L6YEU.js";
|
|
8
|
+
import {
|
|
9
|
+
IndexDatabase
|
|
10
|
+
} from "./chunk-472RLVFC.js";
|
|
6
11
|
|
|
7
12
|
// src/mcp.ts
|
|
8
|
-
import { createRequire
|
|
9
|
-
import { resolve
|
|
13
|
+
import { createRequire } from "module";
|
|
14
|
+
import { resolve } from "path";
|
|
10
15
|
import { existsSync } from "fs";
|
|
11
16
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
17
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
18
|
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";
|
|
23
19
|
var require2 = createRequire(import.meta.url);
|
|
24
|
-
var
|
|
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");
|
|
20
|
+
var pkg = require2("../package.json");
|
|
552
21
|
var db = null;
|
|
553
22
|
var rootDir = "";
|
|
554
23
|
function getDb(root) {
|
|
555
24
|
if (db && rootDir === root) return db;
|
|
556
|
-
const dbPath =
|
|
25
|
+
const dbPath = resolve(root, ".codefocus", "index.db");
|
|
557
26
|
if (!existsSync(dbPath)) {
|
|
558
27
|
throw new Error(
|
|
559
28
|
`no index found at ${dbPath}
|
|
@@ -660,7 +129,7 @@ function createMcpServer(defaultRoot) {
|
|
|
660
129
|
return server;
|
|
661
130
|
}
|
|
662
131
|
async function captureQueryOutput(root, term, budget, depth) {
|
|
663
|
-
const { runQueryCore } = await import("./query-
|
|
132
|
+
const { runQueryCore } = await import("./query-64CFCXTD.js");
|
|
664
133
|
return runQueryCore(root, term, budget, depth);
|
|
665
134
|
}
|
|
666
135
|
function captureGraphOutput(root, target, direction) {
|
|
@@ -673,8 +142,8 @@ function captureGraphOutput(root, target, direction) {
|
|
|
673
142
|
}
|
|
674
143
|
}
|
|
675
144
|
function renderFileGraphToString(database, target, direction) {
|
|
676
|
-
const
|
|
677
|
-
const { DirectedGraph } =
|
|
145
|
+
const require3 = createRequire(import.meta.url);
|
|
146
|
+
const { DirectedGraph } = require3("graphology");
|
|
678
147
|
const graph = new DirectedGraph();
|
|
679
148
|
for (const file of database.getAllFiles()) {
|
|
680
149
|
graph.addNode(file.path);
|
|
@@ -762,10 +231,10 @@ ${lines.join("\n")}` : "Dependents (incoming): (none)"
|
|
|
762
231
|
${sections.join("\n\n")}`;
|
|
763
232
|
}
|
|
764
233
|
function captureMapOutput(root, budget) {
|
|
765
|
-
const
|
|
766
|
-
const { DirectedGraph } =
|
|
767
|
-
const pagerank =
|
|
768
|
-
const { getEncoding } =
|
|
234
|
+
const require3 = createRequire(import.meta.url);
|
|
235
|
+
const { DirectedGraph } = require3("graphology");
|
|
236
|
+
const pagerank = require3("graphology-metrics/centrality/pagerank");
|
|
237
|
+
const { getEncoding } = require3("js-tiktoken");
|
|
769
238
|
const database = getDb(root);
|
|
770
239
|
const files = database.getAllFiles();
|
|
771
240
|
if (files.length === 0) {
|
|
@@ -864,24 +333,42 @@ Usage with Claude Code:
|
|
|
864
333
|
return;
|
|
865
334
|
}
|
|
866
335
|
const root = resolveRoot(flags2.root);
|
|
867
|
-
const dbPath =
|
|
868
|
-
if (!existsSync(
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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);
|
|
878
|
-
console.error(
|
|
879
|
-
`[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`
|
|
880
|
-
);
|
|
336
|
+
const dbPath = resolve(root, ".codefocus", "index.db");
|
|
337
|
+
if (!existsSync(root)) {
|
|
338
|
+
console.error(`Error: root directory does not exist: ${root}`);
|
|
339
|
+
process.exitCode = 1;
|
|
340
|
+
return;
|
|
881
341
|
}
|
|
342
|
+
console.error(`[codefocus] Indexing ${root} ...`);
|
|
343
|
+
const stats = await indexProject(root, dbPath);
|
|
344
|
+
console.error(
|
|
345
|
+
`[codefocus] Index complete: ${stats.filesIndexed} files, ${stats.symbolsExtracted} symbols (${stats.timeMs}ms)`
|
|
346
|
+
);
|
|
882
347
|
getDb(root);
|
|
883
348
|
console.error(`[codefocus] MCP server starting (root: ${root})`);
|
|
884
349
|
console.error(`[codefocus] Database: ${dbPath}`);
|
|
350
|
+
const { startWatcher } = await import("./watcher-6WHIBMPS.js");
|
|
351
|
+
const watcher = await startWatcher({
|
|
352
|
+
rootDir: root,
|
|
353
|
+
dbPath,
|
|
354
|
+
onReindex: (stats2) => {
|
|
355
|
+
if (stats2.filesIndexed > 0) {
|
|
356
|
+
if (db) {
|
|
357
|
+
db.close();
|
|
358
|
+
db = null;
|
|
359
|
+
}
|
|
360
|
+
getDb(root);
|
|
361
|
+
console.error(
|
|
362
|
+
`[codefocus] Re-indexed: ${stats2.filesIndexed} files, ${stats2.symbolsExtracted} symbols (${stats2.timeMs}ms)`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
process.on("SIGINT", () => {
|
|
368
|
+
watcher.close();
|
|
369
|
+
if (db) db.close();
|
|
370
|
+
process.exit(0);
|
|
371
|
+
});
|
|
885
372
|
const server = createMcpServer(root);
|
|
886
373
|
const transport = new StdioServerTransport();
|
|
887
374
|
await server.connect(transport);
|