@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.
- package/README.md +124 -0
- package/dist/benchmark-43DOYNYR.js +465 -0
- package/dist/benchmark-43DOYNYR.js.map +1 -0
- package/dist/chunk-6XH2ZLP6.js +127 -0
- package/dist/chunk-6XH2ZLP6.js.map +1 -0
- package/dist/chunk-7RYHZOYF.js +27 -0
- package/dist/chunk-7RYHZOYF.js.map +1 -0
- package/dist/chunk-ITVAEU6K.js +250 -0
- package/dist/chunk-ITVAEU6K.js.map +1 -0
- package/dist/chunk-Q6DOBQ4F.js +231 -0
- package/dist/chunk-Q6DOBQ4F.js.map +1 -0
- package/dist/chunk-X7DRJUEX.js +543 -0
- package/dist/chunk-X7DRJUEX.js.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands-ICBN54MT.js +64 -0
- package/dist/commands-ICBN54MT.js.map +1 -0
- package/dist/config-OCBWYENF.js +12 -0
- package/dist/config-OCBWYENF.js.map +1 -0
- package/dist/extended-benchmark-5RUXDG3D.js +323 -0
- package/dist/extended-benchmark-5RUXDG3D.js.map +1 -0
- package/dist/find-W5UDE4US.js +63 -0
- package/dist/find-W5UDE4US.js.map +1 -0
- package/dist/graph-DZNBEATA.js +189 -0
- package/dist/graph-DZNBEATA.js.map +1 -0
- package/dist/map-6WOMDLCP.js +131 -0
- package/dist/map-6WOMDLCP.js.map +1 -0
- package/dist/mcp-7WYTXIQS.js +354 -0
- package/dist/mcp-7WYTXIQS.js.map +1 -0
- package/dist/mcp-server.js +369 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/query-DJNWYYJD.js +427 -0
- package/dist/query-DJNWYYJD.js.map +1 -0
- package/dist/query-PS6QVPXP.js +538 -0
- package/dist/query-PS6QVPXP.js.map +1 -0
- package/dist/root-ODTOXM2J.js +10 -0
- package/dist/root-ODTOXM2J.js.map +1 -0
- package/dist/watcher-LFBZAM5E.js +73 -0
- package/dist/watcher-LFBZAM5E.js.map +1 -0
- package/package.json +61 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
var require2 = createRequire(import.meta.url);
|
|
6
|
+
var pkg = require2("../package.json");
|
|
7
|
+
var COMMANDS = ["index", "query", "find", "graph", "map", "benchmark", "serve"];
|
|
8
|
+
function parseArgs(argv) {
|
|
9
|
+
const args = argv.slice(2);
|
|
10
|
+
const flags = {};
|
|
11
|
+
const positional = [];
|
|
12
|
+
let command = null;
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg.startsWith("--")) {
|
|
16
|
+
const key = arg.slice(2);
|
|
17
|
+
const next = args[i + 1];
|
|
18
|
+
if (next && !next.startsWith("--")) {
|
|
19
|
+
flags[key] = next;
|
|
20
|
+
i++;
|
|
21
|
+
} else {
|
|
22
|
+
flags[key] = true;
|
|
23
|
+
}
|
|
24
|
+
} else if (!command && COMMANDS.includes(arg)) {
|
|
25
|
+
command = arg;
|
|
26
|
+
} else {
|
|
27
|
+
positional.push(arg);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { command, positional, flags };
|
|
31
|
+
}
|
|
32
|
+
function printHelp() {
|
|
33
|
+
console.log(`codefocus v${pkg.version} \u2014 Smart code context aggregator
|
|
34
|
+
|
|
35
|
+
Usage: codefocus <command> [options]
|
|
36
|
+
|
|
37
|
+
Commands:
|
|
38
|
+
index Index the codebase (AST parse, build graph, store in SQLite)
|
|
39
|
+
query Search and return ranked, structured code context
|
|
40
|
+
find Quick symbol lookup (no graph expansion)
|
|
41
|
+
graph Show dependency graph for a file or symbol
|
|
42
|
+
map High-level codebase overview (Aider-style repo map)
|
|
43
|
+
serve Start MCP server (stdio transport, keeps DB warm)
|
|
44
|
+
benchmark Benchmark codefocus vs. Explore agent pattern
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--help Show this help message
|
|
48
|
+
--version Show version number
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
npx codefocus index --root ./src
|
|
52
|
+
npx codefocus query "handleCalendarSync" --budget 8000
|
|
53
|
+
npx codefocus find "CalendarEvent" --kind type
|
|
54
|
+
npx codefocus graph src/services/calendar.ts
|
|
55
|
+
npx codefocus map --budget 2000`);
|
|
56
|
+
}
|
|
57
|
+
async function main() {
|
|
58
|
+
const { command, positional, flags } = parseArgs(process.argv);
|
|
59
|
+
if (flags.version) {
|
|
60
|
+
console.log(pkg.version);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (flags.help && !command) {
|
|
64
|
+
printHelp();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!command) {
|
|
68
|
+
printHelp();
|
|
69
|
+
process.exitCode = 2;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const handlers = {
|
|
73
|
+
index: async () => {
|
|
74
|
+
const { runIndex } = await import("./commands-ICBN54MT.js");
|
|
75
|
+
await runIndex(positional, flags);
|
|
76
|
+
},
|
|
77
|
+
query: async () => {
|
|
78
|
+
const { runQuery } = await import("./query-DJNWYYJD.js");
|
|
79
|
+
await runQuery(positional, flags);
|
|
80
|
+
},
|
|
81
|
+
find: async () => {
|
|
82
|
+
const { runFind } = await import("./find-W5UDE4US.js");
|
|
83
|
+
await runFind(positional, flags);
|
|
84
|
+
},
|
|
85
|
+
graph: async () => {
|
|
86
|
+
const { runGraph } = await import("./graph-DZNBEATA.js");
|
|
87
|
+
await runGraph(positional, flags);
|
|
88
|
+
},
|
|
89
|
+
map: async () => {
|
|
90
|
+
const { runMap } = await import("./map-6WOMDLCP.js");
|
|
91
|
+
await runMap(positional, flags);
|
|
92
|
+
},
|
|
93
|
+
serve: async () => {
|
|
94
|
+
const { runServe } = await import("./mcp-7WYTXIQS.js");
|
|
95
|
+
await runServe(positional, flags);
|
|
96
|
+
},
|
|
97
|
+
benchmark: async () => {
|
|
98
|
+
const { runBenchmarkCommand } = await import("./benchmark-43DOYNYR.js");
|
|
99
|
+
await runBenchmarkCommand(positional, flags);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
await handlers[command]();
|
|
104
|
+
} catch (err) {
|
|
105
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
106
|
+
console.error(`Error: ${message}`);
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
main();
|
|
111
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\");\n\nconst COMMANDS = [\"index\", \"query\", \"find\", \"graph\", \"map\", \"benchmark\", \"serve\"] as const;\ntype Command = (typeof COMMANDS)[number];\n\ninterface ParsedArgs {\n command: Command | null;\n positional: string[];\n flags: Record<string, string | boolean>;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const args = argv.slice(2);\n const flags: Record<string, string | boolean> = {};\n const positional: string[] = [];\n let command: Command | null = null;\n\n for (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 } else if (!command && COMMANDS.includes(arg as Command)) {\n command = arg as Command;\n } else {\n positional.push(arg);\n }\n }\n\n return { command, positional, flags };\n}\n\nfunction printHelp(): void {\n console.log(`codefocus v${pkg.version} — Smart code context aggregator\n\nUsage: codefocus <command> [options]\n\nCommands:\n index Index the codebase (AST parse, build graph, store in SQLite)\n query Search and return ranked, structured code context\n find Quick symbol lookup (no graph expansion)\n graph Show dependency graph for a file or symbol\n map High-level codebase overview (Aider-style repo map)\n serve Start MCP server (stdio transport, keeps DB warm)\n benchmark Benchmark codefocus vs. Explore agent pattern\n\nOptions:\n --help Show this help message\n --version Show version number\n\nExamples:\n npx codefocus index --root ./src\n npx codefocus query \"handleCalendarSync\" --budget 8000\n npx codefocus find \"CalendarEvent\" --kind type\n npx codefocus graph src/services/calendar.ts\n npx codefocus map --budget 2000`);\n}\n\nasync function main(): Promise<void> {\n const { command, positional, flags } = parseArgs(process.argv);\n\n if (flags.version) {\n console.log(pkg.version);\n return;\n }\n\n if (flags.help && !command) {\n printHelp();\n return;\n }\n\n if (!command) {\n printHelp();\n process.exitCode = 2;\n return;\n }\n\n const handlers: Record<Command, () => Promise<void>> = {\n index: async () => {\n const { runIndex } = await import(\"./commands/index.js\");\n await runIndex(positional, flags);\n },\n query: async () => {\n const { runQuery } = await import(\"./commands/query.js\");\n await runQuery(positional, flags);\n },\n find: async () => {\n const { runFind } = await import(\"./commands/find.js\");\n await runFind(positional, flags);\n },\n graph: async () => {\n const { runGraph } = await import(\"./commands/graph.js\");\n await runGraph(positional, flags);\n },\n map: async () => {\n const { runMap } = await import(\"./commands/map.js\");\n await runMap(positional, flags);\n },\n serve: async () => {\n const { runServe } = await import(\"./mcp.js\");\n await runServe(positional, flags);\n },\n benchmark: async () => {\n const { runBenchmarkCommand } = await import(\"./benchmark.js\");\n await runBenchmarkCommand(positional, flags);\n },\n };\n\n try {\n await handlers[command]();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${message}`);\n process.exitCode = 1;\n }\n}\n\nmain();\n"],"mappings":";;;AAAA,SAAS,qBAAqB;AAE9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQ,iBAAiB;AAErC,IAAM,WAAW,CAAC,SAAS,SAAS,QAAQ,SAAS,OAAO,aAAa,OAAO;AAShF,SAAS,UAAU,MAA4B;AAC7C,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAM,QAA0C,CAAC;AACjD,QAAM,aAAuB,CAAC;AAC9B,MAAI,UAA0B;AAE9B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,MAAM,IAAI,MAAM,CAAC;AACvB,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG;AAClC,cAAM,GAAG,IAAI;AACb;AAAA,MACF,OAAO;AACL,cAAM,GAAG,IAAI;AAAA,MACf;AAAA,IACF,WAAW,CAAC,WAAW,SAAS,SAAS,GAAc,GAAG;AACxD,gBAAU;AAAA,IACZ,OAAO;AACL,iBAAW,KAAK,GAAG;AAAA,IACrB;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,YAAY,MAAM;AACtC;AAEA,SAAS,YAAkB;AACzB,UAAQ,IAAI,cAAc,IAAI,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAsBL;AAClC;AAEA,eAAe,OAAsB;AACnC,QAAM,EAAE,SAAS,YAAY,MAAM,IAAI,UAAU,QAAQ,IAAI;AAE7D,MAAI,MAAM,SAAS;AACjB,YAAQ,IAAI,IAAI,OAAO;AACvB;AAAA,EACF;AAEA,MAAI,MAAM,QAAQ,CAAC,SAAS;AAC1B,cAAU;AACV;AAAA,EACF;AAEA,MAAI,CAAC,SAAS;AACZ,cAAU;AACV,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,WAAiD;AAAA,IACrD,OAAO,YAAY;AACjB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,wBAAqB;AACvD,YAAM,SAAS,YAAY,KAAK;AAAA,IAClC;AAAA,IACA,OAAO,YAAY;AACjB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,qBAAqB;AACvD,YAAM,SAAS,YAAY,KAAK;AAAA,IAClC;AAAA,IACA,MAAM,YAAY;AAChB,YAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,oBAAoB;AACrD,YAAM,QAAQ,YAAY,KAAK;AAAA,IACjC;AAAA,IACA,OAAO,YAAY;AACjB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,qBAAqB;AACvD,YAAM,SAAS,YAAY,KAAK;AAAA,IAClC;AAAA,IACA,KAAK,YAAY;AACf,YAAM,EAAE,OAAO,IAAI,MAAM,OAAO,mBAAmB;AACnD,YAAM,OAAO,YAAY,KAAK;AAAA,IAChC;AAAA,IACA,OAAO,YAAY;AACjB,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,mBAAU;AAC5C,YAAM,SAAS,YAAY,KAAK;AAAA,IAClC;AAAA,IACA,WAAW,YAAY;AACrB,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,yBAAgB;AAC7D,YAAM,oBAAoB,YAAY,KAAK;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAS,OAAO,EAAE;AAAA,EAC1B,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,YAAQ,WAAW;AAAA,EACrB;AACF;AAEA,KAAK;","names":["require"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
indexProject
|
|
4
|
+
} from "./chunk-X7DRJUEX.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveRoot
|
|
7
|
+
} from "./chunk-7RYHZOYF.js";
|
|
8
|
+
import "./chunk-Q6DOBQ4F.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/index.ts
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
async function runIndex(positional, flags) {
|
|
13
|
+
if (flags.help) {
|
|
14
|
+
console.log(`codefocus index \u2014 Index the codebase
|
|
15
|
+
|
|
16
|
+
Usage: codefocus index [options]
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--root <path> Root directory to index (default: .)
|
|
20
|
+
--lang <lang> Language to parse (default: typescript)
|
|
21
|
+
--watch Watch for file changes and re-index automatically
|
|
22
|
+
--help Show this help message`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const root = resolveRoot(flags.root);
|
|
26
|
+
const dbPath = resolve(root, ".codefocus", "index.db");
|
|
27
|
+
console.log(`[codefocus] Indexing ${root} ...`);
|
|
28
|
+
const stats = await indexProject(root, dbPath);
|
|
29
|
+
console.log(`[codefocus] Index complete`);
|
|
30
|
+
console.log(` files indexed: ${stats.filesIndexed}`);
|
|
31
|
+
console.log(` files skipped: ${stats.filesSkipped}`);
|
|
32
|
+
console.log(` symbols: ${stats.symbolsExtracted}`);
|
|
33
|
+
console.log(` imports: ${stats.importsFound}`);
|
|
34
|
+
console.log(` references: ${stats.referencesCreated}`);
|
|
35
|
+
console.log(` time: ${stats.timeMs}ms`);
|
|
36
|
+
console.log(` database: ${dbPath}`);
|
|
37
|
+
if (flags.watch) {
|
|
38
|
+
const { startWatcher } = await import("./watcher-LFBZAM5E.js");
|
|
39
|
+
console.log(`[codefocus] Watching for changes in ${root} ...`);
|
|
40
|
+
console.log(`[codefocus] Press Ctrl+C to stop`);
|
|
41
|
+
const watcher = await startWatcher({
|
|
42
|
+
rootDir: root,
|
|
43
|
+
dbPath,
|
|
44
|
+
onReindex: (reindexStats) => {
|
|
45
|
+
if (reindexStats.filesIndexed > 0) {
|
|
46
|
+
console.log(
|
|
47
|
+
`[watch] Re-indexed: ${reindexStats.filesIndexed} files, ${reindexStats.symbolsExtracted} symbols, ${reindexStats.referencesCreated} refs (${reindexStats.timeMs}ms)`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
process.on("SIGINT", () => {
|
|
53
|
+
console.log("\n[codefocus] Stopping watcher...");
|
|
54
|
+
watcher.close();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
await new Promise(() => {
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
runIndex
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=commands-ICBN54MT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/index.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { indexProject } from \"../indexer.js\";\nimport { resolveRoot } from \"../root.js\";\n\nexport async function runIndex(\n positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.log(`codefocus index — Index the codebase\n\nUsage: codefocus index [options]\n\nOptions:\n --root <path> Root directory to index (default: .)\n --lang <lang> Language to parse (default: typescript)\n --watch Watch for file changes and re-index automatically\n --help Show this help message`);\n return;\n }\n\n const root = resolveRoot(flags.root);\n const dbPath = resolve(root, \".codefocus\", \"index.db\");\n\n console.log(`[codefocus] Indexing ${root} ...`);\n\n const stats = await indexProject(root, dbPath);\n\n console.log(`[codefocus] Index complete`);\n console.log(` files indexed: ${stats.filesIndexed}`);\n console.log(` files skipped: ${stats.filesSkipped}`);\n console.log(` symbols: ${stats.symbolsExtracted}`);\n console.log(` imports: ${stats.importsFound}`);\n console.log(` references: ${stats.referencesCreated}`);\n console.log(` time: ${stats.timeMs}ms`);\n console.log(` database: ${dbPath}`);\n\n if (flags.watch) {\n const { startWatcher } = await import(\"../watcher.js\");\n\n console.log(`[codefocus] Watching for changes in ${root} ...`);\n console.log(`[codefocus] Press Ctrl+C to stop`);\n\n const watcher = await startWatcher({\n rootDir: root,\n dbPath,\n onReindex: (reindexStats) => {\n if (reindexStats.filesIndexed > 0) {\n console.log(\n `[watch] Re-indexed: ${reindexStats.filesIndexed} files, ` +\n `${reindexStats.symbolsExtracted} symbols, ` +\n `${reindexStats.referencesCreated} refs (${reindexStats.timeMs}ms)`,\n );\n }\n },\n });\n\n // Keep process alive, clean up on exit\n process.on(\"SIGINT\", () => {\n console.log(\"\\n[codefocus] Stopping watcher...\");\n watcher.close();\n process.exit(0);\n });\n\n // Return a never-resolving promise to keep the process alive\n await new Promise(() => {});\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,eAAe;AAIxB,eAAsB,SACpB,YACA,OACe;AACf,MAAI,MAAM,MAAM;AACd,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAQyB;AACrC;AAAA,EACF;AAEA,QAAM,OAAO,YAAY,MAAM,IAAI;AACnC,QAAM,SAAS,QAAQ,MAAM,cAAc,UAAU;AAErD,UAAQ,IAAI,wBAAwB,IAAI,MAAM;AAE9C,QAAM,QAAQ,MAAM,aAAa,MAAM,MAAM;AAE7C,UAAQ,IAAI,4BAA4B;AACxC,UAAQ,IAAI,oBAAoB,MAAM,YAAY,EAAE;AACpD,UAAQ,IAAI,oBAAoB,MAAM,YAAY,EAAE;AACpD,UAAQ,IAAI,oBAAoB,MAAM,gBAAgB,EAAE;AACxD,UAAQ,IAAI,oBAAoB,MAAM,YAAY,EAAE;AACpD,UAAQ,IAAI,oBAAoB,MAAM,iBAAiB,EAAE;AACzD,UAAQ,IAAI,oBAAoB,MAAM,MAAM,IAAI;AAChD,UAAQ,IAAI,oBAAoB,MAAM,EAAE;AAExC,MAAI,MAAM,OAAO;AACf,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,uBAAe;AAErD,YAAQ,IAAI,uCAAuC,IAAI,MAAM;AAC7D,YAAQ,IAAI,kCAAkC;AAE9C,UAAM,UAAU,MAAM,aAAa;AAAA,MACjC,SAAS;AAAA,MACT;AAAA,MACA,WAAW,CAAC,iBAAiB;AAC3B,YAAI,aAAa,eAAe,GAAG;AACjC,kBAAQ;AAAA,YACN,uBAAuB,aAAa,YAAY,WAC3C,aAAa,gBAAgB,aAC7B,aAAa,iBAAiB,UAAU,aAAa,MAAM;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,YAAQ,GAAG,UAAU,MAAM;AACzB,cAAQ,IAAI,mCAAmC;AAC/C,cAAQ,MAAM;AACd,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAGD,UAAM,IAAI,QAAQ,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_SCORING_CONFIG,
|
|
4
|
+
loadScoringConfig,
|
|
5
|
+
serializeConfig
|
|
6
|
+
} from "./chunk-6XH2ZLP6.js";
|
|
7
|
+
export {
|
|
8
|
+
DEFAULT_SCORING_CONFIG,
|
|
9
|
+
loadScoringConfig,
|
|
10
|
+
serializeConfig
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=config-OCBWYENF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadScoringConfig,
|
|
4
|
+
serializeConfig
|
|
5
|
+
} from "./chunk-6XH2ZLP6.js";
|
|
6
|
+
|
|
7
|
+
// src/extended-benchmark.ts
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
import { execFileSync } from "child_process";
|
|
10
|
+
import { readFileSync, existsSync, rmSync } from "fs";
|
|
11
|
+
import { resolve } from "path";
|
|
12
|
+
var require2 = createRequire(import.meta.url);
|
|
13
|
+
var { getEncoding } = require2("js-tiktoken");
|
|
14
|
+
function discoverSuites(projectRoot) {
|
|
15
|
+
const suites = [];
|
|
16
|
+
const fastGlobRoot = resolve(
|
|
17
|
+
projectRoot,
|
|
18
|
+
"node_modules/fast-glob/out"
|
|
19
|
+
);
|
|
20
|
+
if (existsSync(fastGlobRoot)) {
|
|
21
|
+
suites.push({
|
|
22
|
+
id: "fast-glob",
|
|
23
|
+
name: "fast-glob",
|
|
24
|
+
rootDir: fastGlobRoot,
|
|
25
|
+
tasks: [
|
|
26
|
+
{
|
|
27
|
+
id: "fg-pattern-matching",
|
|
28
|
+
question: "How does fast-glob match file patterns?",
|
|
29
|
+
queryTerm: "pattern",
|
|
30
|
+
expectedPatterns: ["pattern"],
|
|
31
|
+
searchTerms: ["pattern", "match", "glob"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "fg-sync-reader",
|
|
35
|
+
question: "How does the synchronous file reader work?",
|
|
36
|
+
queryTerm: "sync",
|
|
37
|
+
expectedPatterns: ["sync"],
|
|
38
|
+
searchTerms: ["sync", "read"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "fg-entry-filter",
|
|
42
|
+
question: "How are file entries filtered?",
|
|
43
|
+
queryTerm: "filter",
|
|
44
|
+
expectedPatterns: ["filter"],
|
|
45
|
+
searchTerms: ["filter", "entry"]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "fg-stream-api",
|
|
49
|
+
question: "How does the streaming API work?",
|
|
50
|
+
queryTerm: "stream",
|
|
51
|
+
expectedPatterns: ["stream"],
|
|
52
|
+
searchTerms: ["stream", "readable"]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const chokidarRoot = resolve(
|
|
58
|
+
projectRoot,
|
|
59
|
+
"node_modules/chokidar"
|
|
60
|
+
);
|
|
61
|
+
if (existsSync(chokidarRoot) && existsSync(resolve(chokidarRoot, "handler.js"))) {
|
|
62
|
+
suites.push({
|
|
63
|
+
id: "chokidar",
|
|
64
|
+
name: "chokidar",
|
|
65
|
+
rootDir: chokidarRoot,
|
|
66
|
+
tasks: [
|
|
67
|
+
{
|
|
68
|
+
id: "chokidar-watch",
|
|
69
|
+
question: "How does chokidar watch files?",
|
|
70
|
+
queryTerm: "watch",
|
|
71
|
+
expectedPatterns: ["index"],
|
|
72
|
+
searchTerms: ["watch", "FSWatcher"]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "chokidar-handler",
|
|
76
|
+
question: "How does chokidar handle file system events?",
|
|
77
|
+
queryTerm: "handler",
|
|
78
|
+
expectedPatterns: ["handler"],
|
|
79
|
+
searchTerms: ["handler", "event", "change"]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return suites;
|
|
85
|
+
}
|
|
86
|
+
function simulateExploreForSuite(rootDir, task) {
|
|
87
|
+
const enc = getEncoding("cl100k_base");
|
|
88
|
+
const matchedFiles = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const term of task.searchTerms) {
|
|
90
|
+
try {
|
|
91
|
+
const grepResult = execFileSync(
|
|
92
|
+
"grep",
|
|
93
|
+
["-rl", "--include=*.js", "--include=*.ts", term, rootDir],
|
|
94
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
95
|
+
);
|
|
96
|
+
for (const line of grepResult.trim().split("\n")) {
|
|
97
|
+
if (line) matchedFiles.add(line);
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let totalTokens = 0;
|
|
103
|
+
for (const filePath of matchedFiles) {
|
|
104
|
+
try {
|
|
105
|
+
const content = readFileSync(filePath, "utf-8");
|
|
106
|
+
totalTokens += enc.encode(content).length;
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return totalTokens;
|
|
111
|
+
}
|
|
112
|
+
function runCodefocusQueryForSuite(rootDir, cliPath, task) {
|
|
113
|
+
let stdout;
|
|
114
|
+
try {
|
|
115
|
+
stdout = execFileSync(
|
|
116
|
+
"node",
|
|
117
|
+
[cliPath, "query", task.queryTerm, "--root", rootDir, "--budget", "8000"],
|
|
118
|
+
{ encoding: "utf-8", timeout: 3e4 }
|
|
119
|
+
);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
stdout = (err.stdout || "") + (err.stderr || "");
|
|
122
|
+
}
|
|
123
|
+
const tokenMatch = stdout.match(/~(\d+) tokens/);
|
|
124
|
+
const tokensOutput = tokenMatch ? parseInt(tokenMatch[1], 10) : 0;
|
|
125
|
+
const fileMatches = stdout.matchAll(/── ([\w/.\-@]+\.[jt]sx?):\d+-\d+/g);
|
|
126
|
+
const filesReturned = [...fileMatches].map((m) => m[1]);
|
|
127
|
+
return { tokensOutput, filesReturned };
|
|
128
|
+
}
|
|
129
|
+
function runExtendedBenchmark(projectRoot, cliPath, suites) {
|
|
130
|
+
const config = loadScoringConfig(projectRoot);
|
|
131
|
+
const resolvedSuites = suites ?? discoverSuites(projectRoot);
|
|
132
|
+
const suiteResults = [];
|
|
133
|
+
for (const suite of resolvedSuites) {
|
|
134
|
+
let indexStdout;
|
|
135
|
+
try {
|
|
136
|
+
indexStdout = execFileSync(
|
|
137
|
+
"node",
|
|
138
|
+
[cliPath, "index", "--root", suite.rootDir],
|
|
139
|
+
{ encoding: "utf-8", timeout: 6e4 }
|
|
140
|
+
);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
indexStdout = (err.stdout || "") + (err.stderr || "");
|
|
143
|
+
}
|
|
144
|
+
const filesMatch = indexStdout.match(/files indexed:\s*(\d+)/);
|
|
145
|
+
const symbolsMatch = indexStdout.match(/symbols:\s+(\d+)/);
|
|
146
|
+
const filesIndexed = filesMatch ? parseInt(filesMatch[1], 10) : 0;
|
|
147
|
+
const symbolsExtracted = symbolsMatch ? parseInt(symbolsMatch[1], 10) : 0;
|
|
148
|
+
const skippedMatch = indexStdout.match(/files skipped:\s*(\d+)/);
|
|
149
|
+
const totalFiles = filesIndexed + (skippedMatch ? parseInt(skippedMatch[1], 10) : 0);
|
|
150
|
+
const taskResults = [];
|
|
151
|
+
for (const task of suite.tasks) {
|
|
152
|
+
const exploreTokens = simulateExploreForSuite(suite.rootDir, task);
|
|
153
|
+
const { tokensOutput, filesReturned } = runCodefocusQueryForSuite(
|
|
154
|
+
suite.rootDir,
|
|
155
|
+
cliPath,
|
|
156
|
+
task
|
|
157
|
+
);
|
|
158
|
+
let patternHits = 0;
|
|
159
|
+
for (const pattern of task.expectedPatterns) {
|
|
160
|
+
const hasMatch = filesReturned.some(
|
|
161
|
+
(f) => f.toLowerCase().includes(pattern.toLowerCase())
|
|
162
|
+
);
|
|
163
|
+
if (hasMatch) patternHits++;
|
|
164
|
+
}
|
|
165
|
+
taskResults.push({
|
|
166
|
+
task,
|
|
167
|
+
filesReturned,
|
|
168
|
+
tokensOutput,
|
|
169
|
+
exploreTokens,
|
|
170
|
+
patternHits,
|
|
171
|
+
patternTotal: task.expectedPatterns.length,
|
|
172
|
+
patternRecall: task.expectedPatterns.length > 0 ? patternHits / task.expectedPatterns.length : 1,
|
|
173
|
+
tokenSavingsRatio: tokensOutput > 0 ? exploreTokens / tokensOutput : 0,
|
|
174
|
+
hasResults: filesReturned.length > 0,
|
|
175
|
+
filesIndexed: totalFiles
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const avg2 = (arr) => arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;
|
|
179
|
+
suiteResults.push({
|
|
180
|
+
suite,
|
|
181
|
+
filesIndexed: totalFiles,
|
|
182
|
+
symbolsExtracted,
|
|
183
|
+
tasks: taskResults,
|
|
184
|
+
avgPatternRecall: avg2(taskResults.map((t) => t.patternRecall)),
|
|
185
|
+
avgTokenSavingsRatio: avg2(taskResults.map((t) => t.tokenSavingsRatio)),
|
|
186
|
+
avgTokensOutput: avg2(taskResults.map((t) => t.tokensOutput))
|
|
187
|
+
});
|
|
188
|
+
const dbDir = resolve(suite.rootDir, ".codefocus");
|
|
189
|
+
if (existsSync(dbDir)) {
|
|
190
|
+
try {
|
|
191
|
+
rmSync(dbDir, { recursive: true });
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const allTasks = suiteResults.flatMap((s) => s.tasks);
|
|
197
|
+
const avg = (arr) => arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;
|
|
198
|
+
return {
|
|
199
|
+
suites: suiteResults,
|
|
200
|
+
config,
|
|
201
|
+
aggregate: {
|
|
202
|
+
totalSuites: suiteResults.length,
|
|
203
|
+
totalTasks: allTasks.length,
|
|
204
|
+
avgPatternRecall: avg(allTasks.map((t) => t.patternRecall)),
|
|
205
|
+
avgTokenSavingsRatio: avg(allTasks.map((t) => t.tokenSavingsRatio)),
|
|
206
|
+
avgTokensOutput: avg(allTasks.map((t) => t.tokensOutput)),
|
|
207
|
+
emptyResultCount: allTasks.filter((t) => !t.hasResults).length
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function pct(n) {
|
|
212
|
+
return `${Math.round(n * 100)}%`;
|
|
213
|
+
}
|
|
214
|
+
function formatExtendedResults(results) {
|
|
215
|
+
const lines = [];
|
|
216
|
+
lines.push("# codefocus Extended Benchmark \u2014 Spike 9");
|
|
217
|
+
lines.push("");
|
|
218
|
+
lines.push(
|
|
219
|
+
"Cross-codebase validation of scoring parameters against dependency libraries."
|
|
220
|
+
);
|
|
221
|
+
lines.push(`Benchmark run: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
222
|
+
lines.push("");
|
|
223
|
+
lines.push("## Aggregate Summary");
|
|
224
|
+
lines.push("");
|
|
225
|
+
lines.push(`| Metric | Value |`);
|
|
226
|
+
lines.push(`|--------|------:|`);
|
|
227
|
+
lines.push(
|
|
228
|
+
`| Libraries tested | ${results.aggregate.totalSuites} |`
|
|
229
|
+
);
|
|
230
|
+
lines.push(`| Total tasks | ${results.aggregate.totalTasks} |`);
|
|
231
|
+
lines.push(
|
|
232
|
+
`| Avg pattern recall | ${pct(results.aggregate.avgPatternRecall)} |`
|
|
233
|
+
);
|
|
234
|
+
lines.push(
|
|
235
|
+
`| Avg token savings | ${results.aggregate.avgTokenSavingsRatio.toFixed(1)}x |`
|
|
236
|
+
);
|
|
237
|
+
lines.push(
|
|
238
|
+
`| Avg tokens output | ${Math.round(results.aggregate.avgTokensOutput)} |`
|
|
239
|
+
);
|
|
240
|
+
lines.push(
|
|
241
|
+
`| Empty results | ${results.aggregate.emptyResultCount}/${results.aggregate.totalTasks} |`
|
|
242
|
+
);
|
|
243
|
+
lines.push("");
|
|
244
|
+
for (const suite of results.suites) {
|
|
245
|
+
lines.push(`## ${suite.suite.name}`);
|
|
246
|
+
lines.push("");
|
|
247
|
+
lines.push(
|
|
248
|
+
`Indexed: ${suite.filesIndexed} files, ${suite.symbolsExtracted} symbols`
|
|
249
|
+
);
|
|
250
|
+
lines.push("");
|
|
251
|
+
lines.push(
|
|
252
|
+
"| Task | CF tokens | Explore tokens | Savings | Pattern recall | Files returned |"
|
|
253
|
+
);
|
|
254
|
+
lines.push(
|
|
255
|
+
"|------|----------:|---------------:|--------:|---------------:|---------------:|"
|
|
256
|
+
);
|
|
257
|
+
for (const t of suite.tasks) {
|
|
258
|
+
const savings = t.tokenSavingsRatio > 0 ? t.tokenSavingsRatio.toFixed(1) + "x" : "\u2014";
|
|
259
|
+
lines.push(
|
|
260
|
+
`| ${t.task.id} | ${t.tokensOutput} | ${t.exploreTokens} | ${savings} | ${pct(t.patternRecall)} | ${t.filesReturned.length} |`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
lines.push("");
|
|
264
|
+
lines.push(
|
|
265
|
+
`**Suite averages:** pattern recall=${pct(suite.avgPatternRecall)}, savings=${suite.avgTokenSavingsRatio.toFixed(1)}x, tokens=${Math.round(suite.avgTokensOutput)}`
|
|
266
|
+
);
|
|
267
|
+
lines.push("");
|
|
268
|
+
}
|
|
269
|
+
lines.push("## Active Scoring Config");
|
|
270
|
+
lines.push("");
|
|
271
|
+
lines.push("```json");
|
|
272
|
+
lines.push(serializeConfig(results.config));
|
|
273
|
+
lines.push("```");
|
|
274
|
+
lines.push("");
|
|
275
|
+
lines.push("## Overfitting Analysis");
|
|
276
|
+
lines.push("");
|
|
277
|
+
const recall = results.aggregate.avgPatternRecall;
|
|
278
|
+
if (recall >= 0.7) {
|
|
279
|
+
lines.push(
|
|
280
|
+
`Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks suggests scoring parameters generalize well.`
|
|
281
|
+
);
|
|
282
|
+
} else if (recall >= 0.4) {
|
|
283
|
+
lines.push(
|
|
284
|
+
`Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks is moderate \u2014 scoring parameters may need calibration for larger codebases.`
|
|
285
|
+
);
|
|
286
|
+
lines.push("");
|
|
287
|
+
lines.push("Parameters most likely to need adjustment:");
|
|
288
|
+
lines.push(
|
|
289
|
+
`- \`scoreFloorRatio\` (${results.config.scoreFloorRatio}) \u2014 may over-prune in larger codebases`
|
|
290
|
+
);
|
|
291
|
+
lines.push(
|
|
292
|
+
`- \`elbowDropRatio\` (${results.config.elbowDropRatio}) \u2014 may cut too aggressively`
|
|
293
|
+
);
|
|
294
|
+
} else {
|
|
295
|
+
lines.push(
|
|
296
|
+
`Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks indicates likely overfitting to the original 16-file codebase.`
|
|
297
|
+
);
|
|
298
|
+
lines.push("");
|
|
299
|
+
lines.push("**Recommended actions:**");
|
|
300
|
+
lines.push(
|
|
301
|
+
"- Lower `scoreFloorRatio` (try 0.10-0.15) to retain more results"
|
|
302
|
+
);
|
|
303
|
+
lines.push(
|
|
304
|
+
"- Raise `elbowDropRatio` (try 0.70-0.80) to be less aggressive"
|
|
305
|
+
);
|
|
306
|
+
lines.push(
|
|
307
|
+
"- Lower `minMarginalValue` (try 0.00001) to include more sections"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
if (results.aggregate.emptyResultCount > 0) {
|
|
311
|
+
lines.push("");
|
|
312
|
+
lines.push(
|
|
313
|
+
`**Warning:** ${results.aggregate.emptyResultCount} tasks returned zero results. This may indicate FTS5 indexing gaps or overly aggressive filtering.`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
return lines.join("\n");
|
|
317
|
+
}
|
|
318
|
+
export {
|
|
319
|
+
discoverSuites,
|
|
320
|
+
formatExtendedResults,
|
|
321
|
+
runExtendedBenchmark
|
|
322
|
+
};
|
|
323
|
+
//# sourceMappingURL=extended-benchmark-5RUXDG3D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/extended-benchmark.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync, existsSync, mkdirSync, rmSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport {\n loadScoringConfig,\n serializeConfig,\n type ScoringConfig,\n} from \"./config.js\";\n\nconst require = createRequire(import.meta.url);\nconst { getEncoding } = require(\"js-tiktoken\");\n\n// ── types ───────────────────────────────────────────────────────────────\n\nexport interface ExtendedTask {\n id: string;\n question: string;\n queryTerm: string;\n /** Substring patterns that should appear in returned file paths */\n expectedPatterns: string[];\n /** Search terms for explore simulation */\n searchTerms: string[];\n}\n\nexport interface ExtendedSuite {\n id: string;\n name: string;\n /** Absolute path to root directory of the library to index */\n rootDir: string;\n tasks: ExtendedTask[];\n}\n\ninterface ExtendedTaskResult {\n task: ExtendedTask;\n /** Files returned by codefocus query */\n filesReturned: string[];\n /** Tokens output by codefocus query */\n tokensOutput: number;\n /** Tokens consumed by explore simulation */\n exploreTokens: number;\n /** How many expectedPatterns matched at least one returned file */\n patternHits: number;\n /** Total expectedPatterns */\n patternTotal: number;\n /** Pattern hit rate (patternHits / patternTotal) */\n patternRecall: number;\n /** Token savings ratio */\n tokenSavingsRatio: number;\n /** Whether the query returned any results */\n hasResults: boolean;\n /** Number of files indexed for this suite */\n filesIndexed: number;\n}\n\nexport interface ExtendedSuiteResult {\n suite: ExtendedSuite;\n filesIndexed: number;\n symbolsExtracted: number;\n tasks: ExtendedTaskResult[];\n avgPatternRecall: number;\n avgTokenSavingsRatio: number;\n avgTokensOutput: number;\n}\n\nexport interface ExtendedBenchmarkResults {\n suites: ExtendedSuiteResult[];\n config: ScoringConfig;\n aggregate: {\n totalSuites: number;\n totalTasks: number;\n avgPatternRecall: number;\n avgTokenSavingsRatio: number;\n avgTokensOutput: number;\n /** Tasks that returned zero results */\n emptyResultCount: number;\n };\n}\n\n// ── suite definitions ────────────────────────────────────────────────────\n\n/**\n * Discover available benchmark suites from installed dependencies.\n * Returns only suites whose source directories exist.\n */\nexport function discoverSuites(projectRoot: string): ExtendedSuite[] {\n const suites: ExtendedSuite[] = [];\n\n // fast-glob: compiled JS in out/ directory\n const fastGlobRoot = resolve(\n projectRoot,\n \"node_modules/fast-glob/out\",\n );\n if (existsSync(fastGlobRoot)) {\n suites.push({\n id: \"fast-glob\",\n name: \"fast-glob\",\n rootDir: fastGlobRoot,\n tasks: [\n {\n id: \"fg-pattern-matching\",\n question: \"How does fast-glob match file patterns?\",\n queryTerm: \"pattern\",\n expectedPatterns: [\"pattern\"],\n searchTerms: [\"pattern\", \"match\", \"glob\"],\n },\n {\n id: \"fg-sync-reader\",\n question: \"How does the synchronous file reader work?\",\n queryTerm: \"sync\",\n expectedPatterns: [\"sync\"],\n searchTerms: [\"sync\", \"read\"],\n },\n {\n id: \"fg-entry-filter\",\n question: \"How are file entries filtered?\",\n queryTerm: \"filter\",\n expectedPatterns: [\"filter\"],\n searchTerms: [\"filter\", \"entry\"],\n },\n {\n id: \"fg-stream-api\",\n question: \"How does the streaming API work?\",\n queryTerm: \"stream\",\n expectedPatterns: [\"stream\"],\n searchTerms: [\"stream\", \"readable\"],\n },\n ],\n });\n }\n\n // chokidar: small but high symbol density (2 files, ~100 symbols)\n const chokidarRoot = resolve(\n projectRoot,\n \"node_modules/chokidar\",\n );\n if (\n existsSync(chokidarRoot) &&\n existsSync(resolve(chokidarRoot, \"handler.js\"))\n ) {\n suites.push({\n id: \"chokidar\",\n name: \"chokidar\",\n rootDir: chokidarRoot,\n tasks: [\n {\n id: \"chokidar-watch\",\n question: \"How does chokidar watch files?\",\n queryTerm: \"watch\",\n expectedPatterns: [\"index\"],\n searchTerms: [\"watch\", \"FSWatcher\"],\n },\n {\n id: \"chokidar-handler\",\n question: \"How does chokidar handle file system events?\",\n queryTerm: \"handler\",\n expectedPatterns: [\"handler\"],\n searchTerms: [\"handler\", \"event\", \"change\"],\n },\n ],\n });\n }\n\n return suites;\n}\n\n// ── explore simulation ──────────────────────────────────────────────────\n\nfunction simulateExploreForSuite(\n rootDir: string,\n task: ExtendedTask,\n): number {\n const enc = getEncoding(\"cl100k_base\");\n const matchedFiles = new Set<string>();\n\n for (const term of task.searchTerms) {\n try {\n const grepResult = execFileSync(\n \"grep\",\n [\"-rl\", \"--include=*.js\", \"--include=*.ts\", term, rootDir],\n { encoding: \"utf-8\", timeout: 10_000 },\n );\n for (const line of grepResult.trim().split(\"\\n\")) {\n if (line) matchedFiles.add(line);\n }\n } catch {\n // grep exit 1 = no matches\n }\n }\n\n let totalTokens = 0;\n for (const filePath of matchedFiles) {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n totalTokens += enc.encode(content).length;\n } catch {\n // skip\n }\n }\n\n return totalTokens;\n}\n\n// ── codefocus query runner ──────────────────────────────────────────────\n\nfunction runCodefocusQueryForSuite(\n rootDir: string,\n cliPath: string,\n task: ExtendedTask,\n): { tokensOutput: number; filesReturned: string[] } {\n let stdout: string;\n try {\n stdout = execFileSync(\n \"node\",\n [cliPath, \"query\", task.queryTerm, \"--root\", rootDir, \"--budget\", \"8000\"],\n { encoding: \"utf-8\", timeout: 30_000 },\n );\n } catch (err: any) {\n stdout = (err.stdout || \"\") + (err.stderr || \"\");\n }\n\n const tokenMatch = stdout.match(/~(\\d+) tokens/);\n const tokensOutput = tokenMatch ? parseInt(tokenMatch[1], 10) : 0;\n\n const fileMatches = stdout.matchAll(/── ([\\w/.\\-@]+\\.[jt]sx?):\\d+-\\d+/g);\n const filesReturned = [...fileMatches].map((m) => m[1]);\n\n return { tokensOutput, filesReturned };\n}\n\n// ── main ────────────────────────────────────────────────────────────────\n\nexport function runExtendedBenchmark(\n projectRoot: string,\n cliPath: string,\n suites?: ExtendedSuite[],\n): ExtendedBenchmarkResults {\n const config = loadScoringConfig(projectRoot);\n const resolvedSuites = suites ?? discoverSuites(projectRoot);\n const suiteResults: ExtendedSuiteResult[] = [];\n\n for (const suite of resolvedSuites) {\n // Index the library\n let indexStdout: string;\n try {\n indexStdout = execFileSync(\n \"node\",\n [cliPath, \"index\", \"--root\", suite.rootDir],\n { encoding: \"utf-8\", timeout: 60_000 },\n );\n } catch (err: any) {\n indexStdout = (err.stdout || \"\") + (err.stderr || \"\");\n }\n\n const filesMatch = indexStdout.match(/files indexed:\\s*(\\d+)/);\n const symbolsMatch = indexStdout.match(/symbols:\\s+(\\d+)/);\n const filesIndexed = filesMatch ? parseInt(filesMatch[1], 10) : 0;\n const symbolsExtracted = symbolsMatch ? parseInt(symbolsMatch[1], 10) : 0;\n\n // Also count skipped files as indexed (they were indexed in a prior run)\n const skippedMatch = indexStdout.match(/files skipped:\\s*(\\d+)/);\n const totalFiles =\n filesIndexed + (skippedMatch ? parseInt(skippedMatch[1], 10) : 0);\n\n const taskResults: ExtendedTaskResult[] = [];\n\n for (const task of suite.tasks) {\n const exploreTokens = simulateExploreForSuite(suite.rootDir, task);\n const { tokensOutput, filesReturned } = runCodefocusQueryForSuite(\n suite.rootDir,\n cliPath,\n task,\n );\n\n // Check pattern matches — each expectedPattern should match at least one file\n let patternHits = 0;\n for (const pattern of task.expectedPatterns) {\n const hasMatch = filesReturned.some((f) =>\n f.toLowerCase().includes(pattern.toLowerCase()),\n );\n if (hasMatch) patternHits++;\n }\n\n taskResults.push({\n task,\n filesReturned,\n tokensOutput,\n exploreTokens,\n patternHits,\n patternTotal: task.expectedPatterns.length,\n patternRecall:\n task.expectedPatterns.length > 0\n ? patternHits / task.expectedPatterns.length\n : 1,\n tokenSavingsRatio:\n tokensOutput > 0 ? exploreTokens / tokensOutput : 0,\n hasResults: filesReturned.length > 0,\n filesIndexed: totalFiles,\n });\n }\n\n const avg = (arr: number[]) =>\n arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;\n\n suiteResults.push({\n suite,\n filesIndexed: totalFiles,\n symbolsExtracted,\n tasks: taskResults,\n avgPatternRecall: avg(taskResults.map((t) => t.patternRecall)),\n avgTokenSavingsRatio: avg(taskResults.map((t) => t.tokenSavingsRatio)),\n avgTokensOutput: avg(taskResults.map((t) => t.tokensOutput)),\n });\n\n // Clean up the index for this library\n const dbDir = resolve(suite.rootDir, \".codefocus\");\n if (existsSync(dbDir)) {\n try {\n rmSync(dbDir, { recursive: true });\n } catch {\n // ignore cleanup failures\n }\n }\n }\n\n // Aggregate results\n const allTasks = suiteResults.flatMap((s) => s.tasks);\n const avg = (arr: number[]) =>\n arr.length > 0 ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;\n\n return {\n suites: suiteResults,\n config,\n aggregate: {\n totalSuites: suiteResults.length,\n totalTasks: allTasks.length,\n avgPatternRecall: avg(allTasks.map((t) => t.patternRecall)),\n avgTokenSavingsRatio: avg(allTasks.map((t) => t.tokenSavingsRatio)),\n avgTokensOutput: avg(allTasks.map((t) => t.tokensOutput)),\n emptyResultCount: allTasks.filter((t) => !t.hasResults).length,\n },\n };\n}\n\n// ── formatting ──────────────────────────────────────────────────────────\n\nfunction pct(n: number): string {\n return `${Math.round(n * 100)}%`;\n}\n\nexport function formatExtendedResults(\n results: ExtendedBenchmarkResults,\n): string {\n const lines: string[] = [];\n\n lines.push(\"# codefocus Extended Benchmark — Spike 9\");\n lines.push(\"\");\n lines.push(\n \"Cross-codebase validation of scoring parameters against dependency libraries.\",\n );\n lines.push(`Benchmark run: ${new Date().toISOString()}`);\n lines.push(\"\");\n\n // ── aggregate summary ──────────────────────────────────────────────\n\n lines.push(\"## Aggregate Summary\");\n lines.push(\"\");\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|------:|`);\n lines.push(\n `| Libraries tested | ${results.aggregate.totalSuites} |`,\n );\n lines.push(`| Total tasks | ${results.aggregate.totalTasks} |`);\n lines.push(\n `| Avg pattern recall | ${pct(results.aggregate.avgPatternRecall)} |`,\n );\n lines.push(\n `| Avg token savings | ${results.aggregate.avgTokenSavingsRatio.toFixed(1)}x |`,\n );\n lines.push(\n `| Avg tokens output | ${Math.round(results.aggregate.avgTokensOutput)} |`,\n );\n lines.push(\n `| Empty results | ${results.aggregate.emptyResultCount}/${results.aggregate.totalTasks} |`,\n );\n lines.push(\"\");\n\n // ── per-suite results ──────────────────────────────────────────────\n\n for (const suite of results.suites) {\n lines.push(`## ${suite.suite.name}`);\n lines.push(\"\");\n lines.push(\n `Indexed: ${suite.filesIndexed} files, ${suite.symbolsExtracted} symbols`,\n );\n lines.push(\"\");\n lines.push(\n \"| Task | CF tokens | Explore tokens | Savings | Pattern recall | Files returned |\",\n );\n lines.push(\n \"|------|----------:|---------------:|--------:|---------------:|---------------:|\",\n );\n\n for (const t of suite.tasks) {\n const savings =\n t.tokenSavingsRatio > 0\n ? t.tokenSavingsRatio.toFixed(1) + \"x\"\n : \"—\";\n lines.push(\n `| ${t.task.id} | ${t.tokensOutput} | ${t.exploreTokens} | ${savings} | ${pct(t.patternRecall)} | ${t.filesReturned.length} |`,\n );\n }\n\n lines.push(\"\");\n lines.push(\n `**Suite averages:** pattern recall=${pct(suite.avgPatternRecall)}, ` +\n `savings=${suite.avgTokenSavingsRatio.toFixed(1)}x, ` +\n `tokens=${Math.round(suite.avgTokensOutput)}`,\n );\n lines.push(\"\");\n }\n\n // ── scoring config ─────────────────────────────────────────────────\n\n lines.push(\"## Active Scoring Config\");\n lines.push(\"\");\n lines.push(\"```json\");\n lines.push(serializeConfig(results.config));\n lines.push(\"```\");\n lines.push(\"\");\n\n // ── overfitting analysis ───────────────────────────────────────────\n\n lines.push(\"## Overfitting Analysis\");\n lines.push(\"\");\n\n const recall = results.aggregate.avgPatternRecall;\n if (recall >= 0.7) {\n lines.push(\n `Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks ` +\n `suggests scoring parameters generalize well.`,\n );\n } else if (recall >= 0.4) {\n lines.push(\n `Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks ` +\n `is moderate — scoring parameters may need calibration for larger codebases.`,\n );\n lines.push(\"\");\n lines.push(\"Parameters most likely to need adjustment:\");\n lines.push(\n `- \\`scoreFloorRatio\\` (${results.config.scoreFloorRatio}) — may over-prune in larger codebases`,\n );\n lines.push(\n `- \\`elbowDropRatio\\` (${results.config.elbowDropRatio}) — may cut too aggressively`,\n );\n } else {\n lines.push(\n `Pattern recall ${pct(recall)} across ${results.aggregate.totalTasks} cross-codebase tasks ` +\n `indicates likely overfitting to the original 16-file codebase.`,\n );\n lines.push(\"\");\n lines.push(\"**Recommended actions:**\");\n lines.push(\n \"- Lower `scoreFloorRatio` (try 0.10-0.15) to retain more results\",\n );\n lines.push(\n \"- Raise `elbowDropRatio` (try 0.70-0.80) to be less aggressive\",\n );\n lines.push(\n \"- Lower `minMarginalValue` (try 0.00001) to include more sections\",\n );\n }\n\n if (results.aggregate.emptyResultCount > 0) {\n lines.push(\"\");\n lines.push(\n `**Warning:** ${results.aggregate.emptyResultCount} tasks returned zero results. ` +\n `This may indicate FTS5 indexing gaps or overly aggressive filtering.`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["require","avg"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
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/commands/find.ts
|
|
10
|
+
import { resolve } from "path";
|
|
11
|
+
import { existsSync } from "fs";
|
|
12
|
+
async function runFind(positional, flags) {
|
|
13
|
+
if (flags.help) {
|
|
14
|
+
console.log(`codefocus find \u2014 Quick symbol lookup
|
|
15
|
+
|
|
16
|
+
Usage: codefocus find <symbol-name> [options]
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--kind <type> Filter by kind: function, class, interface, type, enum, variable, method, all (default: all)
|
|
20
|
+
--root <path> Root directory of indexed project (default: .)
|
|
21
|
+
--help Show this help message`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const symbol = positional[0];
|
|
25
|
+
if (!symbol) {
|
|
26
|
+
console.error("Error: find requires a symbol name");
|
|
27
|
+
process.exitCode = 2;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const kind = flags.kind || "all";
|
|
31
|
+
const root = resolveRoot(flags.root);
|
|
32
|
+
const dbPath = resolve(root, ".codefocus", "index.db");
|
|
33
|
+
if (!existsSync(dbPath)) {
|
|
34
|
+
console.error(
|
|
35
|
+
`Error: no index found at ${dbPath}
|
|
36
|
+
Run 'codefocus index --root ${root}' first.`
|
|
37
|
+
);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const db = new IndexDatabase(dbPath);
|
|
42
|
+
try {
|
|
43
|
+
const results = db.findSymbols(symbol, kind);
|
|
44
|
+
if (results.length === 0) {
|
|
45
|
+
console.log(`No symbols found matching "${symbol}"${kind !== "all" ? ` (kind: ${kind})` : ""}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log(
|
|
49
|
+
`Found ${results.length} symbol${results.length !== 1 ? "s" : ""} matching "${symbol}"${kind !== "all" ? ` (kind: ${kind})` : ""}:
|
|
50
|
+
`
|
|
51
|
+
);
|
|
52
|
+
for (const sym of results) {
|
|
53
|
+
const sig = sym.signature ? ` ${sym.signature}` : "";
|
|
54
|
+
console.log(` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`);
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
db.close();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
runFind
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=find-W5UDE4US.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/find.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { IndexDatabase } from \"../db.js\";\nimport { resolveRoot } from \"../root.js\";\n\nexport async function runFind(\n positional: string[],\n flags: Record<string, string | boolean>,\n): Promise<void> {\n if (flags.help) {\n console.log(`codefocus find — Quick symbol lookup\n\nUsage: codefocus find <symbol-name> [options]\n\nOptions:\n --kind <type> Filter by kind: function, class, interface, type, enum, variable, method, all (default: all)\n --root <path> Root directory of indexed project (default: .)\n --help Show this help message`);\n return;\n }\n\n const symbol = positional[0];\n if (!symbol) {\n console.error(\"Error: find requires a symbol name\");\n process.exitCode = 2;\n return;\n }\n\n const kind = (flags.kind as string) || \"all\";\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 const db = new IndexDatabase(dbPath);\n try {\n const results = db.findSymbols(symbol, kind);\n\n if (results.length === 0) {\n console.log(`No symbols found matching \"${symbol}\"${kind !== \"all\" ? ` (kind: ${kind})` : \"\"}`);\n return;\n }\n\n console.log(\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 console.log(` ${sym.name} ${sym.kind} ${sym.file_path}:${sym.start_line}${sig}`);\n }\n } finally {\n db.close();\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAI3B,eAAsB,QACpB,YACA,OACe;AACf,MAAI,MAAM,MAAM;AACd,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAOwB;AACpC;AAAA,EACF;AAEA,QAAM,SAAS,WAAW,CAAC;AAC3B,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,oCAAoC;AAClD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,OAAQ,MAAM,QAAmB;AACvC,QAAM,OAAO,YAAY,MAAM,IAAI;AACnC,QAAM,SAAS,QAAQ,MAAM,cAAc,UAAU;AAErD,MAAI,CAAC,WAAW,MAAM,GAAG;AACvB,YAAQ;AAAA,MACN,4BAA4B,MAAM;AAAA,8BAAiC,IAAI;AAAA,IACzE;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,cAAc,MAAM;AACnC,MAAI;AACF,UAAM,UAAU,GAAG,YAAY,QAAQ,IAAI;AAE3C,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,8BAA8B,MAAM,IAAI,SAAS,QAAQ,WAAW,IAAI,MAAM,EAAE,EAAE;AAC9F;AAAA,IACF;AAEA,YAAQ;AAAA,MACN,SAAS,QAAQ,MAAM,UAAU,QAAQ,WAAW,IAAI,MAAM,EAAE,cAAc,MAAM,IAAI,SAAS,QAAQ,WAAW,IAAI,MAAM,EAAE;AAAA;AAAA,IAClI;AAEA,eAAW,OAAO,SAAS;AACzB,YAAM,MAAM,IAAI,YAAY,KAAK,IAAI,SAAS,KAAK;AACnD,cAAQ,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,IAAI,IAAI,UAAU,GAAG,GAAG,EAAE;AAAA,IACpF;AAAA,EACF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;","names":[]}
|