@stupidloud/codegraph 0.7.20 → 0.9.5
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.
- package/README.md +127 -106
- package/dist/bin/codegraph.d.ts +4 -0
- package/dist/bin/codegraph.d.ts.map +1 -1
- package/dist/bin/codegraph.js +327 -8
- package/dist/bin/codegraph.js.map +1 -1
- package/dist/bin/node-version-check.d.ts +17 -0
- package/dist/bin/node-version-check.d.ts.map +1 -1
- package/dist/bin/node-version-check.js +37 -0
- package/dist/bin/node-version-check.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -11
- package/dist/config.js.map +1 -1
- package/dist/db/index.d.ts +30 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +75 -25
- package/dist/db/index.js.map +1 -1
- package/dist/db/queries.d.ts +16 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +80 -27
- package/dist/db/queries.js.map +1 -1
- package/dist/db/sqlite-adapter.d.ts +17 -23
- package/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/dist/db/sqlite-adapter.js +51 -174
- package/dist/db/sqlite-adapter.js.map +1 -1
- package/dist/extraction/grammars.d.ts +7 -1
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +42 -2
- package/dist/extraction/grammars.js.map +1 -1
- package/dist/extraction/index.d.ts +9 -14
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +131 -124
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/index.d.ts.map +1 -1
- package/dist/extraction/languages/index.js +4 -0
- package/dist/extraction/languages/index.js.map +1 -1
- package/dist/extraction/languages/lua.d.ts +3 -0
- package/dist/extraction/languages/lua.d.ts.map +1 -0
- package/dist/extraction/languages/lua.js +150 -0
- package/dist/extraction/languages/lua.js.map +1 -0
- package/dist/extraction/languages/luau.d.ts +3 -0
- package/dist/extraction/languages/luau.d.ts.map +1 -0
- package/dist/extraction/languages/luau.js +37 -0
- package/dist/extraction/languages/luau.js.map +1 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +38 -0
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
- package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
- package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
- package/dist/extraction/wasm-runtime-flags.js +105 -0
- package/dist/extraction/wasm-runtime-flags.js.map +1 -0
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +71 -36
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -18
- package/dist/index.js.map +1 -1
- package/dist/installer/config-writer.d.ts.map +1 -1
- package/dist/installer/config-writer.js +3 -1
- package/dist/installer/config-writer.js.map +1 -1
- package/dist/installer/index.d.ts +66 -2
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +195 -5
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/instructions-template.d.ts +2 -2
- package/dist/installer/instructions-template.d.ts.map +1 -1
- package/dist/installer/instructions-template.js +4 -2
- package/dist/installer/instructions-template.js.map +1 -1
- package/dist/installer/targets/claude.d.ts +26 -6
- package/dist/installer/targets/claude.d.ts.map +1 -1
- package/dist/installer/targets/claude.js +165 -10
- package/dist/installer/targets/claude.js.map +1 -1
- package/dist/installer/targets/cursor.d.ts.map +1 -1
- package/dist/installer/targets/cursor.js +57 -3
- package/dist/installer/targets/cursor.js.map +1 -1
- package/dist/installer/targets/hermes.d.ts +18 -0
- package/dist/installer/targets/hermes.d.ts.map +1 -0
- package/dist/installer/targets/hermes.js +305 -0
- package/dist/installer/targets/hermes.js.map +1 -0
- package/dist/installer/targets/registry.d.ts.map +1 -1
- package/dist/installer/targets/registry.js +2 -0
- package/dist/installer/targets/registry.js.map +1 -1
- package/dist/installer/targets/types.d.ts +1 -1
- package/dist/installer/targets/types.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +213 -18
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +15 -0
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +25 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +221 -30
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/transport.d.ts +17 -0
- package/dist/mcp/transport.d.ts.map +1 -1
- package/dist/mcp/transport.js +63 -0
- package/dist/mcp/transport.js.map +1 -1
- package/dist/resolution/frameworks/drupal.d.ts +51 -0
- package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
- package/dist/resolution/frameworks/drupal.js +335 -0
- package/dist/resolution/frameworks/drupal.js.map +1 -0
- package/dist/resolution/frameworks/index.d.ts +2 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/dist/resolution/frameworks/index.js +9 -1
- package/dist/resolution/frameworks/index.js.map +1 -1
- package/dist/resolution/frameworks/nestjs.d.ts +26 -0
- package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
- package/dist/resolution/frameworks/nestjs.js +374 -0
- package/dist/resolution/frameworks/nestjs.js.map +1 -0
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +40 -7
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/lru-cache.d.ts +24 -0
- package/dist/resolution/lru-cache.d.ts.map +1 -0
- package/dist/resolution/lru-cache.js +62 -0
- package/dist/resolution/lru-cache.js.map +1 -0
- package/dist/sync/git-hooks.d.ts +45 -0
- package/dist/sync/git-hooks.d.ts.map +1 -0
- package/dist/sync/git-hooks.js +223 -0
- package/dist/sync/git-hooks.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +12 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/watch-policy.d.ts +48 -0
- package/dist/sync/watch-policy.d.ts.map +1 -0
- package/dist/sync/watch-policy.js +124 -0
- package/dist/sync/watch-policy.js.map +1 -0
- package/dist/sync/watcher.d.ts +2 -4
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +14 -6
- package/dist/sync/watcher.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.js +1 -1
- package/package.json +4 -4
- package/scripts/add-lang/bench.sh +60 -0
- package/scripts/add-lang/check-grammar.mjs +75 -0
- package/scripts/add-lang/dump-ast.mjs +103 -0
- package/scripts/add-lang/verify-extraction.mjs +70 -0
- package/scripts/agent-eval/audit.sh +68 -0
- package/scripts/agent-eval/itrun.sh +1 -1
- package/scripts/agent-eval/run-all.sh +67 -0
- package/scripts/build-bundle.sh +118 -0
- package/scripts/npm-shim.js +246 -0
- package/scripts/pack-npm.sh +95 -0
- package/scripts/patch-tree-sitter-dart.js +0 -112
- package/scripts/release.sh +0 -68
package/dist/bin/codegraph.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Usage:
|
|
9
9
|
* codegraph Run interactive installer (when no args)
|
|
10
10
|
* codegraph install Run interactive installer
|
|
11
|
+
* codegraph uninstall Remove CodeGraph from your agents
|
|
11
12
|
* codegraph init [path] Initialize CodeGraph in a project
|
|
12
13
|
* codegraph uninit [path] Remove CodeGraph from a project
|
|
13
14
|
* codegraph index [path] Index all files in the project
|
|
@@ -16,6 +17,9 @@
|
|
|
16
17
|
* codegraph query <search> Search for symbols
|
|
17
18
|
* codegraph files [options] Show project file structure
|
|
18
19
|
* codegraph context <task> Build context for a task
|
|
20
|
+
* codegraph callers <symbol> Find what calls a function/method
|
|
21
|
+
* codegraph callees <symbol> Find what a function/method calls
|
|
22
|
+
* codegraph impact <symbol> Analyze what code is affected by changing a symbol
|
|
19
23
|
* codegraph affected [files] Find test files affected by changes
|
|
20
24
|
*/
|
|
21
25
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -60,6 +64,7 @@ const directory_1 = require("../directory");
|
|
|
60
64
|
const shimmer_progress_1 = require("../ui/shimmer-progress");
|
|
61
65
|
const glyphs_1 = require("../ui/glyphs");
|
|
62
66
|
const node_version_check_1 = require("./node-version-check");
|
|
67
|
+
const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
|
|
63
68
|
// Lazy-load heavy modules (CodeGraph, runInstaller) to keep CLI startup fast.
|
|
64
69
|
async function loadCodeGraph() {
|
|
65
70
|
try {
|
|
@@ -94,6 +99,22 @@ if (nodeMajor >= 25) {
|
|
|
94
99
|
}
|
|
95
100
|
// Override active — banner shown for visibility, continuing.
|
|
96
101
|
}
|
|
102
|
+
// Enforce the supported Node floor. `engines` in package.json only *warns* on
|
|
103
|
+
// install (unless engine-strict), so hard-block here to actually keep users off
|
|
104
|
+
// unsupported versions. Mirrors the 25+ block above. See package.json `engines`.
|
|
105
|
+
if (nodeMajor < node_version_check_1.MIN_NODE_MAJOR) {
|
|
106
|
+
process.stderr.write((0, node_version_check_1.buildNodeTooOldBanner)(nodeVersion) + '\n');
|
|
107
|
+
if (!process.env.CODEGRAPH_ALLOW_UNSAFE_NODE) {
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
// Override active — banner shown for visibility, continuing.
|
|
111
|
+
}
|
|
112
|
+
// Re-exec with V8's `--liftoff-only` if it isn't already set, so tree-sitter's
|
|
113
|
+
// large WASM grammars never hit the turboshaft Zone OOM (`Fatal process out of
|
|
114
|
+
// memory: Zone`) on Node >= 22. No-op under the bundled launcher, which already
|
|
115
|
+
// passes the flag. Must run before any grammar (in the parse worker, which
|
|
116
|
+
// inherits this process's flags) is compiled. See ../extraction/wasm-runtime-flags.
|
|
117
|
+
(0, wasm_runtime_flags_1.relaunchWithWasmRuntimeFlagsIfNeeded)(__filename);
|
|
97
118
|
// Check if running with no arguments - run installer
|
|
98
119
|
if (process.argv.length === 2) {
|
|
99
120
|
Promise.resolve().then(() => __importStar(require('../installer'))).then(({ runInstaller }) => runInstaller()).catch((err) => {
|
|
@@ -402,6 +423,11 @@ function main() {
|
|
|
402
423
|
}
|
|
403
424
|
}
|
|
404
425
|
catch { /* non-fatal */ }
|
|
426
|
+
try {
|
|
427
|
+
const { offerWatchFallback } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
428
|
+
await offerWatchFallback(clack, projectPath);
|
|
429
|
+
}
|
|
430
|
+
catch { /* non-fatal */ }
|
|
405
431
|
clack.outro('');
|
|
406
432
|
return;
|
|
407
433
|
}
|
|
@@ -448,6 +474,11 @@ function main() {
|
|
|
448
474
|
else {
|
|
449
475
|
clack.log.info('Run "codegraph index" to index the project');
|
|
450
476
|
}
|
|
477
|
+
try {
|
|
478
|
+
const { offerWatchFallback } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
479
|
+
await offerWatchFallback(clack, projectPath);
|
|
480
|
+
}
|
|
481
|
+
catch { /* non-fatal */ }
|
|
451
482
|
clack.outro('Done');
|
|
452
483
|
cg.destroy();
|
|
453
484
|
}
|
|
@@ -486,6 +517,15 @@ function main() {
|
|
|
486
517
|
const { default: CodeGraph } = await loadCodeGraph();
|
|
487
518
|
const cg = CodeGraph.openSync(projectPath);
|
|
488
519
|
cg.uninitialize();
|
|
520
|
+
// Clean up any git sync hooks we installed (no-op if none / not a repo).
|
|
521
|
+
try {
|
|
522
|
+
const { removeGitSyncHook } = await Promise.resolve().then(() => __importStar(require('../sync/git-hooks')));
|
|
523
|
+
const removed = removeGitSyncHook(projectPath);
|
|
524
|
+
if (removed.installed.length > 0) {
|
|
525
|
+
info(`Removed git ${removed.installed.join(', ')} sync hook${removed.installed.length > 1 ? 's' : ''}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
catch { /* non-fatal */ }
|
|
489
529
|
success(`Removed CodeGraph from ${projectPath}`);
|
|
490
530
|
}
|
|
491
531
|
catch (err) {
|
|
@@ -637,6 +677,7 @@ function main() {
|
|
|
637
677
|
const stats = cg.getStats();
|
|
638
678
|
const changes = cg.getChangedFiles();
|
|
639
679
|
const backend = cg.getBackend();
|
|
680
|
+
const journalMode = cg.getJournalMode();
|
|
640
681
|
// JSON output mode
|
|
641
682
|
if (options.json) {
|
|
642
683
|
console.log(JSON.stringify({
|
|
@@ -647,6 +688,7 @@ function main() {
|
|
|
647
688
|
edgeCount: stats.edgeCount,
|
|
648
689
|
dbSizeBytes: stats.dbSizeBytes,
|
|
649
690
|
backend,
|
|
691
|
+
journalMode,
|
|
650
692
|
nodesByKind: stats.nodesByKind,
|
|
651
693
|
languages: Object.entries(stats.filesByLanguage).filter(([, count]) => count > 0).map(([lang]) => lang),
|
|
652
694
|
pendingChanges: {
|
|
@@ -668,14 +710,18 @@ function main() {
|
|
|
668
710
|
console.log(` Nodes: ${formatNumber(stats.nodeCount)}`);
|
|
669
711
|
console.log(` Edges: ${formatNumber(stats.edgeCount)}`);
|
|
670
712
|
console.log(` DB Size: ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
671
|
-
// Surface the active SQLite backend
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
// when the native build fails.
|
|
675
|
-
const backendLabel = backend === 'native'
|
|
676
|
-
? chalk.green('native')
|
|
677
|
-
: chalk.yellow(`wasm ${(0, glyphs_1.getGlyphs)().dash} slower fallback; run \`npm rebuild better-sqlite3\``);
|
|
713
|
+
// Surface the active SQLite backend (node:sqlite — Node's built-in real
|
|
714
|
+
// SQLite, full WAL + FTS5, no native build).
|
|
715
|
+
const backendLabel = chalk.green(`node:sqlite ${(0, glyphs_1.getGlyphs)().dash} built-in (full WAL)`);
|
|
678
716
|
console.log(` Backend: ${backendLabel}`);
|
|
717
|
+
// Effective journal mode: 'wal' means concurrent reads never block on a
|
|
718
|
+
// writer; anything else means they can ("database is locked"). node:sqlite
|
|
719
|
+
// supports WAL everywhere, so a non-wal mode means the filesystem can't
|
|
720
|
+
// (network mounts, WSL2 /mnt). See issue #238.
|
|
721
|
+
const journalLabel = journalMode === 'wal'
|
|
722
|
+
? chalk.green('wal')
|
|
723
|
+
: chalk.yellow(`${journalMode || 'unknown'} ${(0, glyphs_1.getGlyphs)().dash} WAL inactive; reads can block on writes`);
|
|
724
|
+
console.log(` Journal: ${journalLabel}`);
|
|
679
725
|
console.log();
|
|
680
726
|
// Node breakdown
|
|
681
727
|
console.log(chalk.bold('Nodes by Kind:'));
|
|
@@ -988,8 +1034,14 @@ function main() {
|
|
|
988
1034
|
.description('Start CodeGraph as an MCP server for AI assistants')
|
|
989
1035
|
.option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
|
|
990
1036
|
.option('--mcp', 'Run as MCP server (stdio transport)')
|
|
1037
|
+
.option('--no-watch', 'Disable the file watcher (no auto-sync; useful on slow filesystems like WSL2 /mnt drives)')
|
|
991
1038
|
.action(async (options) => {
|
|
992
1039
|
const projectPath = options.path ? resolveProjectPath(options.path) : undefined;
|
|
1040
|
+
// Commander sets watch=false when --no-watch is passed. Route it through
|
|
1041
|
+
// the same env-var chokepoint the watcher and MCP server already honor.
|
|
1042
|
+
if (options.watch === false) {
|
|
1043
|
+
process.env.CODEGRAPH_NO_WATCH = '1';
|
|
1044
|
+
}
|
|
993
1045
|
try {
|
|
994
1046
|
if (options.mcp) {
|
|
995
1047
|
// Start MCP server - it handles initialization lazily based on rootUri from client
|
|
@@ -1189,6 +1241,241 @@ function main() {
|
|
|
1189
1241
|
process.exit(1);
|
|
1190
1242
|
}
|
|
1191
1243
|
});
|
|
1244
|
+
/**
|
|
1245
|
+
* codegraph callers <symbol>
|
|
1246
|
+
*
|
|
1247
|
+
* CLI parity with the MCP graph tools (codegraph_callers/callees/impact) so the
|
|
1248
|
+
* traversal queries work in scripts, CI, and git hooks without a running MCP
|
|
1249
|
+
* server.
|
|
1250
|
+
*/
|
|
1251
|
+
program
|
|
1252
|
+
.command('callers <symbol>')
|
|
1253
|
+
.description('Find all functions/methods that call a specific symbol')
|
|
1254
|
+
.option('-p, --path <path>', 'Project path')
|
|
1255
|
+
.option('-l, --limit <number>', 'Maximum results', '20')
|
|
1256
|
+
.option('-j, --json', 'Output as JSON')
|
|
1257
|
+
.action(async (symbol, options) => {
|
|
1258
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1259
|
+
try {
|
|
1260
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1261
|
+
error(`CodeGraph not initialized in ${projectPath}`);
|
|
1262
|
+
process.exit(1);
|
|
1263
|
+
}
|
|
1264
|
+
const { default: CodeGraph } = await loadCodeGraph();
|
|
1265
|
+
const cg = await CodeGraph.open(projectPath);
|
|
1266
|
+
const limit = parseInt(options.limit || '20', 10);
|
|
1267
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1268
|
+
if (matches.length === 0) {
|
|
1269
|
+
info(`Symbol "${symbol}" not found`);
|
|
1270
|
+
cg.destroy();
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const seen = new Set();
|
|
1274
|
+
const allCallers = [];
|
|
1275
|
+
for (const match of matches) {
|
|
1276
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1277
|
+
if (!exactMatch && matches.length > 1)
|
|
1278
|
+
continue;
|
|
1279
|
+
for (const c of cg.getCallers(match.node.id)) {
|
|
1280
|
+
if (!seen.has(c.node.id)) {
|
|
1281
|
+
seen.add(c.node.id);
|
|
1282
|
+
allCallers.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
// Fallback: if exact filter removed everything, use the top match
|
|
1287
|
+
if (allCallers.length === 0 && matches[0]) {
|
|
1288
|
+
for (const c of cg.getCallers(matches[0].node.id)) {
|
|
1289
|
+
if (!seen.has(c.node.id)) {
|
|
1290
|
+
seen.add(c.node.id);
|
|
1291
|
+
allCallers.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
const limited = allCallers.slice(0, limit);
|
|
1296
|
+
if (options.json) {
|
|
1297
|
+
console.log(JSON.stringify({ symbol, callers: limited }, null, 2));
|
|
1298
|
+
}
|
|
1299
|
+
else if (limited.length === 0) {
|
|
1300
|
+
info(`No callers found for "${symbol}"`);
|
|
1301
|
+
}
|
|
1302
|
+
else {
|
|
1303
|
+
console.log(chalk.bold(`\nCallers of "${symbol}" (${limited.length}):\n`));
|
|
1304
|
+
for (const node of limited) {
|
|
1305
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1306
|
+
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
1307
|
+
chalk.white(node.name));
|
|
1308
|
+
console.log(chalk.dim(` ${node.filePath}${loc}`));
|
|
1309
|
+
console.log();
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
cg.destroy();
|
|
1313
|
+
}
|
|
1314
|
+
catch (err) {
|
|
1315
|
+
error(`callers failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1316
|
+
process.exit(1);
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
/**
|
|
1320
|
+
* codegraph callees <symbol>
|
|
1321
|
+
*/
|
|
1322
|
+
program
|
|
1323
|
+
.command('callees <symbol>')
|
|
1324
|
+
.description('Find all functions/methods that a specific symbol calls')
|
|
1325
|
+
.option('-p, --path <path>', 'Project path')
|
|
1326
|
+
.option('-l, --limit <number>', 'Maximum results', '20')
|
|
1327
|
+
.option('-j, --json', 'Output as JSON')
|
|
1328
|
+
.action(async (symbol, options) => {
|
|
1329
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1330
|
+
try {
|
|
1331
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1332
|
+
error(`CodeGraph not initialized in ${projectPath}`);
|
|
1333
|
+
process.exit(1);
|
|
1334
|
+
}
|
|
1335
|
+
const { default: CodeGraph } = await loadCodeGraph();
|
|
1336
|
+
const cg = await CodeGraph.open(projectPath);
|
|
1337
|
+
const limit = parseInt(options.limit || '20', 10);
|
|
1338
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1339
|
+
if (matches.length === 0) {
|
|
1340
|
+
info(`Symbol "${symbol}" not found`);
|
|
1341
|
+
cg.destroy();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
const seen = new Set();
|
|
1345
|
+
const allCallees = [];
|
|
1346
|
+
for (const match of matches) {
|
|
1347
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1348
|
+
if (!exactMatch && matches.length > 1)
|
|
1349
|
+
continue;
|
|
1350
|
+
for (const c of cg.getCallees(match.node.id)) {
|
|
1351
|
+
if (!seen.has(c.node.id)) {
|
|
1352
|
+
seen.add(c.node.id);
|
|
1353
|
+
allCallees.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
if (allCallees.length === 0 && matches[0]) {
|
|
1358
|
+
for (const c of cg.getCallees(matches[0].node.id)) {
|
|
1359
|
+
if (!seen.has(c.node.id)) {
|
|
1360
|
+
seen.add(c.node.id);
|
|
1361
|
+
allCallees.push({ name: c.node.name, kind: c.node.kind, filePath: c.node.filePath, startLine: c.node.startLine });
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
const limited = allCallees.slice(0, limit);
|
|
1366
|
+
if (options.json) {
|
|
1367
|
+
console.log(JSON.stringify({ symbol, callees: limited }, null, 2));
|
|
1368
|
+
}
|
|
1369
|
+
else if (limited.length === 0) {
|
|
1370
|
+
info(`No callees found for "${symbol}"`);
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
console.log(chalk.bold(`\nCallees of "${symbol}" (${limited.length}):\n`));
|
|
1374
|
+
for (const node of limited) {
|
|
1375
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1376
|
+
console.log(chalk.cyan(node.kind.padEnd(12)) +
|
|
1377
|
+
chalk.white(node.name));
|
|
1378
|
+
console.log(chalk.dim(` ${node.filePath}${loc}`));
|
|
1379
|
+
console.log();
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
cg.destroy();
|
|
1383
|
+
}
|
|
1384
|
+
catch (err) {
|
|
1385
|
+
error(`callees failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1386
|
+
process.exit(1);
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
/**
|
|
1390
|
+
* codegraph impact <symbol>
|
|
1391
|
+
*/
|
|
1392
|
+
program
|
|
1393
|
+
.command('impact <symbol>')
|
|
1394
|
+
.description('Analyze what code is affected by changing a symbol')
|
|
1395
|
+
.option('-p, --path <path>', 'Project path')
|
|
1396
|
+
.option('-d, --depth <number>', 'Traversal depth', '2')
|
|
1397
|
+
.option('-j, --json', 'Output as JSON')
|
|
1398
|
+
.action(async (symbol, options) => {
|
|
1399
|
+
const projectPath = resolveProjectPath(options.path);
|
|
1400
|
+
try {
|
|
1401
|
+
if (!(0, directory_1.isInitialized)(projectPath)) {
|
|
1402
|
+
error(`CodeGraph not initialized in ${projectPath}`);
|
|
1403
|
+
process.exit(1);
|
|
1404
|
+
}
|
|
1405
|
+
const { default: CodeGraph } = await loadCodeGraph();
|
|
1406
|
+
const cg = await CodeGraph.open(projectPath);
|
|
1407
|
+
const depth = Math.min(Math.max(parseInt(options.depth || '2', 10), 1), 10);
|
|
1408
|
+
const matches = cg.searchNodes(symbol, { limit: 50 });
|
|
1409
|
+
if (matches.length === 0) {
|
|
1410
|
+
info(`Symbol "${symbol}" not found`);
|
|
1411
|
+
cg.destroy();
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
// Merge impact subgraphs across all exact-matching symbols
|
|
1415
|
+
const mergedNodes = new Map();
|
|
1416
|
+
const seenEdges = new Set();
|
|
1417
|
+
let edgeCount = 0;
|
|
1418
|
+
for (const match of matches) {
|
|
1419
|
+
const exactMatch = match.node.name === symbol || match.node.name.endsWith(`.${symbol}`) || match.node.name.endsWith(`::${symbol}`);
|
|
1420
|
+
if (!exactMatch && matches.length > 1)
|
|
1421
|
+
continue;
|
|
1422
|
+
const impact = cg.getImpactRadius(match.node.id, depth);
|
|
1423
|
+
for (const [id, n] of impact.nodes) {
|
|
1424
|
+
mergedNodes.set(id, { name: n.name, kind: n.kind, filePath: n.filePath, startLine: n.startLine });
|
|
1425
|
+
}
|
|
1426
|
+
for (const e of impact.edges) {
|
|
1427
|
+
const key = `${e.source}->${e.target}:${e.kind}`;
|
|
1428
|
+
if (!seenEdges.has(key)) {
|
|
1429
|
+
seenEdges.add(key);
|
|
1430
|
+
edgeCount++;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
// Fallback to top match if exact filter removed everything
|
|
1435
|
+
if (mergedNodes.size === 0 && matches[0]) {
|
|
1436
|
+
const impact = cg.getImpactRadius(matches[0].node.id, depth);
|
|
1437
|
+
for (const [id, n] of impact.nodes) {
|
|
1438
|
+
mergedNodes.set(id, { name: n.name, kind: n.kind, filePath: n.filePath, startLine: n.startLine });
|
|
1439
|
+
}
|
|
1440
|
+
edgeCount = impact.edges.length;
|
|
1441
|
+
}
|
|
1442
|
+
if (options.json) {
|
|
1443
|
+
console.log(JSON.stringify({
|
|
1444
|
+
symbol,
|
|
1445
|
+
depth,
|
|
1446
|
+
nodeCount: mergedNodes.size,
|
|
1447
|
+
edgeCount,
|
|
1448
|
+
affected: Array.from(mergedNodes.values()),
|
|
1449
|
+
}, null, 2));
|
|
1450
|
+
}
|
|
1451
|
+
else if (mergedNodes.size === 0) {
|
|
1452
|
+
info(`No affected symbols found for "${symbol}"`);
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
console.log(chalk.bold(`\nImpact of changing "${symbol}" — ${mergedNodes.size} affected symbols:\n`));
|
|
1456
|
+
// Group by file
|
|
1457
|
+
const byFile = new Map();
|
|
1458
|
+
for (const node of mergedNodes.values()) {
|
|
1459
|
+
const list = byFile.get(node.filePath) || [];
|
|
1460
|
+
list.push({ name: node.name, kind: node.kind, startLine: node.startLine });
|
|
1461
|
+
byFile.set(node.filePath, list);
|
|
1462
|
+
}
|
|
1463
|
+
for (const [file, nodes] of byFile) {
|
|
1464
|
+
console.log(chalk.cyan(file));
|
|
1465
|
+
for (const node of nodes) {
|
|
1466
|
+
const loc = node.startLine ? `:${node.startLine}` : '';
|
|
1467
|
+
console.log(` ${chalk.dim(node.kind.padEnd(12))}${node.name}${chalk.dim(loc)}`);
|
|
1468
|
+
}
|
|
1469
|
+
console.log();
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
cg.destroy();
|
|
1473
|
+
}
|
|
1474
|
+
catch (err) {
|
|
1475
|
+
error(`impact failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1476
|
+
process.exit(1);
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1192
1479
|
/**
|
|
1193
1480
|
* codegraph affected [files...]
|
|
1194
1481
|
*
|
|
@@ -1324,7 +1611,7 @@ function main() {
|
|
|
1324
1611
|
*/
|
|
1325
1612
|
program
|
|
1326
1613
|
.command('install')
|
|
1327
|
-
.description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode)')
|
|
1614
|
+
.description('Install codegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
|
|
1328
1615
|
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
|
|
1329
1616
|
.option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
|
|
1330
1617
|
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
|
|
@@ -1373,6 +1660,38 @@ function main() {
|
|
|
1373
1660
|
process.exit(1);
|
|
1374
1661
|
}
|
|
1375
1662
|
});
|
|
1663
|
+
/**
|
|
1664
|
+
* codegraph uninstall
|
|
1665
|
+
*
|
|
1666
|
+
* Inverse of `install`. Removes the codegraph MCP server entry,
|
|
1667
|
+
* instructions block, and permissions from every agent (or a
|
|
1668
|
+
* `--target` subset). Prompts global-vs-local when not given. Does NOT
|
|
1669
|
+
* delete the `.codegraph/` index — that's `codegraph uninit`.
|
|
1670
|
+
*/
|
|
1671
|
+
program
|
|
1672
|
+
.command('uninstall')
|
|
1673
|
+
.description('Remove codegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, Hermes Agent)')
|
|
1674
|
+
.option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
|
|
1675
|
+
.option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
|
|
1676
|
+
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
|
|
1677
|
+
.action(async (opts) => {
|
|
1678
|
+
const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
|
|
1679
|
+
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
|
|
1680
|
+
error(`--location must be "global" or "local" (got "${opts.location}").`);
|
|
1681
|
+
process.exit(1);
|
|
1682
|
+
}
|
|
1683
|
+
try {
|
|
1684
|
+
await runUninstaller({
|
|
1685
|
+
target: opts.target,
|
|
1686
|
+
location: opts.location,
|
|
1687
|
+
yes: opts.yes,
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
catch (err) {
|
|
1691
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1692
|
+
process.exit(1);
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1376
1695
|
// Parse and run
|
|
1377
1696
|
program.parse();
|
|
1378
1697
|
} // end main()
|