@colbymchenry/codegraph 0.6.6 → 0.7.2

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.
Files changed (221) hide show
  1. package/README.md +180 -502
  2. package/dist/bin/codegraph.d.ts +0 -5
  3. package/dist/bin/codegraph.d.ts.map +1 -1
  4. package/dist/bin/codegraph.js +217 -263
  5. package/dist/bin/codegraph.js.map +1 -1
  6. package/dist/bin/uninstall.d.ts +0 -1
  7. package/dist/bin/uninstall.d.ts.map +1 -1
  8. package/dist/bin/uninstall.js +3 -29
  9. package/dist/bin/uninstall.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +0 -3
  12. package/dist/config.js.map +1 -1
  13. package/dist/context/index.d.ts +3 -5
  14. package/dist/context/index.d.ts.map +1 -1
  15. package/dist/context/index.js +497 -46
  16. package/dist/context/index.js.map +1 -1
  17. package/dist/db/migrations.d.ts +1 -1
  18. package/dist/db/migrations.d.ts.map +1 -1
  19. package/dist/db/migrations.js +10 -1
  20. package/dist/db/migrations.js.map +1 -1
  21. package/dist/db/queries.d.ts +53 -0
  22. package/dist/db/queries.d.ts.map +1 -1
  23. package/dist/db/queries.js +244 -24
  24. package/dist/db/queries.js.map +1 -1
  25. package/dist/db/schema.sql +1 -16
  26. package/dist/errors.d.ts +1 -1
  27. package/dist/errors.d.ts.map +1 -1
  28. package/dist/errors.js +1 -7
  29. package/dist/errors.js.map +1 -1
  30. package/dist/extraction/dfm-extractor.d.ts +31 -0
  31. package/dist/extraction/dfm-extractor.d.ts.map +1 -0
  32. package/dist/extraction/dfm-extractor.js +151 -0
  33. package/dist/extraction/dfm-extractor.js.map +1 -0
  34. package/dist/extraction/grammars.d.ts +9 -1
  35. package/dist/extraction/grammars.d.ts.map +1 -1
  36. package/dist/extraction/grammars.js +34 -2
  37. package/dist/extraction/grammars.js.map +1 -1
  38. package/dist/extraction/index.d.ts +7 -1
  39. package/dist/extraction/index.d.ts.map +1 -1
  40. package/dist/extraction/index.js +373 -29
  41. package/dist/extraction/index.js.map +1 -1
  42. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  43. package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
  44. package/dist/extraction/languages/c-cpp.js +126 -0
  45. package/dist/extraction/languages/c-cpp.js.map +1 -0
  46. package/dist/extraction/languages/csharp.d.ts +3 -0
  47. package/dist/extraction/languages/csharp.d.ts.map +1 -0
  48. package/dist/extraction/languages/csharp.js +72 -0
  49. package/dist/extraction/languages/csharp.js.map +1 -0
  50. package/dist/extraction/languages/dart.d.ts +3 -0
  51. package/dist/extraction/languages/dart.d.ts.map +1 -0
  52. package/dist/extraction/languages/dart.js +192 -0
  53. package/dist/extraction/languages/dart.js.map +1 -0
  54. package/dist/extraction/languages/go.d.ts +3 -0
  55. package/dist/extraction/languages/go.d.ts.map +1 -0
  56. package/dist/extraction/languages/go.js +58 -0
  57. package/dist/extraction/languages/go.js.map +1 -0
  58. package/dist/extraction/languages/index.d.ts +10 -0
  59. package/dist/extraction/languages/index.d.ts.map +1 -0
  60. package/dist/extraction/languages/index.js +43 -0
  61. package/dist/extraction/languages/index.js.map +1 -0
  62. package/dist/extraction/languages/java.d.ts +3 -0
  63. package/dist/extraction/languages/java.d.ts.map +1 -0
  64. package/dist/extraction/languages/java.js +64 -0
  65. package/dist/extraction/languages/java.js.map +1 -0
  66. package/dist/extraction/languages/javascript.d.ts +3 -0
  67. package/dist/extraction/languages/javascript.d.ts.map +1 -0
  68. package/dist/extraction/languages/javascript.js +90 -0
  69. package/dist/extraction/languages/javascript.js.map +1 -0
  70. package/dist/extraction/languages/kotlin.d.ts +3 -0
  71. package/dist/extraction/languages/kotlin.d.ts.map +1 -0
  72. package/dist/extraction/languages/kotlin.js +253 -0
  73. package/dist/extraction/languages/kotlin.js.map +1 -0
  74. package/dist/extraction/languages/pascal.d.ts +3 -0
  75. package/dist/extraction/languages/pascal.d.ts.map +1 -0
  76. package/dist/extraction/languages/pascal.js +66 -0
  77. package/dist/extraction/languages/pascal.js.map +1 -0
  78. package/dist/extraction/languages/php.d.ts +3 -0
  79. package/dist/extraction/languages/php.d.ts.map +1 -0
  80. package/dist/extraction/languages/php.js +107 -0
  81. package/dist/extraction/languages/php.js.map +1 -0
  82. package/dist/extraction/languages/python.d.ts +3 -0
  83. package/dist/extraction/languages/python.d.ts.map +1 -0
  84. package/dist/extraction/languages/python.js +56 -0
  85. package/dist/extraction/languages/python.js.map +1 -0
  86. package/dist/extraction/languages/ruby.d.ts +3 -0
  87. package/dist/extraction/languages/ruby.d.ts.map +1 -0
  88. package/dist/extraction/languages/ruby.js +114 -0
  89. package/dist/extraction/languages/ruby.js.map +1 -0
  90. package/dist/extraction/languages/rust.d.ts +3 -0
  91. package/dist/extraction/languages/rust.d.ts.map +1 -0
  92. package/dist/extraction/languages/rust.js +109 -0
  93. package/dist/extraction/languages/rust.js.map +1 -0
  94. package/dist/extraction/languages/swift.d.ts +3 -0
  95. package/dist/extraction/languages/swift.d.ts.map +1 -0
  96. package/dist/extraction/languages/swift.js +91 -0
  97. package/dist/extraction/languages/swift.js.map +1 -0
  98. package/dist/extraction/languages/typescript.d.ts +3 -0
  99. package/dist/extraction/languages/typescript.d.ts.map +1 -0
  100. package/dist/extraction/languages/typescript.js +129 -0
  101. package/dist/extraction/languages/typescript.js.map +1 -0
  102. package/dist/extraction/liquid-extractor.d.ts +52 -0
  103. package/dist/extraction/liquid-extractor.d.ts.map +1 -0
  104. package/dist/extraction/liquid-extractor.js +313 -0
  105. package/dist/extraction/liquid-extractor.js.map +1 -0
  106. package/dist/extraction/parse-worker.d.ts +8 -0
  107. package/dist/extraction/parse-worker.d.ts.map +1 -0
  108. package/dist/extraction/parse-worker.js +57 -0
  109. package/dist/extraction/parse-worker.js.map +1 -0
  110. package/dist/extraction/svelte-extractor.d.ts +47 -0
  111. package/dist/extraction/svelte-extractor.d.ts.map +1 -0
  112. package/dist/extraction/svelte-extractor.js +230 -0
  113. package/dist/extraction/svelte-extractor.js.map +1 -0
  114. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  115. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
  116. package/dist/extraction/tree-sitter-helpers.js +103 -0
  117. package/dist/extraction/tree-sitter-helpers.js.map +1 -0
  118. package/dist/extraction/tree-sitter-types.d.ts +179 -0
  119. package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
  120. package/dist/extraction/tree-sitter-types.js +10 -0
  121. package/dist/extraction/tree-sitter-types.js.map +1 -0
  122. package/dist/extraction/tree-sitter.d.ts +67 -125
  123. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  124. package/dist/extraction/tree-sitter.js +1052 -1860
  125. package/dist/extraction/tree-sitter.js.map +1 -1
  126. package/dist/graph/traversal.d.ts.map +1 -1
  127. package/dist/graph/traversal.js +20 -2
  128. package/dist/graph/traversal.js.map +1 -1
  129. package/dist/index.d.ts +29 -53
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +88 -117
  132. package/dist/index.js.map +1 -1
  133. package/dist/installer/claude-md-template.d.ts +1 -1
  134. package/dist/installer/claude-md-template.d.ts.map +1 -1
  135. package/dist/installer/claude-md-template.js +15 -15
  136. package/dist/installer/config-writer.d.ts +2 -13
  137. package/dist/installer/config-writer.d.ts.map +1 -1
  138. package/dist/installer/config-writer.js +4 -87
  139. package/dist/installer/config-writer.js.map +1 -1
  140. package/dist/installer/index.d.ts +3 -4
  141. package/dist/installer/index.d.ts.map +1 -1
  142. package/dist/installer/index.js +118 -127
  143. package/dist/installer/index.js.map +1 -1
  144. package/dist/mcp/index.d.ts +5 -0
  145. package/dist/mcp/index.d.ts.map +1 -1
  146. package/dist/mcp/index.js +25 -4
  147. package/dist/mcp/index.js.map +1 -1
  148. package/dist/mcp/tools.d.ts +33 -0
  149. package/dist/mcp/tools.d.ts.map +1 -1
  150. package/dist/mcp/tools.js +405 -26
  151. package/dist/mcp/tools.js.map +1 -1
  152. package/dist/mcp/transport.d.ts.map +1 -1
  153. package/dist/mcp/transport.js +0 -2
  154. package/dist/mcp/transport.js.map +1 -1
  155. package/dist/resolution/frameworks/csharp.js +29 -84
  156. package/dist/resolution/frameworks/csharp.js.map +1 -1
  157. package/dist/resolution/frameworks/express.js +44 -48
  158. package/dist/resolution/frameworks/express.js.map +1 -1
  159. package/dist/resolution/frameworks/go.js +34 -70
  160. package/dist/resolution/frameworks/go.js.map +1 -1
  161. package/dist/resolution/frameworks/java.js +29 -87
  162. package/dist/resolution/frameworks/java.js.map +1 -1
  163. package/dist/resolution/frameworks/laravel.js +6 -6
  164. package/dist/resolution/frameworks/laravel.js.map +1 -1
  165. package/dist/resolution/frameworks/python.js +33 -98
  166. package/dist/resolution/frameworks/python.js.map +1 -1
  167. package/dist/resolution/frameworks/react.js +53 -76
  168. package/dist/resolution/frameworks/react.js.map +1 -1
  169. package/dist/resolution/frameworks/ruby.js +12 -24
  170. package/dist/resolution/frameworks/ruby.js.map +1 -1
  171. package/dist/resolution/frameworks/rust.js +26 -66
  172. package/dist/resolution/frameworks/rust.js.map +1 -1
  173. package/dist/resolution/frameworks/svelte.js +11 -31
  174. package/dist/resolution/frameworks/svelte.js.map +1 -1
  175. package/dist/resolution/frameworks/swift.js +42 -160
  176. package/dist/resolution/frameworks/swift.js.map +1 -1
  177. package/dist/resolution/index.d.ts +19 -6
  178. package/dist/resolution/index.d.ts.map +1 -1
  179. package/dist/resolution/index.js +300 -144
  180. package/dist/resolution/index.js.map +1 -1
  181. package/dist/resolution/name-matcher.d.ts +5 -0
  182. package/dist/resolution/name-matcher.d.ts.map +1 -1
  183. package/dist/resolution/name-matcher.js +148 -8
  184. package/dist/resolution/name-matcher.js.map +1 -1
  185. package/dist/resolution/types.d.ts +1 -1
  186. package/dist/resolution/types.d.ts.map +1 -1
  187. package/dist/search/query-utils.d.ts +26 -1
  188. package/dist/search/query-utils.d.ts.map +1 -1
  189. package/dist/search/query-utils.js +209 -9
  190. package/dist/search/query-utils.js.map +1 -1
  191. package/dist/sync/index.d.ts +2 -4
  192. package/dist/sync/index.d.ts.map +1 -1
  193. package/dist/sync/index.js +4 -3
  194. package/dist/sync/index.js.map +1 -1
  195. package/dist/sync/watcher.d.ts +81 -0
  196. package/dist/sync/watcher.d.ts.map +1 -0
  197. package/dist/sync/watcher.js +184 -0
  198. package/dist/sync/watcher.js.map +1 -0
  199. package/dist/types.d.ts +2 -2
  200. package/dist/types.d.ts.map +1 -1
  201. package/dist/types.js +0 -1
  202. package/dist/types.js.map +1 -1
  203. package/dist/ui/shimmer-progress.d.ts +11 -0
  204. package/dist/ui/shimmer-progress.d.ts.map +1 -0
  205. package/dist/ui/shimmer-progress.js +90 -0
  206. package/dist/ui/shimmer-progress.js.map +1 -0
  207. package/dist/ui/shimmer-worker.d.ts +2 -0
  208. package/dist/ui/shimmer-worker.d.ts.map +1 -0
  209. package/dist/ui/shimmer-worker.js +112 -0
  210. package/dist/ui/shimmer-worker.js.map +1 -0
  211. package/dist/ui/types.d.ts +17 -0
  212. package/dist/ui/types.d.ts.map +1 -0
  213. package/dist/ui/types.js +3 -0
  214. package/dist/ui/types.js.map +1 -0
  215. package/dist/vectors/embedder.js +1 -1
  216. package/dist/vectors/embedder.js.map +1 -1
  217. package/dist/visualizer/server.d.ts.map +1 -1
  218. package/dist/visualizer/server.js +3 -11
  219. package/dist/visualizer/server.js.map +1 -1
  220. package/package.json +7 -12
  221. package/scripts/postinstall.js +0 -68
