@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.
- package/README.md +12 -9
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.js +19 -2
- package/dist/cli/index.js +2 -1
- package/dist/cli/serve.d.ts +1 -0
- package/dist/cli/serve.js +3 -1
- package/dist/cli/setup.js +36 -19
- package/dist/cli/status.d.ts +13 -0
- package/dist/cli/status.js +99 -0
- package/dist/config/ignore-service.js +2 -0
- package/dist/core/graphstore/cgdb-row-source.js +3 -2
- package/dist/core/group/bridge-db.js +42 -10
- package/dist/core/run-analyze.d.ts +20 -0
- package/dist/core/run-analyze.js +201 -0
- package/dist/core/search/hybrid-search.js +11 -3
- package/dist/mcp/resources.js +2 -2
- package/dist/server/api.d.ts +14 -2
- package/dist/server/api.js +90 -7
- package/dist/server/mcp-http.d.ts +22 -0
- package/dist/server/mcp-http.js +21 -2
- package/dist/server/web-dashboard.d.ts +28 -0
- package/dist/server/web-dashboard.js +61 -0
- package/dist/web/assets/agent-D5lb0zXz.js +1089 -0
- package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +36 -0
- package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +132 -0
- package/dist/web/assets/c4Diagram-DFAF54RM-C4Hl3J2U.js +10 -0
- package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +231 -0
- package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +1 -0
- package/dist/web/assets/chunk-7RZVMHOQ-BitYcNVR.js +338 -0
- package/dist/web/assets/chunk-AEOMTBSW-BgTIXPsY.js +1 -0
- package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +13 -0
- package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +1 -0
- package/dist/web/assets/chunk-KSICW3F5-BYzvDLNI.js +15 -0
- package/dist/web/assets/chunk-O5ABG6QK-dHwHzA6n.js +1 -0
- package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +206 -0
- package/dist/web/assets/chunk-RWUO3TPN-BgRTY0_k.js +1 -0
- package/dist/web/assets/chunk-TBF5ZNIQ-DL5stGM1.js +1 -0
- package/dist/web/assets/chunk-TU3PZOEN-RLyvLcv-.js +1 -0
- package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +1 -0
- package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +1 -0
- package/dist/web/assets/context-builder-22jU3V56.js +16 -0
- package/dist/web/assets/cose-bilkent-PNC4W37J-DVhePRYg.js +1 -0
- package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +4 -0
- package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +43 -0
- package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +24 -0
- package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +10 -0
- package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +24 -0
- package/dist/web/assets/erDiagram-GCSMX5X6-C3dhDFA8.js +85 -0
- package/dist/web/assets/flowDiagram-OTCZ4VVT-CWSFWmhr.js +162 -0
- package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +292 -0
- package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +106 -0
- package/dist/web/assets/index-BgeqpYgd.js +1415 -0
- package/dist/web/assets/index-CT0GtFLZ.css +1 -0
- package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +2 -0
- package/dist/web/assets/ishikawaDiagram-YMYX4NHK-DUoJvNP2.js +70 -0
- package/dist/web/assets/journeyDiagram-SO5T7YLQ-RMFPNNqz.js +139 -0
- package/dist/web/assets/kanban-definition-LJHFXRCJ-BzpDs1K9.js +89 -0
- package/dist/web/assets/katex-GD7MH7QM-DBQvrix-.js +261 -0
- package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +96 -0
- package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +30 -0
- package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +7 -0
- package/dist/web/assets/requirementDiagram-M5DCFWZL-DLHOVTSv.js +84 -0
- package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +10 -0
- package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +157 -0
- package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +1 -0
- package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +1 -0
- package/dist/web/assets/timeline-definition-5SPVSISX-TRSDRgPw.js +120 -0
- package/dist/web/assets/vennDiagram-IE5QUKF5-DNy7HRBM.js +34 -0
- package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +161 -0
- package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +20 -0
- package/dist/web/assets/xychartDiagram-ZHJ5623Y-Dr9r7a35.js +7 -0
- package/dist/web/codragraph-logo-512.png +0 -0
- package/dist/web/codragraph-logo.png +0 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/index.html +36 -0
- package/hooks/claude/codragraph-hook.cjs +24 -9
- package/hooks/claude/pre-tool-use.sh +6 -1
- package/package.json +3 -1
- package/scripts/build.js +62 -4
- package/scripts/patch-tree-sitter-swift.cjs +0 -1
- package/skills/codragraph-cli.md +1 -1
- package/vendor/leiden/index.cjs +272 -285
- package/vendor/leiden/utils.cjs +264 -274
- package/dist/_shared/lbug/schema-constants.d.ts +0 -16
- package/dist/_shared/lbug/schema-constants.d.ts.map +0 -1
- package/dist/_shared/lbug/schema-constants.js +0 -67
- package/dist/_shared/lbug/schema-constants.js.map +0 -1
- package/dist/core/graphstore/lbug-row-source.d.ts +0 -19
- package/dist/core/graphstore/lbug-row-source.js +0 -141
- package/dist/core/lbug/content-read.d.ts +0 -46
- package/dist/core/lbug/content-read.js +0 -64
- package/dist/core/lbug/csv-generator.d.ts +0 -29
- package/dist/core/lbug/csv-generator.js +0 -492
- package/dist/core/lbug/lbug-adapter.d.ts +0 -176
- package/dist/core/lbug/lbug-adapter.js +0 -1320
- package/dist/core/lbug/pool-adapter.d.ts +0 -93
- package/dist/core/lbug/pool-adapter.js +0 -550
- package/dist/core/lbug/schema.d.ts +0 -62
- package/dist/core/lbug/schema.js +0 -502
- package/dist/mcp/core/lbug-adapter.d.ts +0 -5
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
300
|
+
npx @codragraph/cli@2.1.2 analyze # no global install
|
|
300
301
|
# or
|
|
301
|
-
npm install -g @codragraph/cli@2.1.
|
|
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
|
|
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
|
|
package/dist/cli/ai-context.js
CHANGED
|
@@ -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:
|
|
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
|
|
package/dist/cli/analyze.js
CHANGED
|
@@ -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('
|
|
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
|
|
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
|
|
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')
|
package/dist/cli/serve.d.ts
CHANGED
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
|
-
|
|
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
|
|
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
|
|
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
|
|
70
|
-
*
|
|
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
|
|
80
|
-
const
|
|
81
|
-
|
|
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 {
|
|
90
|
+
return {
|
|
91
|
+
command: 'cmd',
|
|
92
|
+
args: ['/c', 'bunx', CLI_PACKAGE_SPEC, 'mcp'],
|
|
93
|
+
};
|
|
84
94
|
}
|
|
85
|
-
return {
|
|
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
|
-
|
|
112
|
-
|
|
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.
|
package/dist/cli/status.d.ts
CHANGED
|
@@ -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>;
|
package/dist/cli/status.js
CHANGED
|
@@ -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
|
};
|
|
@@ -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
|
-
|
|
298
|
-
|
|
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
|
|
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
|
*
|