@codragraph/cli 2.1.1 → 2.1.4

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 (101) hide show
  1. package/README.md +12 -9
  2. package/dist/cli/ai-context.js +1 -1
  3. package/dist/cli/analyze.js +19 -2
  4. package/dist/cli/index.js +2 -1
  5. package/dist/cli/serve.d.ts +1 -0
  6. package/dist/cli/serve.js +3 -1
  7. package/dist/cli/setup.js +36 -19
  8. package/dist/cli/status.d.ts +13 -0
  9. package/dist/cli/status.js +99 -0
  10. package/dist/config/ignore-service.js +2 -0
  11. package/dist/core/graphstore/cgdb-row-source.js +3 -2
  12. package/dist/core/group/bridge-db.js +42 -10
  13. package/dist/core/run-analyze.d.ts +20 -0
  14. package/dist/core/run-analyze.js +201 -0
  15. package/dist/core/search/hybrid-search.js +11 -3
  16. package/dist/mcp/resources.js +2 -2
  17. package/dist/server/api.d.ts +14 -2
  18. package/dist/server/api.js +90 -7
  19. package/dist/server/mcp-http.d.ts +22 -0
  20. package/dist/server/mcp-http.js +21 -2
  21. package/dist/server/web-dashboard.d.ts +28 -0
  22. package/dist/server/web-dashboard.js +61 -0
  23. package/dist/web/assets/agent-D5lb0zXz.js +1089 -0
  24. package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +36 -0
  25. package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +132 -0
  26. package/dist/web/assets/c4Diagram-DFAF54RM-C4Hl3J2U.js +10 -0
  27. package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +231 -0
  28. package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +1 -0
  29. package/dist/web/assets/chunk-7RZVMHOQ-BitYcNVR.js +338 -0
  30. package/dist/web/assets/chunk-AEOMTBSW-BgTIXPsY.js +1 -0
  31. package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +13 -0
  32. package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +1 -0
  33. package/dist/web/assets/chunk-KSICW3F5-BYzvDLNI.js +15 -0
  34. package/dist/web/assets/chunk-O5ABG6QK-dHwHzA6n.js +1 -0
  35. package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +206 -0
  36. package/dist/web/assets/chunk-RWUO3TPN-BgRTY0_k.js +1 -0
  37. package/dist/web/assets/chunk-TBF5ZNIQ-DL5stGM1.js +1 -0
  38. package/dist/web/assets/chunk-TU3PZOEN-RLyvLcv-.js +1 -0
  39. package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +1 -0
  40. package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +1 -0
  41. package/dist/web/assets/context-builder-22jU3V56.js +16 -0
  42. package/dist/web/assets/cose-bilkent-PNC4W37J-DVhePRYg.js +1 -0
  43. package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +4 -0
  44. package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +43 -0
  45. package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +24 -0
  46. package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +10 -0
  47. package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +24 -0
  48. package/dist/web/assets/erDiagram-GCSMX5X6-C3dhDFA8.js +85 -0
  49. package/dist/web/assets/flowDiagram-OTCZ4VVT-CWSFWmhr.js +162 -0
  50. package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +292 -0
  51. package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +106 -0
  52. package/dist/web/assets/index-BgeqpYgd.js +1415 -0
  53. package/dist/web/assets/index-CT0GtFLZ.css +1 -0
  54. package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +2 -0
  55. package/dist/web/assets/ishikawaDiagram-YMYX4NHK-DUoJvNP2.js +70 -0
  56. package/dist/web/assets/journeyDiagram-SO5T7YLQ-RMFPNNqz.js +139 -0
  57. package/dist/web/assets/kanban-definition-LJHFXRCJ-BzpDs1K9.js +89 -0
  58. package/dist/web/assets/katex-GD7MH7QM-DBQvrix-.js +261 -0
  59. package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +96 -0
  60. package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +30 -0
  61. package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +7 -0
  62. package/dist/web/assets/requirementDiagram-M5DCFWZL-DLHOVTSv.js +84 -0
  63. package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +10 -0
  64. package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +157 -0
  65. package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +1 -0
  66. package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +1 -0
  67. package/dist/web/assets/timeline-definition-5SPVSISX-TRSDRgPw.js +120 -0
  68. package/dist/web/assets/vennDiagram-IE5QUKF5-DNy7HRBM.js +34 -0
  69. package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +161 -0
  70. package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +20 -0
  71. package/dist/web/assets/xychartDiagram-ZHJ5623Y-Dr9r7a35.js +7 -0
  72. package/dist/web/codragraph-logo-512.png +0 -0
  73. package/dist/web/codragraph-logo.png +0 -0
  74. package/dist/web/favicon.png +0 -0
  75. package/dist/web/index.html +36 -0
  76. package/hooks/claude/codragraph-hook.cjs +24 -9
  77. package/hooks/claude/pre-tool-use.sh +6 -1
  78. package/package.json +3 -1
  79. package/scripts/build.js +62 -4
  80. package/scripts/patch-tree-sitter-swift.cjs +0 -1
  81. package/skills/codragraph-cli.md +1 -1
  82. package/vendor/leiden/index.cjs +272 -285
  83. package/vendor/leiden/utils.cjs +264 -274
  84. package/dist/_shared/lbug/schema-constants.d.ts +0 -16
  85. package/dist/_shared/lbug/schema-constants.d.ts.map +0 -1
  86. package/dist/_shared/lbug/schema-constants.js +0 -67
  87. package/dist/_shared/lbug/schema-constants.js.map +0 -1
  88. package/dist/core/graphstore/lbug-row-source.d.ts +0 -19
  89. package/dist/core/graphstore/lbug-row-source.js +0 -141
  90. package/dist/core/lbug/content-read.d.ts +0 -46
  91. package/dist/core/lbug/content-read.js +0 -64
  92. package/dist/core/lbug/csv-generator.d.ts +0 -29
  93. package/dist/core/lbug/csv-generator.js +0 -492
  94. package/dist/core/lbug/lbug-adapter.d.ts +0 -176
  95. package/dist/core/lbug/lbug-adapter.js +0 -1320
  96. package/dist/core/lbug/pool-adapter.d.ts +0 -93
  97. package/dist/core/lbug/pool-adapter.js +0 -550
  98. package/dist/core/lbug/schema.d.ts +0 -62
  99. package/dist/core/lbug/schema.js +0 -502
  100. package/dist/mcp/core/lbug-adapter.d.ts +0 -5
  101. package/dist/mcp/core/lbug-adapter.js +0 -5
