@colbymchenry/codegraph 0.2.9 → 0.4.8
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 @colbymchenry/codegraph might be problematic. Click here for more details.
- package/LICENSE +21 -21
- package/README.md +647 -641
- package/dist/bin/codegraph.d.ts +7 -2
- package/dist/bin/codegraph.d.ts.map +1 -1
- package/dist/bin/codegraph.js +360 -140
- package/dist/bin/codegraph.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/context/index.d.ts +17 -4
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +182 -15
- package/dist/context/index.js.map +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +16 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +11 -12
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +19 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +221 -107
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +154 -149
- package/dist/directory.d.ts +13 -1
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +62 -19
- package/dist/directory.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -1
- package/dist/errors.js.map +1 -1
- package/dist/extraction/grammars.d.ts +9 -4
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +133 -65
- package/dist/extraction/grammars.js.map +1 -1
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +119 -7
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/tree-sitter.d.ts +62 -0
- package/dist/extraction/tree-sitter.d.ts.map +1 -1
- package/dist/extraction/tree-sitter.js +937 -34
- package/dist/extraction/tree-sitter.js.map +1 -1
- package/dist/graph/traversal.d.ts.map +1 -1
- package/dist/graph/traversal.js +6 -2
- package/dist/graph/traversal.js.map +1 -1
- package/dist/index.d.ts +6 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -74
- package/dist/index.js.map +1 -1
- package/dist/installer/banner.js +7 -7
- package/dist/installer/claude-md-template.js +32 -32
- package/dist/installer/config-writer.d.ts +9 -0
- package/dist/installer/config-writer.d.ts.map +1 -1
- package/dist/installer/config-writer.js +78 -0
- package/dist/installer/config-writer.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +28 -16
- package/dist/installer/index.js.map +1 -1
- package/dist/mcp/index.d.ts +14 -3
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +109 -29
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools.d.ts +62 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +414 -43
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/transport.d.ts.map +1 -1
- package/dist/mcp/transport.js +2 -0
- package/dist/mcp/transport.js.map +1 -1
- package/dist/resolution/frameworks/index.d.ts +1 -0
- package/dist/resolution/frameworks/index.d.ts.map +1 -1
- package/dist/resolution/frameworks/index.js +5 -1
- package/dist/resolution/frameworks/index.js.map +1 -1
- package/dist/resolution/frameworks/svelte.d.ts +9 -0
- package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
- package/dist/resolution/frameworks/svelte.js +268 -0
- package/dist/resolution/frameworks/svelte.js.map +1 -0
- package/dist/resolution/index.d.ts +11 -2
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +94 -13
- package/dist/resolution/index.js.map +1 -1
- package/dist/sentry.d.ts +22 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +159 -0
- package/dist/sentry.js.map +1 -0
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +3 -5
- package/dist/sync/index.js.map +1 -1
- package/dist/types.d.ts +5 -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.d.ts +45 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +114 -3
- package/dist/utils.js.map +1 -1
- package/dist/vectors/embedder.d.ts +1 -1
- package/dist/vectors/embedder.d.ts.map +1 -1
- package/dist/vectors/embedder.js +5 -2
- package/dist/vectors/embedder.js.map +1 -1
- package/dist/vectors/search.d.ts.map +1 -1
- package/dist/vectors/search.js +33 -32
- package/dist/vectors/search.js.map +1 -1
- package/package.json +72 -65
- package/scripts/patch-tree-sitter-dart.js +112 -0
- package/scripts/postinstall.js +71 -0
- package/dist/sync/git-hooks.d.ts +0 -66
- package/dist/sync/git-hooks.d.ts.map +0 -1
- package/dist/sync/git-hooks.js +0 -281
- package/dist/sync/git-hooks.js.map +0 -1
package/dist/bin/codegraph.js
CHANGED
|
@@ -9,13 +9,18 @@
|
|
|
9
9
|
* codegraph Run interactive installer (when no args)
|
|
10
10
|
* codegraph install Run interactive installer
|
|
11
11
|
* codegraph init [path] Initialize CodeGraph in a project
|
|
12
|
+
* codegraph uninit [path] Remove CodeGraph from a project
|
|
12
13
|
* codegraph index [path] Index all files in the project
|
|
13
14
|
* codegraph sync [path] Sync changes since last index
|
|
14
15
|
* codegraph status [path] Show index status
|
|
15
16
|
* codegraph query <search> Search for symbols
|
|
17
|
+
* codegraph files [options] Show project file structure
|
|
16
18
|
* codegraph context <task> Build context for a task
|
|
17
|
-
* codegraph
|
|
18
|
-
* codegraph
|
|
19
|
+
* codegraph mark-dirty [path] Mark project as needing sync (hooks)
|
|
20
|
+
* codegraph sync-if-dirty [path] Sync if marked dirty (hooks)
|
|
21
|
+
*
|
|
22
|
+
* Note: Git hooks have been removed. CodeGraph sync is triggered automatically
|
|
23
|
+
* through codegraph's Claude Code hooks integration.
|
|
19
24
|
*/
|
|
20
25
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
26
|
if (k2 === undefined) k2 = k;
|
|
@@ -50,18 +55,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
50
55
|
return result;
|
|
51
56
|
};
|
|
52
57
|
})();
|
|
53
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
54
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
|
-
};
|
|
56
58
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
59
|
const commander_1 = require("commander");
|
|
58
60
|
const path = __importStar(require("path"));
|
|
59
61
|
const fs = __importStar(require("fs"));
|
|
60
|
-
const index_1 =
|
|
62
|
+
const index_1 = __importStar(require("../index"));
|
|
61
63
|
const installer_1 = require("../installer");
|
|
64
|
+
const sentry_1 = require("../sentry");
|
|
62
65
|
// Check if running with no arguments - run installer
|
|
66
|
+
// Read version for Sentry release tag
|
|
67
|
+
const pkgVersion = (() => {
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')).version;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
(0, sentry_1.initSentry)({ processName: 'codegraph-cli', version: pkgVersion });
|
|
63
76
|
if (process.argv.length === 2) {
|
|
64
77
|
(0, installer_1.runInstaller)().catch((err) => {
|
|
78
|
+
(0, sentry_1.captureException)(err);
|
|
65
79
|
console.error('Installation failed:', err.message);
|
|
66
80
|
process.exit(1);
|
|
67
81
|
});
|
|
@@ -70,6 +84,14 @@ else {
|
|
|
70
84
|
// Normal CLI flow
|
|
71
85
|
main();
|
|
72
86
|
}
|
|
87
|
+
process.on('uncaughtException', (error) => {
|
|
88
|
+
(0, sentry_1.captureException)(error);
|
|
89
|
+
console.error('[CodeGraph] Uncaught exception:', error);
|
|
90
|
+
});
|
|
91
|
+
process.on('unhandledRejection', (reason) => {
|
|
92
|
+
(0, sentry_1.captureException)(reason);
|
|
93
|
+
console.error('[CodeGraph] Unhandled rejection:', reason);
|
|
94
|
+
});
|
|
73
95
|
function main() {
|
|
74
96
|
const program = new commander_1.Command();
|
|
75
97
|
// Version from package.json
|
|
@@ -109,9 +131,30 @@ function main() {
|
|
|
109
131
|
// =============================================================================
|
|
110
132
|
/**
|
|
111
133
|
* Resolve project path from argument or current directory
|
|
134
|
+
* Walks up parent directories to find nearest initialized CodeGraph project
|
|
135
|
+
* (must have .codegraph/codegraph.db, not just .codegraph/lessons.db)
|
|
112
136
|
*/
|
|
113
137
|
function resolveProjectPath(pathArg) {
|
|
114
|
-
|
|
138
|
+
const absolutePath = path.resolve(pathArg || process.cwd());
|
|
139
|
+
// If exact path is initialized (has codegraph.db), use it
|
|
140
|
+
if (index_1.default.isInitialized(absolutePath)) {
|
|
141
|
+
return absolutePath;
|
|
142
|
+
}
|
|
143
|
+
// Walk up to find nearest parent with CodeGraph initialized
|
|
144
|
+
// Note: findNearestCodeGraphRoot finds any .codegraph folder, but we need one with codegraph.db
|
|
145
|
+
let current = absolutePath;
|
|
146
|
+
const root = path.parse(current).root;
|
|
147
|
+
while (current !== root) {
|
|
148
|
+
const parent = path.dirname(current);
|
|
149
|
+
if (parent === current)
|
|
150
|
+
break;
|
|
151
|
+
current = parent;
|
|
152
|
+
if (index_1.default.isInitialized(current)) {
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Not found - return original path (will fail later with helpful error)
|
|
157
|
+
return absolutePath;
|
|
115
158
|
}
|
|
116
159
|
/**
|
|
117
160
|
* Format a number with commas
|
|
@@ -195,7 +238,6 @@ function main() {
|
|
|
195
238
|
.command('init [path]')
|
|
196
239
|
.description('Initialize CodeGraph in a project directory')
|
|
197
240
|
.option('-i, --index', 'Run initial indexing after initialization')
|
|
198
|
-
.option('--no-hooks', 'Skip git hooks installation')
|
|
199
241
|
.action(async (pathArg, options) => {
|
|
200
242
|
const projectPath = resolveProjectPath(pathArg);
|
|
201
243
|
console.log(chalk.bold('\nInitializing CodeGraph...\n'));
|
|
@@ -212,16 +254,6 @@ function main() {
|
|
|
212
254
|
});
|
|
213
255
|
success(`Initialized CodeGraph in ${projectPath}`);
|
|
214
256
|
info(`Created .codegraph/ directory`);
|
|
215
|
-
// Install git hooks if requested (default: true)
|
|
216
|
-
if (options.hooks !== false && cg.isGitRepository()) {
|
|
217
|
-
const hookResult = cg.installGitHooks();
|
|
218
|
-
if (hookResult.success) {
|
|
219
|
-
success('Installed git post-commit hook for auto-sync');
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
warn(`Could not install git hooks: ${hookResult.message}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
257
|
// Run initial index if requested
|
|
226
258
|
if (options.index) {
|
|
227
259
|
console.log('\nIndexing project...\n');
|
|
@@ -245,10 +277,48 @@ function main() {
|
|
|
245
277
|
cg.destroy();
|
|
246
278
|
}
|
|
247
279
|
catch (err) {
|
|
280
|
+
(0, sentry_1.captureException)(err);
|
|
248
281
|
error(`Failed to initialize: ${err instanceof Error ? err.message : String(err)}`);
|
|
249
282
|
process.exit(1);
|
|
250
283
|
}
|
|
251
284
|
});
|
|
285
|
+
/**
|
|
286
|
+
* codegraph uninit [path]
|
|
287
|
+
*/
|
|
288
|
+
program
|
|
289
|
+
.command('uninit [path]')
|
|
290
|
+
.description('Remove CodeGraph from a project (deletes .codegraph/ directory)')
|
|
291
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
292
|
+
.action(async (pathArg, options) => {
|
|
293
|
+
const projectPath = resolveProjectPath(pathArg);
|
|
294
|
+
try {
|
|
295
|
+
if (!index_1.default.isInitialized(projectPath)) {
|
|
296
|
+
warn(`CodeGraph is not initialized in ${projectPath}`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (!options.force) {
|
|
300
|
+
// Confirm with user
|
|
301
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
302
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
303
|
+
const answer = await new Promise((resolve) => {
|
|
304
|
+
rl.question(chalk.yellow('⚠ This will permanently delete all CodeGraph data. Continue? (y/N) '), resolve);
|
|
305
|
+
});
|
|
306
|
+
rl.close();
|
|
307
|
+
if (answer.toLowerCase() !== 'y') {
|
|
308
|
+
info('Cancelled');
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const cg = index_1.default.openSync(projectPath);
|
|
313
|
+
cg.uninitialize();
|
|
314
|
+
success(`Removed CodeGraph from ${projectPath}`);
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
(0, sentry_1.captureException)(err);
|
|
318
|
+
error(`Failed to uninitialize: ${err instanceof Error ? err.message : String(err)}`);
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
252
322
|
/**
|
|
253
323
|
* codegraph index [path]
|
|
254
324
|
*/
|
|
@@ -305,6 +375,7 @@ function main() {
|
|
|
305
375
|
cg.destroy();
|
|
306
376
|
}
|
|
307
377
|
catch (err) {
|
|
378
|
+
(0, sentry_1.captureException)(err);
|
|
308
379
|
error(`Failed to index: ${err instanceof Error ? err.message : String(err)}`);
|
|
309
380
|
process.exit(1);
|
|
310
381
|
}
|
|
@@ -355,6 +426,7 @@ function main() {
|
|
|
355
426
|
cg.destroy();
|
|
356
427
|
}
|
|
357
428
|
catch (err) {
|
|
429
|
+
(0, sentry_1.captureException)(err);
|
|
358
430
|
if (!options.quiet) {
|
|
359
431
|
error(`Failed to sync: ${err instanceof Error ? err.message : String(err)}`);
|
|
360
432
|
}
|
|
@@ -367,10 +439,15 @@ function main() {
|
|
|
367
439
|
program
|
|
368
440
|
.command('status [path]')
|
|
369
441
|
.description('Show index status and statistics')
|
|
370
|
-
.
|
|
442
|
+
.option('-j, --json', 'Output as JSON')
|
|
443
|
+
.action(async (pathArg, options) => {
|
|
371
444
|
const projectPath = resolveProjectPath(pathArg);
|
|
372
445
|
try {
|
|
373
446
|
if (!index_1.default.isInitialized(projectPath)) {
|
|
447
|
+
if (options.json) {
|
|
448
|
+
console.log(JSON.stringify({ initialized: false, projectPath }));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
374
451
|
console.log(chalk.bold('\nCodeGraph Status\n'));
|
|
375
452
|
info(`Project: ${projectPath}`);
|
|
376
453
|
warn('Not initialized');
|
|
@@ -380,6 +457,26 @@ function main() {
|
|
|
380
457
|
const cg = await index_1.default.open(projectPath);
|
|
381
458
|
const stats = cg.getStats();
|
|
382
459
|
const changes = cg.getChangedFiles();
|
|
460
|
+
// JSON output mode
|
|
461
|
+
if (options.json) {
|
|
462
|
+
console.log(JSON.stringify({
|
|
463
|
+
initialized: true,
|
|
464
|
+
projectPath,
|
|
465
|
+
fileCount: stats.fileCount,
|
|
466
|
+
nodeCount: stats.nodeCount,
|
|
467
|
+
edgeCount: stats.edgeCount,
|
|
468
|
+
dbSizeBytes: stats.dbSizeBytes,
|
|
469
|
+
nodesByKind: stats.nodesByKind,
|
|
470
|
+
languages: Object.entries(stats.filesByLanguage).filter(([, count]) => count > 0).map(([lang]) => lang),
|
|
471
|
+
pendingChanges: {
|
|
472
|
+
added: changes.added.length,
|
|
473
|
+
modified: changes.modified.length,
|
|
474
|
+
removed: changes.removed.length,
|
|
475
|
+
},
|
|
476
|
+
}));
|
|
477
|
+
cg.destroy();
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
383
480
|
console.log(chalk.bold('\nCodeGraph Status\n'));
|
|
384
481
|
// Project info
|
|
385
482
|
console.log(chalk.cyan('Project:'), projectPath);
|
|
@@ -428,20 +525,10 @@ function main() {
|
|
|
428
525
|
success('Index is up to date');
|
|
429
526
|
}
|
|
430
527
|
console.log();
|
|
431
|
-
// Git hooks status
|
|
432
|
-
if (cg.isGitRepository()) {
|
|
433
|
-
const hookInstalled = cg.isGitHookInstalled();
|
|
434
|
-
if (hookInstalled) {
|
|
435
|
-
success('Git hooks: installed');
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
warn('Git hooks: not installed');
|
|
439
|
-
info('Run "codegraph hooks install" to enable auto-sync');
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
528
|
cg.destroy();
|
|
443
529
|
}
|
|
444
530
|
catch (err) {
|
|
531
|
+
(0, sentry_1.captureException)(err);
|
|
445
532
|
error(`Failed to get status: ${err instanceof Error ? err.message : String(err)}`);
|
|
446
533
|
process.exit(1);
|
|
447
534
|
}
|
|
@@ -496,54 +583,24 @@ function main() {
|
|
|
496
583
|
cg.destroy();
|
|
497
584
|
}
|
|
498
585
|
catch (err) {
|
|
586
|
+
(0, sentry_1.captureException)(err);
|
|
499
587
|
error(`Search failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
500
588
|
process.exit(1);
|
|
501
589
|
}
|
|
502
590
|
});
|
|
503
591
|
/**
|
|
504
|
-
* codegraph
|
|
592
|
+
* codegraph files [path]
|
|
505
593
|
*/
|
|
506
594
|
program
|
|
507
|
-
.command('
|
|
508
|
-
.description('
|
|
509
|
-
.option('-p, --path <path>', 'Project path')
|
|
510
|
-
.option('-n, --max-nodes <number>', 'Maximum nodes to include', '50')
|
|
511
|
-
.option('-c, --max-code <number>', 'Maximum code blocks', '10')
|
|
512
|
-
.option('--no-code', 'Exclude code blocks')
|
|
513
|
-
.option('-f, --format <format>', 'Output format (markdown, json)', 'markdown')
|
|
514
|
-
.action(async (task, options) => {
|
|
515
|
-
const projectPath = resolveProjectPath(options.path);
|
|
516
|
-
try {
|
|
517
|
-
if (!index_1.default.isInitialized(projectPath)) {
|
|
518
|
-
error(`CodeGraph not initialized in ${projectPath}`);
|
|
519
|
-
process.exit(1);
|
|
520
|
-
}
|
|
521
|
-
const cg = await index_1.default.open(projectPath);
|
|
522
|
-
const context = await cg.buildContext(task, {
|
|
523
|
-
maxNodes: parseInt(options.maxNodes || '50', 10),
|
|
524
|
-
maxCodeBlocks: parseInt(options.maxCode || '10', 10),
|
|
525
|
-
includeCode: options.code !== false,
|
|
526
|
-
format: options.format,
|
|
527
|
-
});
|
|
528
|
-
// Output the context
|
|
529
|
-
console.log(context);
|
|
530
|
-
cg.destroy();
|
|
531
|
-
}
|
|
532
|
-
catch (err) {
|
|
533
|
-
error(`Failed to build context: ${err instanceof Error ? err.message : String(err)}`);
|
|
534
|
-
process.exit(1);
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
/**
|
|
538
|
-
* codegraph hooks <action>
|
|
539
|
-
*/
|
|
540
|
-
const hooksCommand = program
|
|
541
|
-
.command('hooks')
|
|
542
|
-
.description('Manage git hooks');
|
|
543
|
-
hooksCommand
|
|
544
|
-
.command('install')
|
|
545
|
-
.description('Install git post-commit hook for auto-sync')
|
|
595
|
+
.command('files')
|
|
596
|
+
.description('Show project file structure from the index')
|
|
546
597
|
.option('-p, --path <path>', 'Project path')
|
|
598
|
+
.option('--filter <dir>', 'Filter to files under this directory')
|
|
599
|
+
.option('--pattern <glob>', 'Filter files matching this glob pattern')
|
|
600
|
+
.option('--format <format>', 'Output format (tree, flat, grouped)', 'tree')
|
|
601
|
+
.option('--max-depth <number>', 'Maximum directory depth for tree format')
|
|
602
|
+
.option('--no-metadata', 'Hide file metadata (language, symbol count)')
|
|
603
|
+
.option('-j, --json', 'Output as JSON')
|
|
547
604
|
.action(async (options) => {
|
|
548
605
|
const projectPath = resolveProjectPath(options.path);
|
|
549
606
|
try {
|
|
@@ -552,69 +609,165 @@ function main() {
|
|
|
552
609
|
process.exit(1);
|
|
553
610
|
}
|
|
554
611
|
const cg = await index_1.default.open(projectPath);
|
|
555
|
-
|
|
556
|
-
|
|
612
|
+
let files = cg.getFiles();
|
|
613
|
+
if (files.length === 0) {
|
|
614
|
+
info('No files indexed. Run "codegraph index" first.');
|
|
557
615
|
cg.destroy();
|
|
558
|
-
|
|
616
|
+
return;
|
|
559
617
|
}
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
info('Previous hook backed up to post-commit.codegraph-backup');
|
|
565
|
-
}
|
|
618
|
+
// Filter by path prefix
|
|
619
|
+
if (options.filter) {
|
|
620
|
+
const filter = options.filter;
|
|
621
|
+
files = files.filter(f => f.path.startsWith(filter) || f.path.startsWith('./' + filter));
|
|
566
622
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
cg.destroy();
|
|
572
|
-
}
|
|
573
|
-
catch (err) {
|
|
574
|
-
error(`Failed to install hooks: ${err instanceof Error ? err.message : String(err)}`);
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
hooksCommand
|
|
579
|
-
.command('remove')
|
|
580
|
-
.description('Remove git post-commit hook')
|
|
581
|
-
.option('-p, --path <path>', 'Project path')
|
|
582
|
-
.action(async (options) => {
|
|
583
|
-
const projectPath = resolveProjectPath(options.path);
|
|
584
|
-
try {
|
|
585
|
-
if (!index_1.default.isInitialized(projectPath)) {
|
|
586
|
-
error(`CodeGraph not initialized in ${projectPath}`);
|
|
587
|
-
process.exit(1);
|
|
623
|
+
// Filter by glob pattern
|
|
624
|
+
if (options.pattern) {
|
|
625
|
+
const regex = globToRegex(options.pattern);
|
|
626
|
+
files = files.filter(f => regex.test(f.path));
|
|
588
627
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
error('Not a git repository');
|
|
628
|
+
if (files.length === 0) {
|
|
629
|
+
info('No files found matching the criteria.');
|
|
592
630
|
cg.destroy();
|
|
593
|
-
|
|
631
|
+
return;
|
|
594
632
|
}
|
|
595
|
-
|
|
596
|
-
if (
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
633
|
+
// JSON output
|
|
634
|
+
if (options.json) {
|
|
635
|
+
const output = files.map(f => ({
|
|
636
|
+
path: f.path,
|
|
637
|
+
language: f.language,
|
|
638
|
+
nodeCount: f.nodeCount,
|
|
639
|
+
size: f.size,
|
|
640
|
+
}));
|
|
641
|
+
console.log(JSON.stringify(output, null, 2));
|
|
642
|
+
cg.destroy();
|
|
643
|
+
return;
|
|
601
644
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
645
|
+
const includeMetadata = options.metadata !== false;
|
|
646
|
+
const format = options.format || 'tree';
|
|
647
|
+
const maxDepth = options.maxDepth ? parseInt(options.maxDepth, 10) : undefined;
|
|
648
|
+
// Format output
|
|
649
|
+
switch (format) {
|
|
650
|
+
case 'flat':
|
|
651
|
+
console.log(chalk.bold(`\nFiles (${files.length}):\n`));
|
|
652
|
+
for (const file of files.sort((a, b) => a.path.localeCompare(b.path))) {
|
|
653
|
+
if (includeMetadata) {
|
|
654
|
+
console.log(` ${file.path} ${chalk.dim(`(${file.language}, ${file.nodeCount} symbols)`)}`);
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
console.log(` ${file.path}`);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
case 'grouped':
|
|
662
|
+
console.log(chalk.bold(`\nFiles by Language (${files.length} total):\n`));
|
|
663
|
+
const byLang = new Map();
|
|
664
|
+
for (const file of files) {
|
|
665
|
+
const existing = byLang.get(file.language) || [];
|
|
666
|
+
existing.push(file);
|
|
667
|
+
byLang.set(file.language, existing);
|
|
668
|
+
}
|
|
669
|
+
const sortedLangs = [...byLang.entries()].sort((a, b) => b[1].length - a[1].length);
|
|
670
|
+
for (const [lang, langFiles] of sortedLangs) {
|
|
671
|
+
console.log(chalk.cyan(`${lang} (${langFiles.length}):`));
|
|
672
|
+
for (const file of langFiles.sort((a, b) => a.path.localeCompare(b.path))) {
|
|
673
|
+
if (includeMetadata) {
|
|
674
|
+
console.log(` ${file.path} ${chalk.dim(`(${file.nodeCount} symbols)`)}`);
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
console.log(` ${file.path}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
console.log();
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
case 'tree':
|
|
684
|
+
default:
|
|
685
|
+
console.log(chalk.bold(`\nProject Structure (${files.length} files):\n`));
|
|
686
|
+
printFileTree(files, includeMetadata, maxDepth, chalk);
|
|
687
|
+
break;
|
|
605
688
|
}
|
|
689
|
+
console.log();
|
|
606
690
|
cg.destroy();
|
|
607
691
|
}
|
|
608
692
|
catch (err) {
|
|
609
|
-
|
|
693
|
+
(0, sentry_1.captureException)(err);
|
|
694
|
+
error(`Failed to list files: ${err instanceof Error ? err.message : String(err)}`);
|
|
610
695
|
process.exit(1);
|
|
611
696
|
}
|
|
612
697
|
});
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
698
|
+
/**
|
|
699
|
+
* Convert glob pattern to regex
|
|
700
|
+
*/
|
|
701
|
+
function globToRegex(pattern) {
|
|
702
|
+
const escaped = pattern
|
|
703
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
704
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
705
|
+
.replace(/\*/g, '[^/]*')
|
|
706
|
+
.replace(/\?/g, '[^/]')
|
|
707
|
+
.replace(/\{\{GLOBSTAR\}\}/g, '.*');
|
|
708
|
+
return new RegExp(escaped);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Print files as a tree
|
|
712
|
+
*/
|
|
713
|
+
function printFileTree(files, includeMetadata, maxDepth, chalk) {
|
|
714
|
+
const root = { name: '', children: new Map() };
|
|
715
|
+
for (const file of files) {
|
|
716
|
+
const parts = file.path.split('/');
|
|
717
|
+
let current = root;
|
|
718
|
+
for (let i = 0; i < parts.length; i++) {
|
|
719
|
+
const part = parts[i];
|
|
720
|
+
if (!part)
|
|
721
|
+
continue;
|
|
722
|
+
if (!current.children.has(part)) {
|
|
723
|
+
current.children.set(part, { name: part, children: new Map() });
|
|
724
|
+
}
|
|
725
|
+
current = current.children.get(part);
|
|
726
|
+
if (i === parts.length - 1) {
|
|
727
|
+
current.file = { language: file.language, nodeCount: file.nodeCount };
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
const renderNode = (node, prefix, isLast, depth) => {
|
|
732
|
+
if (maxDepth !== undefined && depth > maxDepth)
|
|
733
|
+
return;
|
|
734
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
735
|
+
const childPrefix = isLast ? ' ' : '│ ';
|
|
736
|
+
if (node.name) {
|
|
737
|
+
let line = prefix + connector + node.name;
|
|
738
|
+
if (node.file && includeMetadata) {
|
|
739
|
+
line += chalk.dim(` (${node.file.language}, ${node.file.nodeCount} symbols)`);
|
|
740
|
+
}
|
|
741
|
+
console.log(line);
|
|
742
|
+
}
|
|
743
|
+
const children = [...node.children.values()];
|
|
744
|
+
children.sort((a, b) => {
|
|
745
|
+
const aIsDir = a.children.size > 0 && !a.file;
|
|
746
|
+
const bIsDir = b.children.size > 0 && !b.file;
|
|
747
|
+
if (aIsDir !== bIsDir)
|
|
748
|
+
return aIsDir ? -1 : 1;
|
|
749
|
+
return a.name.localeCompare(b.name);
|
|
750
|
+
});
|
|
751
|
+
for (let i = 0; i < children.length; i++) {
|
|
752
|
+
const child = children[i];
|
|
753
|
+
const nextPrefix = node.name ? prefix + childPrefix : prefix;
|
|
754
|
+
renderNode(child, nextPrefix, i === children.length - 1, depth + 1);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
renderNode(root, '', true, 0);
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* codegraph context <task>
|
|
761
|
+
*/
|
|
762
|
+
program
|
|
763
|
+
.command('context <task>')
|
|
764
|
+
.description('Build context for a task (outputs markdown)')
|
|
616
765
|
.option('-p, --path <path>', 'Project path')
|
|
617
|
-
.
|
|
766
|
+
.option('-n, --max-nodes <number>', 'Maximum nodes to include', '50')
|
|
767
|
+
.option('-c, --max-code <number>', 'Maximum code blocks', '10')
|
|
768
|
+
.option('--no-code', 'Exclude code blocks')
|
|
769
|
+
.option('-f, --format <format>', 'Output format (markdown, json)', 'markdown')
|
|
770
|
+
.action(async (task, options) => {
|
|
618
771
|
const projectPath = resolveProjectPath(options.path);
|
|
619
772
|
try {
|
|
620
773
|
if (!index_1.default.isInitialized(projectPath)) {
|
|
@@ -622,22 +775,19 @@ function main() {
|
|
|
622
775
|
process.exit(1);
|
|
623
776
|
}
|
|
624
777
|
const cg = await index_1.default.open(projectPath);
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
else {
|
|
634
|
-
warn('Git hook is not installed');
|
|
635
|
-
info('Run "codegraph hooks install" to enable auto-sync');
|
|
636
|
-
}
|
|
778
|
+
const context = await cg.buildContext(task, {
|
|
779
|
+
maxNodes: parseInt(options.maxNodes || '50', 10),
|
|
780
|
+
maxCodeBlocks: parseInt(options.maxCode || '10', 10),
|
|
781
|
+
includeCode: options.code !== false,
|
|
782
|
+
format: options.format,
|
|
783
|
+
});
|
|
784
|
+
// Output the context
|
|
785
|
+
console.log(context);
|
|
637
786
|
cg.destroy();
|
|
638
787
|
}
|
|
639
788
|
catch (err) {
|
|
640
|
-
|
|
789
|
+
(0, sentry_1.captureException)(err);
|
|
790
|
+
error(`Failed to build context: ${err instanceof Error ? err.message : String(err)}`);
|
|
641
791
|
process.exit(1);
|
|
642
792
|
}
|
|
643
793
|
});
|
|
@@ -664,15 +814,15 @@ function main() {
|
|
|
664
814
|
console.log(chalk.bold('\nCodeGraph MCP Server\n'));
|
|
665
815
|
info('Use --mcp flag to start the MCP server');
|
|
666
816
|
console.log('\nTo use with Claude Code, add to your MCP configuration:');
|
|
667
|
-
console.log(chalk.dim(`
|
|
668
|
-
{
|
|
669
|
-
"mcpServers": {
|
|
670
|
-
"codegraph": {
|
|
671
|
-
"command": "codegraph",
|
|
672
|
-
"args": ["serve", "--mcp"]
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
817
|
+
console.log(chalk.dim(`
|
|
818
|
+
{
|
|
819
|
+
"mcpServers": {
|
|
820
|
+
"codegraph": {
|
|
821
|
+
"command": "codegraph",
|
|
822
|
+
"args": ["serve", "--mcp"]
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
676
826
|
`));
|
|
677
827
|
console.log('Available tools:');
|
|
678
828
|
console.log(chalk.cyan(' codegraph_search') + ' - Search for code symbols');
|
|
@@ -681,14 +831,84 @@ function main() {
|
|
|
681
831
|
console.log(chalk.cyan(' codegraph_callees') + ' - Find what a symbol calls');
|
|
682
832
|
console.log(chalk.cyan(' codegraph_impact') + ' - Analyze impact of changes');
|
|
683
833
|
console.log(chalk.cyan(' codegraph_node') + ' - Get symbol details');
|
|
834
|
+
console.log(chalk.cyan(' codegraph_files') + ' - Get project file structure');
|
|
684
835
|
console.log(chalk.cyan(' codegraph_status') + ' - Get index status');
|
|
685
836
|
}
|
|
686
837
|
}
|
|
687
838
|
catch (err) {
|
|
839
|
+
(0, sentry_1.captureException)(err);
|
|
688
840
|
error(`Failed to start server: ${err instanceof Error ? err.message : String(err)}`);
|
|
689
841
|
process.exit(1);
|
|
690
842
|
}
|
|
691
843
|
});
|
|
844
|
+
/**
|
|
845
|
+
* codegraph mark-dirty [path]
|
|
846
|
+
*
|
|
847
|
+
* Touches .codegraph/.dirty to signal that files have changed.
|
|
848
|
+
* Used by Claude Code PostToolUse hooks to batch syncs.
|
|
849
|
+
* Runs silently and always exits 0.
|
|
850
|
+
*/
|
|
851
|
+
program
|
|
852
|
+
.command('mark-dirty [path]')
|
|
853
|
+
.description('Mark project as needing sync (used by Claude Code hooks)')
|
|
854
|
+
.action(async (pathArg) => {
|
|
855
|
+
try {
|
|
856
|
+
const startPath = path.resolve(pathArg || process.cwd());
|
|
857
|
+
const projectRoot = (0, index_1.findNearestCodeGraphRoot)(startPath);
|
|
858
|
+
if (!projectRoot) {
|
|
859
|
+
// No .codegraph/ found — exit silently
|
|
860
|
+
process.exit(0);
|
|
861
|
+
}
|
|
862
|
+
const dirtyPath = path.join((0, index_1.getCodeGraphDir)(projectRoot), '.dirty');
|
|
863
|
+
fs.writeFileSync(dirtyPath, Date.now().toString(), 'utf-8');
|
|
864
|
+
}
|
|
865
|
+
catch {
|
|
866
|
+
// Never fail — this runs in the background during edits
|
|
867
|
+
}
|
|
868
|
+
process.exit(0);
|
|
869
|
+
});
|
|
870
|
+
/**
|
|
871
|
+
* codegraph sync-if-dirty [path]
|
|
872
|
+
*
|
|
873
|
+
* Syncs the index only if .codegraph/.dirty exists.
|
|
874
|
+
* Removes the marker BEFORE syncing so edits during sync
|
|
875
|
+
* create a new marker for the next Stop event.
|
|
876
|
+
* Runs silently and always exits 0.
|
|
877
|
+
*/
|
|
878
|
+
program
|
|
879
|
+
.command('sync-if-dirty [path]')
|
|
880
|
+
.description('Sync if project was marked dirty (used by Claude Code hooks)')
|
|
881
|
+
.action(async (pathArg) => {
|
|
882
|
+
try {
|
|
883
|
+
const startPath = path.resolve(pathArg || process.cwd());
|
|
884
|
+
const projectRoot = (0, index_1.findNearestCodeGraphRoot)(startPath);
|
|
885
|
+
if (!projectRoot) {
|
|
886
|
+
process.exit(0);
|
|
887
|
+
}
|
|
888
|
+
const dirtyPath = path.join((0, index_1.getCodeGraphDir)(projectRoot), '.dirty');
|
|
889
|
+
// No marker → nothing to do (sub-ms exit)
|
|
890
|
+
if (!fs.existsSync(dirtyPath)) {
|
|
891
|
+
process.exit(0);
|
|
892
|
+
}
|
|
893
|
+
// Remove marker FIRST so edits during sync create a new one
|
|
894
|
+
try {
|
|
895
|
+
fs.unlinkSync(dirtyPath);
|
|
896
|
+
}
|
|
897
|
+
catch { /* ignore */ }
|
|
898
|
+
// If not fully initialized (no DB), exit
|
|
899
|
+
if (!index_1.default.isInitialized(projectRoot)) {
|
|
900
|
+
process.exit(0);
|
|
901
|
+
}
|
|
902
|
+
// Run sync
|
|
903
|
+
const cg = await index_1.default.open(projectRoot);
|
|
904
|
+
await cg.sync();
|
|
905
|
+
cg.destroy();
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
// Never fail — this runs at the end of Claude responses
|
|
909
|
+
}
|
|
910
|
+
process.exit(0);
|
|
911
|
+
});
|
|
692
912
|
/**
|
|
693
913
|
* codegraph install
|
|
694
914
|
*/
|