@colbymchenry/codegraph 0.6.8 → 0.7.3

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 (207) hide show
  1. package/README.md +179 -476
  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 -237
  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/context/index.d.ts +3 -5
  11. package/dist/context/index.d.ts.map +1 -1
  12. package/dist/context/index.js +531 -52
  13. package/dist/context/index.js.map +1 -1
  14. package/dist/db/migrations.d.ts +1 -1
  15. package/dist/db/migrations.d.ts.map +1 -1
  16. package/dist/db/migrations.js +10 -1
  17. package/dist/db/migrations.js.map +1 -1
  18. package/dist/db/queries.d.ts +53 -0
  19. package/dist/db/queries.d.ts.map +1 -1
  20. package/dist/db/queries.js +244 -14
  21. package/dist/db/queries.js.map +1 -1
  22. package/dist/db/schema.sql +1 -16
  23. package/dist/extraction/dfm-extractor.d.ts +31 -0
  24. package/dist/extraction/dfm-extractor.d.ts.map +1 -0
  25. package/dist/extraction/dfm-extractor.js +151 -0
  26. package/dist/extraction/dfm-extractor.js.map +1 -0
  27. package/dist/extraction/grammars.d.ts +9 -1
  28. package/dist/extraction/grammars.d.ts.map +1 -1
  29. package/dist/extraction/grammars.js +34 -2
  30. package/dist/extraction/grammars.js.map +1 -1
  31. package/dist/extraction/index.d.ts +7 -1
  32. package/dist/extraction/index.d.ts.map +1 -1
  33. package/dist/extraction/index.js +373 -22
  34. package/dist/extraction/index.js.map +1 -1
  35. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  36. package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
  37. package/dist/extraction/languages/c-cpp.js +126 -0
  38. package/dist/extraction/languages/c-cpp.js.map +1 -0
  39. package/dist/extraction/languages/csharp.d.ts +3 -0
  40. package/dist/extraction/languages/csharp.d.ts.map +1 -0
  41. package/dist/extraction/languages/csharp.js +72 -0
  42. package/dist/extraction/languages/csharp.js.map +1 -0
  43. package/dist/extraction/languages/dart.d.ts +3 -0
  44. package/dist/extraction/languages/dart.d.ts.map +1 -0
  45. package/dist/extraction/languages/dart.js +192 -0
  46. package/dist/extraction/languages/dart.js.map +1 -0
  47. package/dist/extraction/languages/go.d.ts +3 -0
  48. package/dist/extraction/languages/go.d.ts.map +1 -0
  49. package/dist/extraction/languages/go.js +58 -0
  50. package/dist/extraction/languages/go.js.map +1 -0
  51. package/dist/extraction/languages/index.d.ts +10 -0
  52. package/dist/extraction/languages/index.d.ts.map +1 -0
  53. package/dist/extraction/languages/index.js +43 -0
  54. package/dist/extraction/languages/index.js.map +1 -0
  55. package/dist/extraction/languages/java.d.ts +3 -0
  56. package/dist/extraction/languages/java.d.ts.map +1 -0
  57. package/dist/extraction/languages/java.js +64 -0
  58. package/dist/extraction/languages/java.js.map +1 -0
  59. package/dist/extraction/languages/javascript.d.ts +3 -0
  60. package/dist/extraction/languages/javascript.d.ts.map +1 -0
  61. package/dist/extraction/languages/javascript.js +90 -0
  62. package/dist/extraction/languages/javascript.js.map +1 -0
  63. package/dist/extraction/languages/kotlin.d.ts +3 -0
  64. package/dist/extraction/languages/kotlin.d.ts.map +1 -0
  65. package/dist/extraction/languages/kotlin.js +253 -0
  66. package/dist/extraction/languages/kotlin.js.map +1 -0
  67. package/dist/extraction/languages/pascal.d.ts +3 -0
  68. package/dist/extraction/languages/pascal.d.ts.map +1 -0
  69. package/dist/extraction/languages/pascal.js +66 -0
  70. package/dist/extraction/languages/pascal.js.map +1 -0
  71. package/dist/extraction/languages/php.d.ts +3 -0
  72. package/dist/extraction/languages/php.d.ts.map +1 -0
  73. package/dist/extraction/languages/php.js +107 -0
  74. package/dist/extraction/languages/php.js.map +1 -0
  75. package/dist/extraction/languages/python.d.ts +3 -0
  76. package/dist/extraction/languages/python.d.ts.map +1 -0
  77. package/dist/extraction/languages/python.js +56 -0
  78. package/dist/extraction/languages/python.js.map +1 -0
  79. package/dist/extraction/languages/ruby.d.ts +3 -0
  80. package/dist/extraction/languages/ruby.d.ts.map +1 -0
  81. package/dist/extraction/languages/ruby.js +114 -0
  82. package/dist/extraction/languages/ruby.js.map +1 -0
  83. package/dist/extraction/languages/rust.d.ts +3 -0
  84. package/dist/extraction/languages/rust.d.ts.map +1 -0
  85. package/dist/extraction/languages/rust.js +109 -0
  86. package/dist/extraction/languages/rust.js.map +1 -0
  87. package/dist/extraction/languages/swift.d.ts +3 -0
  88. package/dist/extraction/languages/swift.d.ts.map +1 -0
  89. package/dist/extraction/languages/swift.js +91 -0
  90. package/dist/extraction/languages/swift.js.map +1 -0
  91. package/dist/extraction/languages/typescript.d.ts +3 -0
  92. package/dist/extraction/languages/typescript.d.ts.map +1 -0
  93. package/dist/extraction/languages/typescript.js +129 -0
  94. package/dist/extraction/languages/typescript.js.map +1 -0
  95. package/dist/extraction/liquid-extractor.d.ts +52 -0
  96. package/dist/extraction/liquid-extractor.d.ts.map +1 -0
  97. package/dist/extraction/liquid-extractor.js +313 -0
  98. package/dist/extraction/liquid-extractor.js.map +1 -0
  99. package/dist/extraction/parse-worker.d.ts +8 -0
  100. package/dist/extraction/parse-worker.d.ts.map +1 -0
  101. package/dist/extraction/parse-worker.js +57 -0
  102. package/dist/extraction/parse-worker.js.map +1 -0
  103. package/dist/extraction/svelte-extractor.d.ts +56 -0
  104. package/dist/extraction/svelte-extractor.d.ts.map +1 -0
  105. package/dist/extraction/svelte-extractor.js +272 -0
  106. package/dist/extraction/svelte-extractor.js.map +1 -0
  107. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  108. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
  109. package/dist/extraction/tree-sitter-helpers.js +103 -0
  110. package/dist/extraction/tree-sitter-helpers.js.map +1 -0
  111. package/dist/extraction/tree-sitter-types.d.ts +179 -0
  112. package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
  113. package/dist/extraction/tree-sitter-types.js +10 -0
  114. package/dist/extraction/tree-sitter-types.js.map +1 -0
  115. package/dist/extraction/tree-sitter.d.ts +67 -125
  116. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  117. package/dist/extraction/tree-sitter.js +1052 -1855
  118. package/dist/extraction/tree-sitter.js.map +1 -1
  119. package/dist/graph/traversal.d.ts.map +1 -1
  120. package/dist/graph/traversal.js +27 -3
  121. package/dist/graph/traversal.js.map +1 -1
  122. package/dist/index.d.ts +29 -53
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +88 -114
  125. package/dist/index.js.map +1 -1
  126. package/dist/installer/claude-md-template.d.ts +1 -1
  127. package/dist/installer/claude-md-template.d.ts.map +1 -1
  128. package/dist/installer/claude-md-template.js +15 -15
  129. package/dist/installer/config-writer.d.ts +1 -10
  130. package/dist/installer/config-writer.d.ts.map +1 -1
  131. package/dist/installer/config-writer.js +0 -79
  132. package/dist/installer/config-writer.js.map +1 -1
  133. package/dist/installer/index.d.ts +3 -4
  134. package/dist/installer/index.d.ts.map +1 -1
  135. package/dist/installer/index.js +118 -116
  136. package/dist/installer/index.js.map +1 -1
  137. package/dist/mcp/index.d.ts +5 -0
  138. package/dist/mcp/index.d.ts.map +1 -1
  139. package/dist/mcp/index.js +25 -1
  140. package/dist/mcp/index.js.map +1 -1
  141. package/dist/mcp/tools.d.ts +33 -0
  142. package/dist/mcp/tools.d.ts.map +1 -1
  143. package/dist/mcp/tools.js +432 -21
  144. package/dist/mcp/tools.js.map +1 -1
  145. package/dist/resolution/frameworks/csharp.js +29 -84
  146. package/dist/resolution/frameworks/csharp.js.map +1 -1
  147. package/dist/resolution/frameworks/express.js +44 -48
  148. package/dist/resolution/frameworks/express.js.map +1 -1
  149. package/dist/resolution/frameworks/go.js +34 -70
  150. package/dist/resolution/frameworks/go.js.map +1 -1
  151. package/dist/resolution/frameworks/java.js +29 -87
  152. package/dist/resolution/frameworks/java.js.map +1 -1
  153. package/dist/resolution/frameworks/laravel.js +6 -6
  154. package/dist/resolution/frameworks/laravel.js.map +1 -1
  155. package/dist/resolution/frameworks/python.js +33 -98
  156. package/dist/resolution/frameworks/python.js.map +1 -1
  157. package/dist/resolution/frameworks/react.js +53 -76
  158. package/dist/resolution/frameworks/react.js.map +1 -1
  159. package/dist/resolution/frameworks/ruby.js +12 -24
  160. package/dist/resolution/frameworks/ruby.js.map +1 -1
  161. package/dist/resolution/frameworks/rust.js +26 -66
  162. package/dist/resolution/frameworks/rust.js.map +1 -1
  163. package/dist/resolution/frameworks/svelte.js +11 -31
  164. package/dist/resolution/frameworks/svelte.js.map +1 -1
  165. package/dist/resolution/frameworks/swift.js +42 -160
  166. package/dist/resolution/frameworks/swift.js.map +1 -1
  167. package/dist/resolution/index.d.ts +19 -6
  168. package/dist/resolution/index.d.ts.map +1 -1
  169. package/dist/resolution/index.js +300 -141
  170. package/dist/resolution/index.js.map +1 -1
  171. package/dist/resolution/name-matcher.d.ts +5 -0
  172. package/dist/resolution/name-matcher.d.ts.map +1 -1
  173. package/dist/resolution/name-matcher.js +148 -8
  174. package/dist/resolution/name-matcher.js.map +1 -1
  175. package/dist/resolution/types.d.ts +1 -1
  176. package/dist/resolution/types.d.ts.map +1 -1
  177. package/dist/search/query-utils.d.ts +26 -1
  178. package/dist/search/query-utils.d.ts.map +1 -1
  179. package/dist/search/query-utils.js +209 -9
  180. package/dist/search/query-utils.js.map +1 -1
  181. package/dist/sync/index.d.ts +2 -4
  182. package/dist/sync/index.d.ts.map +1 -1
  183. package/dist/sync/index.js +4 -3
  184. package/dist/sync/index.js.map +1 -1
  185. package/dist/sync/watcher.d.ts +81 -0
  186. package/dist/sync/watcher.d.ts.map +1 -0
  187. package/dist/sync/watcher.js +184 -0
  188. package/dist/sync/watcher.js.map +1 -0
  189. package/dist/types.d.ts +2 -0
  190. package/dist/types.d.ts.map +1 -1
  191. package/dist/types.js.map +1 -1
  192. package/dist/ui/shimmer-progress.d.ts +11 -0
  193. package/dist/ui/shimmer-progress.d.ts.map +1 -0
  194. package/dist/ui/shimmer-progress.js +90 -0
  195. package/dist/ui/shimmer-progress.js.map +1 -0
  196. package/dist/ui/shimmer-worker.d.ts +2 -0
  197. package/dist/ui/shimmer-worker.d.ts.map +1 -0
  198. package/dist/ui/shimmer-worker.js +112 -0
  199. package/dist/ui/shimmer-worker.js.map +1 -0
  200. package/dist/ui/types.d.ts +17 -0
  201. package/dist/ui/types.d.ts.map +1 -0
  202. package/dist/ui/types.js +3 -0
  203. package/dist/ui/types.js.map +1 -0
  204. package/dist/vectors/embedder.js +1 -1
  205. package/dist/vectors/embedder.js.map +1 -1
  206. package/package.json +7 -12
  207. 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,8 +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");