package/README.md CHANGED
@@ -55,16 +55,16 @@ If you prefer to configure manually instead of using `codragraph setup`:
55
55
 
56
56
  ```bash
57
57
  # macOS / Linux
58
- claude mcp add codragraph -- npx -y @codragraph/cli@2.1.1 mcp
58
+ claude mcp add codragraph -- npx -y @codragraph/cli@2.1.2 mcp
59
59
 
60
60
  # Windows
61
- claude mcp add codragraph -- cmd /c npx -y @codragraph/cli@2.1.1 mcp
61
+ claude mcp add codragraph -- cmd /c npx -y @codragraph/cli@2.1.2 mcp
62
62
  ```
63
63
 
64
64
  ### Codex (full support — MCP + skills)
65
65
 
66
66
  ```bash
67
- codex mcp add codragraph -- npx -y @codragraph/cli@2.1.1 mcp
67
+ codex mcp add codragraph -- npx -y @codragraph/cli@2.1.2 mcp
68
68
  ```
69
69
 
70
70
  ### Cursor / Windsurf
@@ -76,7 +76,7 @@ Add to `~/.cursor/mcp.json` (global — works for all projects):
76
76
  "mcpServers": {
77
77
  "codragraph": {
78
78
  "command": "npx",
79
- "args": ["-y", "@codragraph/cli@2.1.1", "mcp"]
79
+ "args": ["-y", "@codragraph/cli@2.1.2", "mcp"]
80
80
  }
81
81
  }
82
82
  }
@@ -91,7 +91,7 @@ Add to `~/.config/opencode/config.json`:
91
91
  "mcp": {
92
92
  "codragraph": {
93
93
  "command": "npx",
94
- "args": ["-y", "@codragraph/cli@2.1.1", "mcp"]
94
+ "args": ["-y", "@codragraph/cli@2.1.2", "mcp"]
95
95
  }
96
96
  }
97
97
  }
@@ -172,7 +172,8 @@ codragraph feature-context Settings # Focus files, line ranges, flows, dependenc
172
172
  codragraph context-pack Settings # Compact agent context pack for one feature
173
173
  codragraph cluster-impact Settings --direction both # Feature-level blast radius
174
174
  codragraph mcp # Start MCP server (stdio) — serves all indexed repos
175
- codragraph serve # Start local HTTP server (multi-repo) for web UI
175
+ codragraph serve # Start local HTTP API + bundled web UI
176
+ codragraph serve --web hosted # API only; connect from hosted web UI
176
177
  codragraph index # Register an existing .codragraph/ folder into the global registry
177
178
  codragraph list # List all indexed repositories
178
179
  codragraph status # Show index status for current repo
@@ -296,9 +297,9 @@ It is fixed in **codragraph v1.6.2+**. Upgrade to the current workspace
296
297
  version, or pin the version your team has validated:
297
298
 
298
299
  ```bash
299
- npx @codragraph/cli@2.1.1 analyze # no global install
300
+ npx @codragraph/cli@2.1.2 analyze # no global install
300
301
  # or
301
- npm install -g @codragraph/cli@2.1.1 # upgrade a global install
302
+ npm install -g @codragraph/cli@2.1.2 # upgrade a global install
302
303
  ```
303
304
 
304
305
  If you still hit npm install issues after upgrading, these generic workarounds
@@ -402,7 +403,9 @@ Values above **32768 KB (32 MB)** are clamped to the tree-sitter parser ceiling;
402
403
 