@@ -16,11 +16,6 @@
16
16
  * codegraph files [options] Show project file structure
17
17
  * codegraph context <task> Build context for a task
18
18
  * codegraph affected [files] Find test files affected by changes
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.
24
19
  */
25
20
  export {};
26
21
  //# sourceMappingURL=codegraph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codegraph.d.ts","sourceRoot":"","sources":["../../src/bin/codegraph.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
1
+ {"version":3,"file":"codegraph.d.ts","sourceRoot":"","sources":["../../src/bin/codegraph.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -17,11 +17,6 @@
17
17
  * codegraph files [options] Show project file structure
18
18
  * codegraph context <task> Build context for a task
19
19
  * codegraph affected [files] Find test files affected by changes
20
- * codegraph mark-dirty [path] Mark project as needing sync (hooks)
21
- * codegraph sync-if-dirty [path] Sync if marked dirty (hooks)
22
- *
23
- * Note: Git hooks have been removed. CodeGraph sync is triggered automatically
24
- * through codegraph's Claude Code hooks integration.
25
20
  */
26
21
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
22
  if (k2 === undefined) k2 = k;
@@ -60,9 +55,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
60
55
  const commander_1 = require("commander");
61
56
  const path = __importStar(require("path"));
62
57
  const fs = __importStar(require("fs"));
63
- const child_process_1 = require("child_process");
64
58
  const directory_1 = require("../directory");
65
- const sentry_1 = require("../sentry");
59
+ const shimmer_progress_1 = require("../ui/shimmer-progress");
66
60
  // Lazy-load heavy modules (CodeGraph, runInstaller) to keep CLI startup fast.
67
61
  async function loadCodeGraph() {
68
62
  try {
@@ -77,20 +71,21 @@ async function loadCodeGraph() {
77
71
  process.exit(1);
78
72
  }
79
73
  }
74
+ // Dynamic import helper — tsc compiles import() to require() in CJS mode,
75
+ // which fails for ESM-only packages. This bypasses the transformation.
76
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
77
+ const importESM = new Function('specifier', 'return import(specifier)');
78
+ // Warn about unsupported Node.js versions (Node 25+ has V8 turboshaft WASM bugs)
79
+ const nodeVersion = process.versions.node;
80
+ const nodeMajor = parseInt(nodeVersion.split('.')[0] ?? '0', 10);
81
+ if (nodeMajor >= 25) {
82
+ console.warn('\x1b[33m⚠\x1b[0m CodeGraph may crash on Node.js %s due to a V8 WASM compiler bug in Node 25+.', nodeVersion);
83
+ console.warn(' Please use Node.js 22 LTS instead: https://nodejs.org/en/download');
84
+ console.warn(' See: https://github.com/colbymchenry/codegraph/issues/81\n');
85
+ }
80
86
  // Check if running with no arguments - run installer
81
- // Read version for Sentry release tag
82
- const pkgVersion = (() => {
83
- try {
84
- return JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8')).version;
85
- }
86
- catch {
87
- return undefined;
88
- }
89
- })();
90
- (0, sentry_1.initSentry)({ processName: 'codegraph-cli', version: pkgVersion });
91
87
  if (process.argv.length === 2) {
92
88
  Promise.resolve().then(() => __importStar(require('../installer'))).then(({ runInstaller }) => runInstaller()).catch((err) => {
93
- (0, sentry_1.captureException)(err);
94
89
  console.error('Installation failed:', err instanceof Error ? err.message : String(err));
95
90
  process.exit(1);
96
91
  });
@@ -100,11 +95,9 @@ else {
100
95
  main();
101
96
  }
102
97
  process.on('uncaughtException', (error) => {
103
- (0, sentry_1.captureException)(error);
104
98
  console.error('[CodeGraph] Uncaught exception:', error);
105
99
  });
106
100
  process.on('unhandledRejection', (reason) => {
107
- (0, sentry_1.captureException)(reason);
108
101
  console.error('[CodeGraph] Unhandled rejection:', reason);
109
102
  });
110
103
  function main() {
@@ -192,32 +185,38 @@ function main() {
192
185
  const remainingSeconds = seconds % 60;
193
186
  return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
194
187
  }
188
+ // Shimmer progress renderer (runs in a worker thread for smooth animation)
189
+ // Imported at top of file from '../ui/shimmer-progress'
195
190
  /**
196
- * Create a progress bar string
197
- */
198
- function progressBar(current, total, width = 30) {
199
- const percent = total > 0 ? current / total : 0;
200
- const filled = Math.round(width * percent);
201
- const empty = width - filled;
202
- const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
203
- const percentStr = `${Math.round(percent * 100)}%`.padStart(4);
204
- return `${bar} ${percentStr}`;
205
- }
206
- /**
207
- * Print a progress update (overwrites current line)
191
+ * Create a plain-text progress callback for --verbose mode.
192
+ * No animations, no ANSI tricks — just timestamped lines to stdout.
208
193
  */
209
- function printProgress(progress) {
210
- const phaseNames = {
211
- scanning: 'Scanning files',
212
- parsing: 'Parsing code',
213
- storing: 'Storing data',
214
- resolving: 'Resolving refs',
194
+ function createVerboseProgress() {
195
+ let lastPhase = '';
196
+ let lastPct = -1;
197
+ const startTime = Date.now();
198
+ return (progress) => {
199
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
200
+ if (progress.phase !== lastPhase) {
201
+ lastPhase = progress.phase;
202
+ lastPct = -1;
203
+ console.log(`[${elapsed}s] Phase: ${progress.phase}`);
204
+ }
205
+ if (progress.total > 0) {
206
+ const pct = Math.floor((progress.current / progress.total) * 100);
207
+ // Log every 5% to keep output manageable
208
+ if (pct >= lastPct + 5 || progress.current === progress.total) {
209
+ lastPct = pct;
210
+ console.log(`[${elapsed}s] ${progress.current}/${progress.total} (${pct}%)${progress.currentFile ? ` — ${progress.currentFile}` : ''}`);
211
+ }
212
+ }
213
+ else if (progress.current > 0) {
214
+ // Scanning phase (no total yet) — log periodically
215
+ if (progress.current % 1000 === 0 || progress.current === 1) {
216
+ console.log(`[${elapsed}s] ${formatNumber(progress.current)} files found`);
217
+ }
218
+ }
215
219
  };
216
- const phaseName = phaseNames[progress.phase] || progress.phase;
217
- const bar = progressBar(progress.current, progress.total);
218
- const file = progress.currentFile ? chalk.dim(` ${progress.currentFile}`) : '';
219
- // Clear line and print progress
220
- process.stdout.write(`\r${chalk.cyan(phaseName)}: ${bar}${file}`.padEnd(100));
221
220
  }
222
221
  /**
223
222
  * Print success message
@@ -243,6 +242,102 @@ function main() {
243
242
  function warn(message) {
244
243
  console.log(chalk.yellow('⚠') + ' ' + message);
245
244
  }
245
+ /**
246
+ * Print indexing results using clack log methods
247
+ */
248
+ function printIndexResult(clack, result, projectPath) {
249
+ const hasErrors = result.filesErrored > 0;
250
+ if (result.filesIndexed > 0) {
251
+ if (hasErrors) {
252
+ clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files (${formatNumber(result.filesErrored)} could not be parsed)`);
253
+ }
254
+ else {
255
+ clack.log.success(`Indexed ${formatNumber(result.filesIndexed)} files`);
256
+ }
257
+ clack.log.info(`${formatNumber(result.nodesCreated)} nodes, ${formatNumber(result.edgesCreated)} edges in ${formatDuration(result.durationMs)}`);
258
+ }
259
+ else if (hasErrors) {
260
+ clack.log.error(`Indexing failed — all ${formatNumber(result.filesErrored)} files had errors`);
261
+ }
262
+ else {
263
+ clack.log.warn('No files found to index');
264
+ }
265
+ if (hasErrors) {
266
+ const errorsByCode = new Map();
267
+ for (const err of result.errors) {
268
+ if (err.severity === 'error') {
269
+ const code = err.code || 'unknown';
270
+ errorsByCode.set(code, (errorsByCode.get(code) || 0) + 1);
271
+ }
272
+ }
273
+ const codeLabels = {
274
+ parse_error: 'files failed to parse',
275
+ read_error: 'files could not be read',
276
+ size_exceeded: 'files exceeded size limit',
277
+ path_traversal: 'blocked paths',
278
+ unsupported_language: 'unsupported language',
279
+ parser_error: 'parser initialization failures',
280
+ };
281
+ const breakdown = Array.from(errorsByCode)
282
+ .map(([code, count]) => `${formatNumber(count)} ${codeLabels[code] || code}`)
283
+ .join('\n');
284
+ clack.note(breakdown, 'Error breakdown');
285
+ if (projectPath) {
286
+ writeErrorLog(projectPath, result.errors);
287
+ clack.log.info('See .codegraph/errors.log for details');
288
+ }
289
+ if (result.filesIndexed > 0) {
290
+ clack.log.info('The index is fully usable — only the failed files are missing.');
291
+ }
292
+ }
293
+ else if (projectPath) {
294
+ const logPath = path.join(projectPath, '.codegraph', 'errors.log');
295
+ if (fs.existsSync(logPath)) {
296
+ fs.unlinkSync(logPath);
297
+ }
298
+ }
299
+ }
300
+ /**
301
+ * Write detailed error log to .codegraph/errors.log
302
+ */
303
+ function writeErrorLog(projectPath, errors) {
304
+ const cgDir = path.join(projectPath, '.codegraph');
305
+ if (!fs.existsSync(cgDir))
306
+ return;
307
+ const logPath = path.join(cgDir, 'errors.log');
308
+ // Group errors by file path
309
+ const errorsByFile = new Map();
310
+ const noFileErrors = [];
311
+ for (const err of errors) {
312
+ if (err.severity !== 'error')
313
+ continue;
314
+ if (err.filePath) {
315
+ let list = errorsByFile.get(err.filePath);
316
+ if (!list) {
317
+ list = [];
318
+ errorsByFile.set(err.filePath, list);
319
+ }
320
+ list.push({ message: err.message, code: err.code });
321
+ }
322
+ else {
323
+ noFileErrors.push({ message: err.message, code: err.code });
324
+ }
325
+ }
326
+ const lines = [
327
+ `CodeGraph Error Log — ${new Date().toISOString()}`,
328
+ `${errorsByFile.size} files with errors`,
329
+ '',
330
+ ];
331
+ for (const [filePath, fileErrors] of errorsByFile) {
332
+ for (const err of fileErrors) {
333
+ lines.push(`${filePath}: ${err.message}`);
334
+ }
335
+ }
336
+ for (const err of noFileErrors) {
337
+ lines.push(err.message);
338
+ }
339
+ fs.writeFileSync(logPath, lines.join('\n') + '\n');
340
+ }
246
341
  // =============================================================================
247
342
  // Commands
248
343
  // =============================================================================
@@ -253,48 +348,47 @@ function main() {
253
348
  .command('init [path]')
254
349
  .description('Initialize CodeGraph in a project directory')
255
350
  .option('-i, --index', 'Run initial indexing after initialization')
351
+ .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
256
352
  .action(async (pathArg, options) => {
257
- const projectPath = resolveProjectPath(pathArg);
258
- console.log(chalk.bold('\nInitializing CodeGraph...\n'));
353
+ const projectPath = path.resolve(pathArg || process.cwd());
354
+ const clack = await importESM('@clack/prompts');
355
+ clack.intro('Initializing CodeGraph');
259
356
  try {
260
- // Check if already initialized
261
357
  if ((0, directory_1.isInitialized)(projectPath)) {
262
- warn(`CodeGraph already initialized in ${projectPath}`);
263
- info('Use "codegraph index" to re-index or "codegraph sync" to update');
358
+ clack.log.warn(`Already initialized in ${projectPath}`);
359
+ clack.log.info('Use "codegraph index" to re-index or "codegraph sync" to update');
360
+ clack.outro('');
264
361
  return;
265
362
  }
266
- // Initialize
267
363
  const { default: CodeGraph } = await loadCodeGraph();
268
- const cg = await CodeGraph.init(projectPath, {
269
- index: false, // We'll handle indexing ourselves for progress
270
- });
271
- success(`Initialized CodeGraph in ${projectPath}`);
272
- info(`Created .codegraph/ directory`);
273
- // Run initial index if requested
364
+ const cg = await CodeGraph.init(projectPath, { index: false });
365
+ clack.log.success(`Initialized in ${projectPath}`);
274
366
  if (options.index) {
275
- console.log('\nIndexing project...\n');
276
- const result = await cg.indexAll({
277
- onProgress: printProgress,
278
- });
279
- // Clear progress line
280
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
281
- if (result.success) {
282
- success(`Indexed ${formatNumber(result.filesIndexed)} files`);
283
- info(`Created ${formatNumber(result.nodesCreated)} nodes and ${formatNumber(result.edgesCreated)} edges`);
284
- info(`Completed in ${formatDuration(result.durationMs)}`);
367
+ let result;
368
+ if (options.verbose) {
369
+ result = await cg.indexAll({
370
+ onProgress: createVerboseProgress(),
371
+ verbose: true,
372
+ });
285
373
  }
286
374
  else {
287
- warn(`Indexing completed with ${result.errors.length} errors`);
375
+ process.stdout.write(`${colors.dim}│${colors.reset}\n`);
376
+ const progress = (0, shimmer_progress_1.createShimmerProgress)();
377
+ result = await cg.indexAll({
378
+ onProgress: progress.onProgress,
379
+ });
380
+ await progress.stop();
288
381
  }
382
+ printIndexResult(clack, result, projectPath);
289
383
  }
290
384
  else {
291
- info('Run "codegraph index" to index the project');
385
+ clack.log.info('Run "codegraph index" to index the project');
292
386
  }
387
+ clack.outro('Done');
293
388
  cg.destroy();
294
389
  }
295
390
  catch (err) {
296
- (0, sentry_1.captureException)(err);
297
- error(`Failed to initialize: ${err instanceof Error ? err.message : String(err)}`);
391
+ clack.log.error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
298
392
  process.exit(1);
299
393
  }
300
394
  });
@@ -331,7 +425,6 @@ function main() {
331
425
  success(`Removed CodeGraph from ${projectPath}`);
332
426
  }
333
427
  catch (err) {
334
- (0, sentry_1.captureException)(err);
335
428
  error(`Failed to uninitialize: ${err instanceof Error ? err.message : String(err)}`);
336
429
  process.exit(1);
337
430
  }
@@ -344,6 +437,7 @@ function main() {
344
437
  .description('Index all files in the project')
345
438
  .option('-f, --force', 'Force full re-index even if already indexed')
346
439
  .option('-q, --quiet', 'Suppress progress output')
440
+ .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
347
441
  .action(async (pathArg, options) => {
348
442
  const projectPath = resolveProjectPath(pathArg);
349
443
  try {
@@ -354,46 +448,45 @@ function main() {
354
448
  }
355
449
  const { default: CodeGraph } = await loadCodeGraph();
356
450
  const cg = await CodeGraph.open(projectPath);
357
- if (!options.quiet) {
358
- console.log(chalk.bold('\nIndexing project...\n'));
451
+ if (options.quiet) {
452
+ // Quiet mode: no UI, just run
453
+ if (options.force)
454
+ cg.clear();
455
+ const result = await cg.indexAll();
456
+ if (!result.success)
457
+ process.exit(1);
458
+ cg.destroy();
459
+ return;
359
460
  }
360
- // Clear existing data if force
461
+ const clack = await importESM('@clack/prompts');
462
+ clack.intro('Indexing project');
361
463
  if (options.force) {
362
464
  cg.clear();
363
- if (!options.quiet) {
364
- info('Cleared existing index');
365
- }
366
- }
367
- const result = await cg.indexAll({
368
- onProgress: options.quiet ? undefined : printProgress,
369
- });
370
- // Clear progress line
371
- if (!options.quiet) {
372
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
465
+ clack.log.info('Cleared existing index');
373
466
  }
374
- if (result.success) {
375
- if (!options.quiet) {
376
- success(`Indexed ${formatNumber(result.filesIndexed)} files`);
377
- info(`Created ${formatNumber(result.nodesCreated)} nodes and ${formatNumber(result.edgesCreated)} edges`);
378
- info(`Completed in ${formatDuration(result.durationMs)}`);
379
- }
467
+ let result;
468
+ if (options.verbose) {
469
+ result = await cg.indexAll({
470
+ onProgress: createVerboseProgress(),
471
+ verbose: true,
472
+ });
380
473
  }
381
474
  else {
382
- if (!options.quiet) {
383
- warn(`Indexing completed with ${result.errors.length} errors`);
384
- for (const err of result.errors.slice(0, 5)) {
385
- console.log(chalk.dim(` - ${err.message}`));
386
- }
387
- if (result.errors.length > 5) {
388
- console.log(chalk.dim(` ... and ${result.errors.length - 5} more`));
389
- }
390
- }
475
+ process.stdout.write(`${colors.dim}│${colors.reset}\n`);
476
+ const progress = (0, shimmer_progress_1.createShimmerProgress)();
477
+ result = await cg.indexAll({
478
+ onProgress: progress.onProgress,
479
+ });
480
+ await progress.stop();
481
+ }
482
+ printIndexResult(clack, result, projectPath);
483
+ if (!result.success) {
391
484
  process.exit(1);
392
485
  }
486
+ clack.outro('Done');
393
487
  cg.destroy();
394
488
  }
395
489
  catch (err) {
396
- (0, sentry_1.captureException)(err);
397
490
  error(`Failed to index: ${err instanceof Error ? err.message : String(err)}`);
398
491
  process.exit(1);
399
492
  }
@@ -416,36 +509,38 @@ function main() {
416
509
  }
417
510
  const { default: CodeGraph } = await loadCodeGraph();
418
511
  const cg = await CodeGraph.open(projectPath);
512
+ if (options.quiet) {
513
+ await cg.sync();
514
+ cg.destroy();
515
+ return;
516
+ }
517
+ const clack = await importESM('@clack/prompts');
518
+ clack.intro('Syncing CodeGraph');
519
+ process.stdout.write(`${colors.dim}│${colors.reset}\n`);
520
+ const progress = (0, shimmer_progress_1.createShimmerProgress)();
419
521
  const result = await cg.sync({
420
- onProgress: options.quiet ? undefined : printProgress,
522
+ onProgress: progress.onProgress,
421
523
  });
422
- // Clear progress line
423
- if (!options.quiet) {
424
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
425
- }
524
+ await progress.stop();
426
525
  const totalChanges = result.filesAdded + result.filesModified + result.filesRemoved;
427
- if (!options.quiet) {
428
- if (totalChanges === 0) {
429
- success('Already up to date');
430
- }
431
- else {
432
- success(`Synced ${formatNumber(totalChanges)} changed files`);
433
- if (result.filesAdded > 0) {
434
- info(` Added: ${result.filesAdded}`);
435
- }
436
- if (result.filesModified > 0) {
437
- info(` Modified: ${result.filesModified}`);
438
- }
439
- if (result.filesRemoved > 0) {
440
- info(` Removed: ${result.filesRemoved}`);
441
- }
442
- info(`Updated ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
443
- }
526
+ if (totalChanges === 0) {
527
+ clack.log.info('Already up to date');
528
+ }
529
+ else {
530
+ clack.log.success(`Synced ${formatNumber(totalChanges)} changed files`);
531
+ const details = [];
532
+ if (result.filesAdded > 0)
533
+ details.push(`Added: ${result.filesAdded}`);
534
+ if (result.filesModified > 0)
535
+ details.push(`Modified: ${result.filesModified}`);
536
+ if (result.filesRemoved > 0)
537
+ details.push(`Removed: ${result.filesRemoved}`);
538
+ clack.log.info(`${details.join(', ')} — ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
444
539
  }
540
+ clack.outro('Done');
445
541
  cg.destroy();
446
542
  }
447
543
  catch (err) {
448
- (0, sentry_1.captureException)(err);
449
544
  if (!options.quiet) {
450
545
  error(`Failed to sync: ${err instanceof Error ? err.message : String(err)}`);
451
546
  }
@@ -548,7 +643,6 @@ function main() {
548
643
  cg.destroy();
549
644
  }
550
645
  catch (err) {
551
- (0, sentry_1.captureException)(err);
552
646
  error(`Failed to get status: ${err instanceof Error ? err.message : String(err)}`);
553
647
  process.exit(1);
554
648
  }
@@ -604,7 +698,6 @@ function main() {
604
698
  cg.destroy();
605
699
  }
606
700
  catch (err) {
607
- (0, sentry_1.captureException)(err);
608
701
  error(`Search failed: ${err instanceof Error ? err.message : String(err)}`);
609
702
  process.exit(1);
610
703
  }
@@ -712,7 +805,6 @@ function main() {
712
805
  cg.destroy();
713
806
  }
714
807
  catch (err) {
715
- (0, sentry_1.captureException)(err);
716
808
  error(`Failed to list files: ${err instanceof Error ? err.message : String(err)}`);
717
809
  process.exit(1);
718
810
  }
@@ -809,7 +901,6 @@ function main() {
809
901
  cg.destroy();
810
902
  }
811
903
  catch (err) {
812
- (0, sentry_1.captureException)(err);
813
904
  error(`Failed to build context: ${err instanceof Error ? err.message : String(err)}`);
814
905
  process.exit(1);
815
906
  }
@@ -860,145 +951,10 @@ function main() {
860
951
  }
861
952
  }
862
953
  catch (err) {
863
- (0, sentry_1.captureException)(err);
864
954
  error(`Failed to start server: ${err instanceof Error ? err.message : String(err)}`);
865
955
  process.exit(1);
866
956
  }
867
957
  });
868
- /**
869
- * codegraph visualize [path]
870
- */
871
- program
872
- .command('visualize [path]')
873
- .description('Open interactive graph visualization in your browser')
874
- .option('-p, --port <port>', 'Port to listen on (default: auto)', parseInt)
875
- .option('--no-open', 'Do not open browser automatically')
876
- .action(async (pathArg, options) => {
877
- const projectPath = resolveProjectPath(pathArg);
878
- try {
879
- if (!(0, directory_1.isInitialized)(projectPath)) {
880
- error(`CodeGraph not initialized in ${projectPath}`);
881
- info('Run "codegraph init -i" first');
882
- process.exit(1);
883
- }
884
- const { default: CodeGraph } = await loadCodeGraph();
885
- const cg = await CodeGraph.open(projectPath);
886
- const stats = cg.getStats();
887
- console.log(chalk.bold('\n CodeGraph Explorer\n'));
888
- info(`Project: ${projectPath}`);
889
- info(`Indexed: ${formatNumber(stats.nodeCount)} nodes, ${formatNumber(stats.edgeCount)} edges, ${formatNumber(stats.fileCount)} files\n`);
890
- const { VisualizerServer } = await Promise.resolve().then(() => __importStar(require('../visualizer/server')));
891
- const server = new VisualizerServer(cg);
892
- const { url } = await server.start({ port: options.port, openBrowser: options.open !== false });
893
- success(`Visualizer running at ${chalk.cyan(url)}`);
894
- console.log(chalk.dim(' Press Ctrl+C to stop\n'));
895
- // Open browser
896
- if (options.open !== false) {
897
- const openCmd = process.platform === 'darwin' ? 'open' :
898
- process.platform === 'win32' ? 'start' : 'xdg-open';
899
- (0, child_process_1.spawn)(openCmd, [url], { detached: true, stdio: 'ignore' }).unref();
900
- }
901
- // Handle shutdown — force exit on second Ctrl+C
902
- let shuttingDown = false;
903
- const shutdown = () => {
904
- if (shuttingDown) {
905
- process.exit(1);
906
- }
907
- shuttingDown = true;
908
- console.log(chalk.dim('\n Shutting down...'));
909
- server.stop().then(() => {
910
- cg.close();
911
- process.exit(0);
912
- }).catch(() => process.exit(1));
913
- // Force exit after 2s if graceful shutdown hangs
914
- setTimeout(() => process.exit(1), 2000).unref();
915
- };
916
- process.on('SIGINT', shutdown);
917
- process.on('SIGTERM', shutdown);
918
- }
919
- catch (err) {
920
- (0, sentry_1.captureException)(err);
921
- error(`Failed to start visualizer: ${err instanceof Error ? err.message : String(err)}`);
922
- process.exit(1);
923
- }
924
- });
925
- /**
926
- * codegraph mark-dirty [path]
927
- *
928
- * Touches .codegraph/.dirty to signal that files have changed.
929
- * Used by Claude Code PostToolUse hooks to batch syncs.
930
- * Runs silently and always exits 0.
931
- */
932
- program
933
- .command('mark-dirty [path]')
934
- .description('Mark project as needing sync (used by Claude Code hooks)')
935
- .action(async (pathArg) => {
936
- try {
937
- const startPath = path.resolve(pathArg || process.cwd());
938
- const projectRoot = (0, directory_1.findNearestCodeGraphRoot)(startPath);
939
- if (!projectRoot) {
940
- // No .codegraph/ found — exit silently
941
- process.exit(0);
942
- }
943
- const dirtyPath = path.join((0, directory_1.getCodeGraphDir)(projectRoot), '.dirty');
944
- fs.writeFileSync(dirtyPath, Date.now().toString(), 'utf-8');
945
- }
946
- catch {
947
- // Never fail — this runs in the background during edits
948
- }
949
- process.exit(0);
950
- });
951
- /**
952
- * codegraph sync-if-dirty [path]
953
- *
954
- * Checks if .codegraph/.dirty exists and, if so, spawns a detached
955
- * background process to run `codegraph sync`. The hook process exits
956
- * immediately so Claude Code's Stop hook never blocks.
957
- *
958
- * Removes the marker BEFORE spawning so edits during sync
959
- * create a new marker for the next Stop event.
960
- * Runs silently and always exits 0.
961
- */
962
- program
963
- .command('sync-if-dirty [path]')
964
- .description('Sync if project was marked dirty (used by Claude Code hooks)')
965
- .action(async (pathArg) => {
966
- try {
967
- const startPath = path.resolve(pathArg || process.cwd());
968
- const projectRoot = (0, directory_1.findNearestCodeGraphRoot)(startPath);
969
- if (!projectRoot) {
970
- process.exit(0);
971
- }
972
- const dirtyPath = path.join((0, directory_1.getCodeGraphDir)(projectRoot), '.dirty');
973
- // No marker → nothing to do (sub-ms exit)
974
- if (!fs.existsSync(dirtyPath)) {
975
- process.exit(0);
976
- }
977
- // Remove marker FIRST so edits during sync create a new one
978
- try {
979
- fs.unlinkSync(dirtyPath);
980
- }
981
- catch { /* ignore */ }
982
- // If not fully initialized (no DB), exit
983
- if (!(0, directory_1.isInitialized)(projectRoot)) {
984
- process.exit(0);
985
- }
986
- // Spawn sync as a detached background process
987
- // so this hook exits immediately and doesn't block Claude Code.
988
- // Uses process.argv[0]/[1] (e.g. node /path/to/codegraph.js) so it
989
- // works whether invoked via global install, npx, or directly.
990
- const child = (0, child_process_1.spawn)(process.argv[0], [process.argv[1], 'sync', '--quiet', projectRoot], {
991
- detached: true,
992
- stdio: 'ignore',
993
- windowsHide: true,
994
- });
995
- child.unref();
996
- }
997
- catch {
998
- // Never fail — this runs at the end of Claude responses
999
- }
1000
- process.exit(0);
1001
- });
1002
958
  /**
1003
959
  * codegraph unlock [path]
1004
960
  */
@@ -1021,7 +977,6 @@ function main() {
1021
977
  success('Removed lock file. You can now run indexing again.');
1022
978
  }
1023
979
  catch (err) {
1024
- (0, sentry_1.captureException)(err);
1025
980
  error(`Failed to remove lock: ${err instanceof Error ? err.message : String(err)}`);
1026
981
  process.exit(1);
1027
982
  }
@@ -1152,7 +1107,6 @@ function main() {
1152
1107
  cg.destroy();
1153
1108
  }
1154
1109
  catch (err) {
1155
- (0, sentry_1.captureException)(err);
1156
1110
  error(`Affected analysis failed: ${err instanceof Error ? err.message : String(err)}`);
1157
1111
  process.exit(1);
1158
1112
  }