59
+ const shimmer_progress_1 = require("../ui/shimmer-progress");
65
60
  // Lazy-load heavy modules (CodeGraph, runInstaller) to keep CLI startup fast.
66
61
  async function loadCodeGraph() {
67
62
  try {
@@ -76,6 +71,18 @@ async function loadCodeGraph() {
76
71
  process.exit(1);
77
72
  }
78
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
+ }
79
86
  // Check if running with no arguments - run installer
80
87
  if (process.argv.length === 2) {
81
88
  Promise.resolve().then(() => __importStar(require('../installer'))).then(({ runInstaller }) => runInstaller()).catch((err) => {
@@ -178,32 +185,38 @@ function main() {
178
185
  const remainingSeconds = seconds % 60;
179
186
  return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
180
187
  }
188
+ // Shimmer progress renderer (runs in a worker thread for smooth animation)
189
+ // Imported at top of file from '../ui/shimmer-progress'
181
190
  /**
182
- * Create a progress bar string
183
- */
184
- function progressBar(current, total, width = 30) {
185
- const percent = total > 0 ? current / total : 0;
186
- const filled = Math.round(width * percent);
187
- const empty = width - filled;
188
- const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
189
- const percentStr = `${Math.round(percent * 100)}%`.padStart(4);
190
- return `${bar} ${percentStr}`;
191
- }
192
- /**
193
- * 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.
194
193
  */
195
- function printProgress(progress) {
196
- const phaseNames = {
197
- scanning: 'Scanning files',
198
- parsing: 'Parsing code',
199
- storing: 'Storing data',
200
- 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
+ }
201
219
  };
202
- const phaseName = phaseNames[progress.phase] || progress.phase;
203
- const bar = progressBar(progress.current, progress.total);
204
- const file = progress.currentFile ? chalk.dim(` ${progress.currentFile}`) : '';
205
- // Clear line and print progress
206
- process.stdout.write(`\r${chalk.cyan(phaseName)}: ${bar}${file}`.padEnd(100));
207
220
  }
208
221
  /**
209
222
  * Print success message
@@ -229,6 +242,102 @@ function main() {
229
242
  function warn(message) {
230
243
  console.log(chalk.yellow('⚠') + ' ' + message);
231
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
+ }
232
341
  // =============================================================================
233
342
  // Commands
234
343
  // =============================================================================
@@ -239,47 +348,47 @@ function main() {
239
348
  .command('init [path]')
240
349
  .description('Initialize CodeGraph in a project directory')
241
350
  .option('-i, --index', 'Run initial indexing after initialization')
351
+ .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
242
352
  .action(async (pathArg, options) => {
243
- const projectPath = resolveProjectPath(pathArg);
244
- 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');
245
356
  try {
246
- // Check if already initialized
247
357
  if ((0, directory_1.isInitialized)(projectPath)) {
248
- warn(`CodeGraph already initialized in ${projectPath}`);
249
- 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('');
250
361
  return;
251
362
  }
252
- // Initialize
253
363
  const { default: CodeGraph } = await loadCodeGraph();
254
- const cg = await CodeGraph.init(projectPath, {
255
- index: false, // We'll handle indexing ourselves for progress
256
- });
257
- success(`Initialized CodeGraph in ${projectPath}`);
258
- info(`Created .codegraph/ directory`);
259
- // Run initial index if requested
364
+ const cg = await CodeGraph.init(projectPath, { index: false });
365
+ clack.log.success(`Initialized in ${projectPath}`);
260
366
  if (options.index) {
261
- console.log('\nIndexing project...\n');
262
- const result = await cg.indexAll({
263
- onProgress: printProgress,
264
- });
265
- // Clear progress line
266
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
267
- if (result.success) {
268
- success(`Indexed ${formatNumber(result.filesIndexed)} files`);
269
- info(`Created ${formatNumber(result.nodesCreated)} nodes and ${formatNumber(result.edgesCreated)} edges`);
270
- 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
+ });
271
373
  }
272
374
  else {
273
- 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();
274
381
  }
382
+ printIndexResult(clack, result, projectPath);
275
383
  }
276
384
  else {
277
- info('Run "codegraph index" to index the project');
385
+ clack.log.info('Run "codegraph index" to index the project');
278
386
  }
387
+ clack.outro('Done');
279
388
  cg.destroy();
280
389
  }
281
390
  catch (err) {
282
- error(`Failed to initialize: ${err instanceof Error ? err.message : String(err)}`);
391
+ clack.log.error(`Failed: ${err instanceof Error ? err.message : String(err)}`);
283
392
  process.exit(1);
284
393
  }
285
394
  });
@@ -328,6 +437,7 @@ function main() {
328
437
  .description('Index all files in the project')
329
438
  .option('-f, --force', 'Force full re-index even if already indexed')
330
439
  .option('-q, --quiet', 'Suppress progress output')
440
+ .option('-v, --verbose', 'Show detailed worker lifecycle and memory info')
331
441
  .action(async (pathArg, options) => {
332
442
  const projectPath = resolveProjectPath(pathArg);
333
443
  try {
@@ -338,42 +448,42 @@ function main() {
338
448
  }
339
449
  const { default: CodeGraph } = await loadCodeGraph();
340
450
  const cg = await CodeGraph.open(projectPath);
341
- if (!options.quiet) {
342
- 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;
343
460
  }
344
- // Clear existing data if force
461
+ const clack = await importESM('@clack/prompts');
462
+ clack.intro('Indexing project');
345
463
  if (options.force) {
346
464
  cg.clear();
347
- if (!options.quiet) {
348
- info('Cleared existing index');
349
- }
465
+ clack.log.info('Cleared existing index');
350
466
  }
351
- const result = await cg.indexAll({
352
- onProgress: options.quiet ? undefined : printProgress,
353
- });
354
- // Clear progress line
355
- if (!options.quiet) {
356
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
357
- }
358
- if (result.success) {
359
- if (!options.quiet) {
360
- success(`Indexed ${formatNumber(result.filesIndexed)} files`);
361
- info(`Created ${formatNumber(result.nodesCreated)} nodes and ${formatNumber(result.edgesCreated)} edges`);
362
- info(`Completed in ${formatDuration(result.durationMs)}`);
363
- }
467
+ let result;
468
+ if (options.verbose) {
469
+ result = await cg.indexAll({
470
+ onProgress: createVerboseProgress(),
471
+ verbose: true,
472
+ });
364
473
  }
365
474
  else {
366
- if (!options.quiet) {
367
- warn(`Indexing completed with ${result.errors.length} errors`);
368
- for (const err of result.errors.slice(0, 5)) {
369
- console.log(chalk.dim(` - ${err.message}`));
370
- }
371
- if (result.errors.length > 5) {
372
- console.log(chalk.dim(` ... and ${result.errors.length - 5} more`));
373
- }
374
- }
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) {
375
484
  process.exit(1);
376
485
  }
486
+ clack.outro('Done');
377
487
  cg.destroy();
378
488
  }
379
489
  catch (err) {
@@ -399,32 +509,35 @@ function main() {
399
509
  }
400
510
  const { default: CodeGraph } = await loadCodeGraph();
401
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)();
402
521
  const result = await cg.sync({
403
- onProgress: options.quiet ? undefined : printProgress,
522
+ onProgress: progress.onProgress,
404
523
  });
405
- // Clear progress line
406
- if (!options.quiet) {
407
- process.stdout.write('\r' + ' '.repeat(100) + '\r');
408
- }
524
+ await progress.stop();
409
525
  const totalChanges = result.filesAdded + result.filesModified + result.filesRemoved;
410
- if (!options.quiet) {
411
- if (totalChanges === 0) {
412
- success('Already up to date');
413
- }
414
- else {
415
- success(`Synced ${formatNumber(totalChanges)} changed files`);
416
- if (result.filesAdded > 0) {
417
- info(` Added: ${result.filesAdded}`);
418
- }
419
- if (result.filesModified > 0) {
420
- info(` Modified: ${result.filesModified}`);
421
- }
422
- if (result.filesRemoved > 0) {
423
- info(` Removed: ${result.filesRemoved}`);
424
- }
425
- info(`Updated ${formatNumber(result.nodesUpdated)} nodes in ${formatDuration(result.durationMs)}`);
426
- }
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)}`);
427
539
  }
540
+ clack.outro('Done');
428
541
  cg.destroy();
429
542
  }
430
543
  catch (err) {
@@ -842,139 +955,6 @@ function main() {
842
955
  process.exit(1);
843
956
  }
844
957
  });
845
- /**
846
- * codegraph visualize [path]
847
- */
848
- program
849
- .command('visualize [path]')
850
- .description('Open interactive graph visualization in your browser')
851
- .option('-p, --port <port>', 'Port to listen on (default: auto)', parseInt)
852
- .option('--no-open', 'Do not open browser automatically')
853
- .action(async (pathArg, options) => {
854
- const projectPath = resolveProjectPath(pathArg);
855
- try {
856
- if (!(0, directory_1.isInitialized)(projectPath)) {
857
- error(`CodeGraph not initialized in ${projectPath}`);
858
- info('Run "codegraph init -i" first');
859
- process.exit(1);
860
- }
861
- const { default: CodeGraph } = await loadCodeGraph();
862
- const cg = await CodeGraph.open(projectPath);
863
- const stats = cg.getStats();
864
- console.log(chalk.bold('\n CodeGraph Explorer\n'));
865
- info(`Project: ${projectPath}`);
866
- info(`Indexed: ${formatNumber(stats.nodeCount)} nodes, ${formatNumber(stats.edgeCount)} edges, ${formatNumber(stats.fileCount)} files\n`);
867
- const { VisualizerServer } = await Promise.resolve().then(() => __importStar(require('../visualizer/server')));
868
- const server = new VisualizerServer(cg);
869
- const { url } = await server.start({ port: options.port, openBrowser: options.open !== false });
870
- success(`Visualizer running at ${chalk.cyan(url)}`);
871
- console.log(chalk.dim(' Press Ctrl+C to stop\n'));
872
- // Open browser
873
- if (options.open !== false) {
874
- const openCmd = process.platform === 'darwin' ? 'open' :
875
- process.platform === 'win32' ? 'start' : 'xdg-open';
876
- (0, child_process_1.spawn)(openCmd, [url], { detached: true, stdio: 'ignore' }).unref();
877
- }
878
- // Handle shutdown — force exit on second Ctrl+C
879
- let shuttingDown = false;
880
- const shutdown = () => {
881
- if (shuttingDown) {
882
- process.exit(1);
883
- }
884
- shuttingDown = true;
885
- console.log(chalk.dim('\n Shutting down...'));
886
- server.stop().then(() => {
887
- cg.close();
888
- process.exit(0);
889
- }).catch(() => process.exit(1));
890
- // Force exit after 2s if graceful shutdown hangs
891
- setTimeout(() => process.exit(1), 2000).unref();
892
- };
893
- process.on('SIGINT', shutdown);
894
- process.on('SIGTERM', shutdown);
895
- }
896
- catch (err) {
897
- error(`Failed to start visualizer: ${err instanceof Error ? err.message : String(err)}`);
898
- process.exit(1);
899
- }
900
- });
901
- /**
902
- * codegraph mark-dirty [path]
903
- *
904
- * Touches .codegraph/.dirty to signal that files have changed.
905
- * Used by Claude Code PostToolUse hooks to batch syncs.
906
- * Runs silently and always exits 0.
907
- */
908
- program
909
- .command('mark-dirty [path]')
910
- .description('Mark project as needing sync (used by Claude Code hooks)')
911
- .action(async (pathArg) => {
912
- try {
913
- const startPath = path.resolve(pathArg || process.cwd());
914
- const projectRoot = (0, directory_1.findNearestCodeGraphRoot)(startPath);
915
- if (!projectRoot) {
916
- // No .codegraph/ found — exit silently
917
- process.exit(0);
918
- }
919
- const dirtyPath = path.join((0, directory_1.getCodeGraphDir)(projectRoot), '.dirty');
920
- fs.writeFileSync(dirtyPath, Date.now().toString(), 'utf-8');
921
- }
922
- catch {
923
- // Never fail — this runs in the background during edits
924
- }
925
- process.exit(0);
926
- });
927
- /**
928
- * codegraph sync-if-dirty [path]
929
- *
930
- * Checks if .codegraph/.dirty exists and, if so, spawns a detached
931
- * background process to run `codegraph sync`. The hook process exits
932
- * immediately so Claude Code's Stop hook never blocks.
933
- *
934
- * Removes the marker BEFORE spawning so edits during sync
935
- * create a new marker for the next Stop event.
936
- * Runs silently and always exits 0.
937
- */
938
- program
939
- .command('sync-if-dirty [path]')
940
- .description('Sync if project was marked dirty (used by Claude Code hooks)')
941
- .action(async (pathArg) => {
942
- try {
943
- const startPath = path.resolve(pathArg || process.cwd());
944
- const projectRoot = (0, directory_1.findNearestCodeGraphRoot)(startPath);
945
- if (!projectRoot) {
946
- process.exit(0);
947
- }
948
- const dirtyPath = path.join((0, directory_1.getCodeGraphDir)(projectRoot), '.dirty');
949
- // No marker → nothing to do (sub-ms exit)
950
- if (!fs.existsSync(dirtyPath)) {
951
- process.exit(0);
952
- }
953
- // Remove marker FIRST so edits during sync create a new one
954
- try {
955
- fs.unlinkSync(dirtyPath);
956
- }
957
- catch { /* ignore */ }
958
- // If not fully initialized (no DB), exit
959
- if (!(0, directory_1.isInitialized)(projectRoot)) {
960
- process.exit(0);
961
- }
962
- // Spawn sync as a detached background process
963
- // so this hook exits immediately and doesn't block Claude Code.
964
- // Uses process.argv[0]/[1] (e.g. node /path/to/codegraph.js) so it
965
- // works whether invoked via global install, npx, or directly.
966
- const child = (0, child_process_1.spawn)(process.argv[0], [process.argv[1], 'sync', '--quiet', projectRoot], {
967
- detached: true,
968
- stdio: 'ignore',
969
- windowsHide: true,
970
- });
971
- child.unref();
972
- }
973
- catch {
974
- // Never fail — this runs at the end of Claude responses
975
- }
976
- process.exit(0);
977
- });
978
958
  /**
979
959
  * codegraph unlock [path]
980
960
  */