403
404
  CodraGraph also has a browser-based UI at [codragraph.vercel.app](https://codragraph.vercel.app) — 100% client-side, your code never leaves the browser.
404
405
 
405
- **Local Backend Mode:** Run `codragraph serve` and open the web UI locally it auto-detects the server and shows all your indexed repos, feature clusters, dependency context, and full AI chat support. No need to re-upload or re-index. The agent's tools (Cypher queries, search, code navigation) route through the backend HTTP API automatically.
406
+ **Local Backend Mode:** Run `codragraph serve` and open `http://localhost:4747`. The installed CLI serves the bundled dashboard from `dist/web` and does not package `apps/web/node_modules`. It auto-detects the server and shows all your indexed repos, feature clusters, dependency context, and full AI chat support. No need to re-upload or re-index. The agent's tools (Cypher queries, search, code navigation) route through the backend HTTP API automatically.
407
+
408
+ **Hosted Dashboard Mode:** Run `codragraph serve --web hosted`, then open the hosted dashboard and connect it to `http://localhost:4747`. The UI is hosted, but project data still comes from your local API.
406
409
 
407
410
  ## License
408
411
 
@@ -122,7 +122,7 @@ This repository is listed under CodraGraph **group(s): ${groupNames.join(', ')}*
122
122
  `
123
123
  : ''}## CLI
124
124
 
125
- Commands are cross-platform: use \`npx @codragraph/cli ...\` or \`codragraph ...\` in Windows PowerShell, macOS bash/zsh, and Linux shells. Prefer package commands like \`npm --prefix packages/core test\` from repo root instead of shell-specific \`cd dir && ...\` chains.
125
+ Commands are cross-platform: \`codragraph ...\`, \`npx @codragraph/cli ...\`, or \`bunx @codragraph/cli ...\`. Scripts: \`npm --prefix ...\` or \`bun run --filter ...\`.
126
126
 
127
127
  ${skillsTable}
128
128
 
@@ -19,6 +19,7 @@ import { getGitRoot, hasGitDir } from '../storage/git.js';
19
19
  import { runFullAnalysis } from '../core/run-analyze.js';
20
20
  import { getMaxFileSizeBannerMessage } from '../core/ingestion/utils/max-file-size.js';
21
21
  import fs from 'fs/promises';
22
+ import { formatBytes, LARGE_INDEX_WARNING_BYTES, summarizeIndexStorage } from './status.js';
22
23
  const require = createRequire(import.meta.url);
23
24
  const pkg = require('../../package.json');
24
25
  const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
@@ -255,7 +256,7 @@ export const analyzeCommand = async (inputPath, options) => {
255
256
  console.warn = origWarn;
256
257
  console.error = origError;
257
258
  bar.stop();
258
- console.log(' Already up to date\n');
259
+ console.log(` ${result.reuseReason ?? 'Already up to date'}\n`);
259
260
  // Safe to return without process.exit(0) — the early-return path in
260
261
  // runFullAnalysis never opens LadybugDB, so no native handles prevent exit.
261
262
  return;
@@ -324,6 +325,20 @@ export const analyzeCommand = async (inputPath, options) => {
324
325
  console.log(`\n Repository indexed successfully (${totalTime}s)\n`);
325
326
  console.log(` ${(s.nodes ?? 0).toLocaleString()} nodes | ${(s.edges ?? 0).toLocaleString()} edges | ${s.communities ?? 0} clusters | ${s.processes ?? 0} flows`);
326
327
  console.log(` ${repoPath}`);
328
+ try {
329
+ const { storagePath } = getStoragePaths(repoPath);
330
+ const storageSummary = await summarizeIndexStorage(storagePath);
331
+ if (storageSummary) {
332
+ console.log(` .codragraph size: ${formatBytes(storageSummary.bytes)}`);
333
+ if (storageSummary.bytes >= LARGE_INDEX_WARNING_BYTES) {
334
+ console.log(` Storage warning: index is >= ${formatBytes(LARGE_INDEX_WARNING_BYTES)}. ` +
335
+ `Use --compress brotli (or zstd on Node >=22.15) and reserve --embeddings for repos that need vector search.`);
336
+ }
337
+ }
338
+ }
339
+ catch {
340
+ /* size summary is best-effort */
341
+ }
327
342
  // Surface @codragraph/compress's value prop with concrete numbers: how
328
343
  // many tokens of distilled context did we generate. Best-effort — never
329
344
  // fail the analyze for a stat read.
@@ -391,12 +406,13 @@ export const analyzeCommand = async (inputPath, options) => {
391
406
  // 'node.target'" crash happens inside npm *before* codragraph code runs,
392
407
  // so it can't be caught here. This branch handles dependency-resolution
393
408
  // errors that surface at runtime (e.g. dynamic require failures).
394
- console.error(' This looks like an npm dependency resolution issue.');
409
+ console.error(' This looks like a package-manager dependency resolution issue.');
395
410
  console.error(' Suggestions:');
396
411
  console.error(' 1. Clear the npm cache: npm cache clean --force');
397
412
  console.error(' 2. Update npm: npm install -g npm@latest');
398
413
  console.error(` 3. Reinstall codragraph: npm install -g ${CLI_PACKAGE_SPEC}`);
399
414
  console.error(` 4. Or try npx directly: npx ${CLI_PACKAGE_SPEC} analyze`);
415
+ console.error(` 5. Bun alternative: bunx ${CLI_PACKAGE_SPEC} analyze`);
400
416
  console.error('');
401
417
  }
402
418
  else if (msg.includes('MODULE_NOT_FOUND') ||
@@ -406,6 +422,7 @@ export const analyzeCommand = async (inputPath, options) => {
406
422
  console.error(' Suggestions:');
407
423
  console.error(` 1. Reinstall: npm install -g ${CLI_PACKAGE_SPEC}`);
408
424
  console.error(` 2. Clear cache: npm cache clean --force && npx ${CLI_PACKAGE_SPEC} analyze`);
425
+ console.error(` 3. Bun cache: bun pm cache rm && bunx ${CLI_PACKAGE_SPEC} analyze`);
409
426
  console.error('');
410
427
  }
411
428
  process.exitCode = 1;
package/dist/cli/index.js CHANGED
@@ -55,9 +55,10 @@ program
55
55
  .action(createLazyAction(() => import('./index-repo.js'), 'indexCommand'));
56
56
  program
57
57
  .command('serve')
58
- .description('Start local HTTP server for web UI connection')
58
+ .description('Start local HTTP API and web dashboard')
59
59
  .option('-p, --port <port>', 'Port number', '4747')
60
60
  .option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
61
+ .option('--web <mode>', 'Dashboard mode: local serves the bundled app, hosted prints hosted UI connection info, off serves API only', 'local')
61
62
  .action(createLazyAction(() => import('./serve.js'), 'serveCommand'));
62
63
  program
63
64
  .command('mcp')
@@ -1,4 +1,5 @@
1
1
  export declare const serveCommand: (options?: {
2
2
  port?: string;
3
3
  host?: string;
4
+ web?: string;
4
5
  }) => Promise<void>;
package/dist/cli/serve.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createServer } from '../server/api.js';
2
+ import { normalizeWebDashboardMode } from '../server/web-dashboard.js';
2
3
  // Catch anything that would cause a silent exit
3
4
  process.on('uncaughtException', (err) => {
4
5
  console.error('\n[codragraph serve] Uncaught exception:', err.message);
@@ -19,7 +20,8 @@ export const serveCommand = async (options) => {
19
20
  // hosted frontend at codragraph.vercel.app connects to localhost.
20
21
  const host = options?.host ?? 'localhost';
21
22
  try {
22
- await createServer(port, host);
23
+ const web = normalizeWebDashboardMode(options?.web);
24
+ await createServer(port, host, { web });
23
25
  }
24
26
  catch (err) {
25
27
  console.error(`\nFailed to start CodraGraph server:\n`);
package/dist/cli/setup.js CHANGED
@@ -23,7 +23,7 @@ const pkg = require('../../package.json');
23
23
  const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
24
24
  /**
25
25
  * Resolve the absolute path to the `codragraph` binary if it's installed
26
- * globally (or via npm -g / yarn global). Returns null when not found.
26
+ * globally (or via npm -g / bun -g / yarn global). Returns null when not found.
27
27
  *
28
28
  * Note: the npm package is `@codragraph/cli`, but the executable it installs
29
29
  * is `codragraph` (see package.json `bin`). PATH lookup must use the bin name.
@@ -49,7 +49,7 @@ function resolveCodragraphBin() {
49
49
  .filter(Boolean);
50
50
  if (process.platform === 'win32') {
51
51
  // Prefer a Windows-executable shim. If none is on PATH, return null so
52
- // the caller falls through to the `cmd /c npx -y @codragraph/cli` path.
52
+ // the caller falls through to the package-runner fallback.
53
53
  const exe = lines.find((l) => /\.(cmd|exe|bat)$/i.test(l));
54
54
  return exe ?? null;
55
55
  }
@@ -63,28 +63,40 @@ function resolveCodragraphBin() {
63
63
  * The MCP server entry for all editors.
64
64
  *
65
65
  * Prefers the globally-installed `codragraph` binary (starts in ~1 s) over
66
- * `npx -y @codragraph/cli@<version>` (cold-cache install of native deps can take
66
+ * `npx` / `bunx` package execution (cold-cache install of native deps can take
67
67
  * >60 s, exceeding Claude Code's 30 s MCP connection timeout).
68
68
  *
69
- * Falls back to npx when the binary isn't on PATH — e.g. first-time
70
- * users who ran `npx @codragraph/cli analyze` but haven't done `npm i -g`.
69
+ * Falls back to the package runner that invoked setup when the binary isn't on
70
+ * PATH e.g. first-time `npx @codragraph/cli` or `bunx @codragraph/cli` users.
71
71
  *
72
72
  * Windows note: even when the bin is on PATH, we launch via `cmd /c codragraph
73
73
  * mcp` rather than writing the resolved path. Reason: `where codragraph`
74
74
  * returns the extensionless Unix shim before `codragraph.cmd`, and Node's
75
75
  * spawn/execFile cannot launch the extensionless shim on Windows. Letting
76
76
  * cmd resolve via PATHEXT is the only reliable path that works for npm-,
77
- * pnpm-, and yarn-installed shims alike.
77
+ * bun-, pnpm-, and yarn-installed shims alike.
78
78
  */
79
- function getMcpEntry() {
80
- const bin = resolveCodragraphBin();
81
- if (bin) {
79
+ function isRunningUnderBun() {
80
+ const userAgent = (process.env.npm_config_user_agent ?? '').toLowerCase();
81
+ const rawExecPath = process.env.npm_execpath ?? '';
82
+ const execPath = process.platform === 'win32'
83
+ ? path.win32.basename(rawExecPath).toLowerCase()
84
+ : path.basename(rawExecPath).toLowerCase();
85
+ return userAgent.startsWith('bun/') || execPath === 'bun' || execPath === 'bun.exe';
86
+ }
87
+ function getPackageRunnerMcpEntry() {
88
+ if (isRunningUnderBun()) {
82
89
  if (process.platform === 'win32') {
83
- return { command: 'cmd', args: ['/c', 'codragraph', 'mcp'] };
90
+ return {
91
+ command: 'cmd',
92
+ args: ['/c', 'bunx', CLI_PACKAGE_SPEC, 'mcp'],
93
+ };
84
94
  }
85
- return { command: bin, args: ['mcp'] };
95
+ return {
96
+ command: 'bunx',
97
+ args: [CLI_PACKAGE_SPEC, 'mcp'],
98
+ };
86
99
  }
87
- // Fallback: npx (works without a global install, but slow cold-start)
88
100
  if (process.platform === 'win32') {
89
101
  return {
90
102
  command: 'cmd',
@@ -96,6 +108,16 @@ function getMcpEntry() {
96
108
  args: ['-y', CLI_PACKAGE_SPEC, 'mcp'],
97
109
  };
98
110
  }
111
+ function getMcpEntry() {
112
+ const bin = resolveCodragraphBin();
113
+ if (bin) {
114
+ if (process.platform === 'win32') {
115
+ return { command: 'cmd', args: ['/c', 'codragraph', 'mcp'] };
116
+ }
117
+ return { command: bin, args: ['mcp'] };
118
+ }
119
+ return getPackageRunnerMcpEntry();
120
+ }
99
121
  /**
100
122
  * OpenCode uses a different MCP format: { type: "local", command: [...] }
101
123
  * where command is a flat array (command + args combined).
@@ -108,13 +130,8 @@ function getOpenCodeMcpEntry() {
108
130
  }
109
131
  return { type: 'local', command: [bin, 'mcp'] };
110
132
  }
111
- if (process.platform === 'win32') {
112
- return {
113
- type: 'local',
114
- command: ['cmd', '/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
115
- };
116
- }
117
- return { type: 'local', command: ['npx', '-y', CLI_PACKAGE_SPEC, 'mcp'] };
133
+ const entry = getPackageRunnerMcpEntry();
134
+ return { type: 'local', command: [entry.command, ...entry.args] };
118
135
  }
119
136
  /**
120
137
  * Merge codragraph entry into an existing MCP config JSON object.
@@ -3,4 +3,17 @@
3
3
  *
4
4
  * Shows the indexing status of the current repository.
5
5
  */
6
+ import { type RepoMeta } from '../storage/repo-manager.js';
7
+ export declare const LARGE_INDEX_WARNING_BYTES: number;
8
+ export interface IndexStorageSummary {
9
+ path: string;
10
+ bytes: number;
11
+ fileCount: number;
12
+ unreadableEntries: number;
13
+ }
14
+ export declare const formatBytes: (bytes: number) => string;
15
+ export declare const summarizeIndexStorage: (storagePath: string) => Promise<IndexStorageSummary | null>;
16
+ export declare const formatIndexStatusLines: (summary: IndexStorageSummary | null, meta: Pick<RepoMeta, "stats" | "compress">, options?: {
17
+ largeIndexWarningBytes?: number;
18
+ }) => string[];
6
19
  export declare const statusCommand: () => Promise<void>;
@@ -3,8 +3,99 @@
3
3
  *
4
4
  * Shows the indexing status of the current repository.
5
5
  */
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
6
8
  import { findRepo, getStoragePaths, hasKuzuIndex } from '../storage/repo-manager.js';
7
9
  import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
10
+ export const LARGE_INDEX_WARNING_BYTES = 500 * 1024 * 1024;
11
+ const STORAGE_SCAN_BATCH_SIZE = 64;
12
+ export const formatBytes = (bytes) => {
13
+ if (!Number.isFinite(bytes) || bytes <= 0)
14
+ return '0 B';
15
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
16
+ let value = bytes;
17
+ let unitIndex = 0;
18
+ while (value >= 1024 && unitIndex < units.length - 1) {
19
+ value /= 1024;
20
+ unitIndex += 1;
21
+ }
22
+ if (unitIndex === 0)
23
+ return `${Math.round(value)} ${units[unitIndex]}`;
24
+ return `${value.toFixed(1)} ${units[unitIndex]}`;
25
+ };
26
+ export const summarizeIndexStorage = async (storagePath) => {
27
+ let rootStat;
28
+ try {
29
+ rootStat = await fs.lstat(storagePath);
30
+ }
31
+ catch (error) {
32
+ if (error.code === 'ENOENT')
33
+ return null;
34
+ return { path: storagePath, bytes: 0, fileCount: 0, unreadableEntries: 1 };
35
+ }
36
+ if (!rootStat.isDirectory()) {
37
+ return { path: storagePath, bytes: rootStat.size, fileCount: 1, unreadableEntries: 0 };
38
+ }
39
+ const stack = [storagePath];
40
+ let bytes = 0;
41
+ let fileCount = 0;
42
+ let unreadableEntries = 0;
43
+ while (stack.length > 0) {
44
+ const current = stack.pop();
45
+ let entries;
46
+ try {
47
+ entries = await fs.readdir(current, { withFileTypes: true });
48
+ }
49
+ catch {
50
+ unreadableEntries += 1;
51
+ continue;
52
+ }
53
+ for (let i = 0; i < entries.length; i += STORAGE_SCAN_BATCH_SIZE) {
54
+ const batch = entries.slice(i, i + STORAGE_SCAN_BATCH_SIZE);
55
+ const stats = await Promise.all(batch.map(async (entry) => {
56
+ const entryPath = path.join(current, entry.name);
57
+ try {
58
+ return { entryPath, stat: await fs.lstat(entryPath) };
59
+ }
60
+ catch {
61
+ return { entryPath, stat: null };
62
+ }
63
+ }));
64
+ for (const { entryPath, stat } of stats) {
65
+ if (!stat) {
66
+ unreadableEntries += 1;
67
+ continue;
68
+ }
69
+ if (stat.isDirectory()) {
70
+ stack.push(entryPath);
71
+ continue;
72
+ }
73
+ bytes += stat.size;
74
+ fileCount += 1;
75
+ }
76
+ }
77
+ }
78
+ return { path: storagePath, bytes, fileCount, unreadableEntries };
79
+ };
80
+ export const formatIndexStatusLines = (summary, meta, options = {}) => {
81
+ const lines = [];
82
+ const threshold = options.largeIndexWarningBytes ?? LARGE_INDEX_WARNING_BYTES;
83
+ if (summary) {
84
+ const fileLabel = summary.fileCount === 1 ? 'file' : 'files';
85
+ lines.push(`Index size: ${formatBytes(summary.bytes)} (${summary.fileCount.toLocaleString('en-US')} ${fileLabel} under .codragraph)`);
86
+ if (summary.unreadableEntries > 0) {
87
+ const entryLabel = summary.unreadableEntries === 1 ? 'entry' : 'entries';
88
+ lines.push(`Index size note: ${summary.unreadableEntries.toLocaleString('en-US')} ${entryLabel} could not be read.`);
89
+ }
90
+ if (summary.bytes >= threshold) {
91
+ lines.push(`Storage warning: .codragraph is ${formatBytes(summary.bytes)} (>= ${formatBytes(threshold)}). Consider codragraph analyze --compress brotli (or zstd on Node >=22.15) and only use --embeddings when vectors are needed.`);
92
+ }
93
+ }
94
+ const embeddings = meta.stats?.embeddings;
95
+ lines.push(`Embeddings: ${typeof embeddings === 'number' ? embeddings.toLocaleString('en-US') : 'unknown'}`);
96
+ lines.push(`Compression: ${meta.compress ?? 'none'}`);
97
+ return lines;
98
+ };
8
99
  export const statusCommand = async () => {
9
100
  const cwd = process.cwd();
10
101
  if (!isGitRepo(cwd)) {
@@ -16,6 +107,7 @@ export const statusCommand = async () => {
16
107
  // Check if there's a stale KuzuDB index that needs migration
17
108
  const repoRoot = getGitRoot(cwd) ?? cwd;
18
109
  const { storagePath } = getStoragePaths(repoRoot);
110
+ const storageSummary = await summarizeIndexStorage(storagePath);
19
111
  if (await hasKuzuIndex(storagePath)) {
20
112
  console.log('Repository has a stale KuzuDB index from a previous version.');
21
113
  console.log('Run: codragraph analyze (rebuilds the index with LadybugDB)');
@@ -24,13 +116,20 @@ export const statusCommand = async () => {
24
116
  console.log('Repository not indexed.');
25
117
  console.log('Run: codragraph analyze');
26
118
  }
119
+ if (storageSummary) {
120
+ for (const line of formatIndexStatusLines(storageSummary, {}))
121
+ console.log(line);
122
+ }
27
123
  return;
28
124
  }
29
125
  const currentCommit = getCurrentCommit(repo.repoPath);
30
126
  const isUpToDate = currentCommit === repo.meta.lastCommit;
127
+ const storageSummary = await summarizeIndexStorage(repo.storagePath);
31
128
  console.log(`Repository: ${repo.repoPath}`);
32
129
  console.log(`Indexed: ${new Date(repo.meta.indexedAt).toLocaleString()}`);
33
130
  console.log(`Indexed commit: ${repo.meta.lastCommit?.slice(0, 7)}`);
34
131
  console.log(`Current commit: ${currentCommit?.slice(0, 7)}`);
132
+ for (const line of formatIndexStatusLines(storageSummary, repo.meta))
133
+ console.log(line);
35
134
  console.log(`Status: ${isUpToDate ? '✅ up-to-date' : '⚠️ stale (re-run codragraph analyze)'}`);
36
135
  };
@@ -212,6 +212,8 @@ const IGNORED_EXTENSIONS = new Set([
212
212
  // Files to ignore by exact name
213
213
  const IGNORED_FILES = new Set([
214
214
  'package-lock.json',
215
+ 'bun.lock',
216
+ 'bun.lockb',
215
217
  'yarn.lock',
216
218
  'pnpm-lock.yaml',
217
219
  'composer.lock',
@@ -26,7 +26,7 @@ export const createCgdbRowSource = (opts = {}) => {
26
26
  // `core/search/bm25-index.ts` for FTS results. Tables that do not
27
27
  // exist on disk for a given repo throw here — we treat that as
28
28
  // "no rows" via the onSkip callback rather than a hard failure.
29
- rows = await executeQuery(`MATCH (n:${tableName}) RETURN n`);
29
+ rows = await executeQuery(`MATCH (n:${quoteCypherIdentifier(tableName)}) RETURN n`);
30
30
  }
31
31
  catch (err) {
32
32
  onSkip(tableName, err);
@@ -55,7 +55,7 @@ export const createCgdbRowSource = (opts = {}) => {
55
55
  // `rel`. Scalars give us a deterministic edge id even if the rel
56
56
  // payload's shape changes; `rel` carries any extra properties for
57
57
  // hashing.
58
- rows = await executeQuery(`MATCH (a)-[r:${REL_TABLE_NAME}]->(b) RETURN a.id AS \`from\`, b.id AS \`to\`, r.type AS type, r AS rel`);
58
+ rows = await executeQuery(`MATCH (a)-[r:${quoteCypherIdentifier(REL_TABLE_NAME)}]->(b) RETURN a.id AS \`from\`, b.id AS \`to\`, r.type AS type, r AS rel`);
59
59
  }
60
60
  catch (err) {
61
61
  onSkip(REL_TABLE_NAME, err);
@@ -100,6 +100,7 @@ const pickField = (row, named, positional) => {
100
100
  return row[named] ?? row[positional];
101
101
  };
102
102
  const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
103
+ const quoteCypherIdentifier = (identifier) => `\`${identifier.replace(/`/g, '``')}\``;
103
104
  /**
104
105
  * Sanitize a node row for canonical hashing:
105
106
  * - Drop LadybugDB-specific internal fields (`_id`, `_label`) that are
@@ -74,7 +74,7 @@ export function findContractNode(index, repo, role, symbolUid, filePath, symbolN
74
74
  export async function openBridgeDb(dbPath) {
75
75
  const parentDir = path.dirname(dbPath);
76
76
  await fsp.mkdir(parentDir, { recursive: true });
77
- const db = new cgdb.Database(dbPath, 0, false, false); // writable
77
+ const db = new cgdb.Database(dbPath, 0, false, false, BRIDGE_MAX_DB_SIZE_BYTES); // writable
78
78
  const conn = new cgdb.Connection(db);
79
79
  return { _db: db, _conn: conn, groupDir: parentDir };
80
80
  }
@@ -238,6 +238,25 @@ export async function retryRename(src, dst, attempts = 3) {
238
238
  }
239
239
  }
240
240
  }
241
+ async function removeBridgeArtifacts(basePath) {
242
+ for (const candidate of [basePath, `${basePath}.wal`, `${basePath}.lock`]) {
243
+ try {
244
+ await fsp.rm(candidate, { recursive: true, force: true });
245
+ }
246
+ catch {
247
+ /* ignore */
248
+ }
249
+ }
250
+ }
251
+ async function renameBridgeArtifactIfExists(src, dst) {
252
+ try {
253
+ await fsp.access(src);
254
+ }
255
+ catch {
256
+ return;
257
+ }
258
+ await retryRename(src, dst);
259
+ }
241
260
  /* ------------------------------------------------------------------ */
242
261
  /* writeBridgeMeta / readBridgeMeta */
243
262
  /* ------------------------------------------------------------------ */
@@ -261,6 +280,10 @@ export async function readBridgeMeta(groupDir) {
261
280
  }
262
281
  }
263
282
  const MAX_SAMPLE_ERRORS = 10;
283
+ // LadybugDB defaults maxDBSize to an 8 TiB mmap window on some platforms.
284
+ // Bridge databases are small contract registries, so cap the mapping to keep
285
+ // CI and low-resource user machines from failing before the first query.
286
+ const BRIDGE_MAX_DB_SIZE_BYTES = 512 * 1024 * 1024;
264
287
  function errMessage(err) {
265
288
  if (err instanceof Error)
266
289
  return err.message;
@@ -293,13 +316,9 @@ export async function writeBridge(groupDir, input) {
293
316
  report.sampleErrors.push({ kind, id, message: errMessage(err) });
294
317
  }
295
318
  };
296
- // Clean up any leftover tmp
297
- try {
298
- await fsp.rm(tmpPath, { recursive: true, force: true });
299
- }
300
- catch {
301
- /* ignore */
302
- }
319
+ // Clean up any leftover tmp plus native sidecars. LadybugDB stores WAL/lock
320
+ // files next to the database path, not inside it.
321
+ await removeBridgeArtifacts(tmpPath);
303
322
  // 1. Create temp DB, insert all data.
304
323
  //
305
324
  // Everything after `openBridgeDb` must run inside a try/finally so that
@@ -440,16 +459,27 @@ export async function writeBridge(groupDir, input) {
440
459
  }
441
460
  }
442
461
  // 3. Atomic swap: old→.bak, tmp→final, rm .bak
462
+ // Keep LadybugDB sidecars paired with the path rename. WAL files live next
463
+ // to the DB path, so renaming only the base path can hide fresh schema/data.
464
+ await removeBridgeArtifacts(bakPath);
443
465
  try {
444
466
  await fsp.access(finalPath);
445
467
  await retryRename(finalPath, bakPath);
468
+ await renameBridgeArtifactIfExists(`${finalPath}.wal`, `${bakPath}.wal`);
446
469
  }
447
470
  catch {
448
471
  /* no existing db */
449
472
  }
450
473
  await retryRename(tmpPath, finalPath);
474
+ await renameBridgeArtifactIfExists(`${tmpPath}.wal`, `${finalPath}.wal`);
475
+ try {
476
+ await fsp.rm(`${tmpPath}.lock`, { force: true });
477
+ }
478
+ catch {
479
+ /* ignore */
480
+ }
451
481
  try {
452
- await fsp.rm(bakPath, { recursive: true, force: true });
482
+ await removeBridgeArtifacts(bakPath);
453
483
  }
454
484
  catch {
455
485
  /* ignore */
@@ -480,6 +510,8 @@ export async function openBridgeDbReadOnly(groupDir) {
480
510
  try {
481
511
  await fsp.access(bakPath);
482
512
  await retryRename(bakPath, dbPath);
513
+ await renameBridgeArtifactIfExists(`${bakPath}.wal`, `${dbPath}.wal`);
514
+ await fsp.rm(`${bakPath}.lock`, { force: true });
483
515
  }
484
516
  catch {
485
517
  return null;
@@ -506,7 +538,7 @@ export async function openBridgeDbReadOnly(groupDir) {
506
538
  let db;
507
539
  let conn;
508
540
  try {
509
- db = new cgdb.Database(dbPath, 0, false, true); // readOnly
541
+ db = new cgdb.Database(dbPath, 0, false, true, BRIDGE_MAX_DB_SIZE_BYTES); // readOnly
510
542
  conn = new cgdb.Connection(db);
511
543
  return { _db: db, _conn: conn, groupDir };
512
544
  }
@@ -8,6 +8,7 @@
8
8
  * IMPORTANT: This module must NEVER call process.exit(). The caller (CLI
9
9
  * wrapper or server worker) is responsible for process lifecycle.
10
10
  */
11
+ import { type RepoMeta } from '../storage/repo-manager.js';
11
12
  import type { ContentEncoding } from '@codragraph/graphstore';
12
13
  export interface AnalyzeCallbacks {
13
14
  onProgress: (phase: string, percent: number, message: string) => void;
@@ -68,10 +69,29 @@ export interface AnalyzeResult {
68
69
  embeddings?: number;
69
70
  };
70
71
  alreadyUpToDate?: boolean;
72
+ /** User-facing explanation for a reused index fast path. */
73
+ reuseReason?: string;
74
+ /** True when the git commit advanced but indexed inputs did not. */
75
+ reusedExistingIndex?: boolean;
71
76
  /** The raw pipeline result — only populated when needed by callers (e.g. skill generation). */
72
77
  pipelineResult?: any;
73
78
  }
79
+ export interface AnalyzeChangedPath {
80
+ /** Git name-status token, e.g. M, A, D, R100. */
81
+ status: string;
82
+ /** Current path for additions/modifications, or deleted path for deletions. */
83
+ path: string;
84
+ /** Previous path for renames/copies. */
85
+ previousPath?: string;
86
+ }
74
87
  export declare const PHASE_LABELS: Record<string, string>;
88
+ export declare const parseGitNameStatus: (raw: string) => AnalyzeChangedPath[];
89
+ export declare const listChangedPathsBetweenCommits: (repoPath: string, fromRef: string, toRef: string) => AnalyzeChangedPath[] | null;
90
+ export declare const isGeneratedAgentContextPath: (filePath: string) => boolean;
91
+ export declare const isGraphContentPath: (filePath: string) => boolean;
92
+ export declare const changedPathAffectsGraph: (change: AnalyzeChangedPath) => boolean;
93
+ export declare const getGraphRelevantChangedPaths: (changes: readonly AnalyzeChangedPath[]) => AnalyzeChangedPath[];
94
+ export declare const getAnalyzeConfigRebuildReason: (existingMeta: Pick<RepoMeta, "compress" | "stats">, options: Pick<AnalyzeOptions, "compress" | "embeddings">) => string | null;
75
95
  /**
76
96
  * Run the full CodraGraph analysis pipeline.
77
97
  *