@codragraph/cli 2.0.0 → 2.1.1
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 +60 -22
- package/dist/_shared/cgdb/schema-constants.d.ts +16 -0
- package/dist/_shared/cgdb/schema-constants.d.ts.map +1 -0
- package/dist/_shared/cgdb/schema-constants.js +70 -0
- package/dist/_shared/cgdb/schema-constants.js.map +1 -0
- package/dist/_shared/feature-clusters.d.ts +99 -0
- package/dist/_shared/feature-clusters.d.ts.map +1 -0
- package/dist/_shared/feature-clusters.js +2 -0
- package/dist/_shared/feature-clusters.js.map +1 -0
- package/dist/_shared/graph/types.d.ts +16 -2
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +3 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +1 -1
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/pipeline.d.ts +1 -1
- package/dist/_shared/pipeline.d.ts.map +1 -1
- package/dist/cli/ai-context.js +4 -0
- package/dist/cli/analyze.js +30 -27
- package/dist/cli/graphstore.js +21 -21
- package/dist/cli/index-repo.js +3 -3
- package/dist/cli/index.js +37 -0
- package/dist/cli/setup.js +9 -5
- package/dist/cli/tool.d.ts +25 -0
- package/dist/cli/tool.js +74 -0
- package/dist/cli/wiki.js +3 -3
- package/dist/config/supported-languages.d.ts +3 -3
- package/dist/config/supported-languages.js +3 -3
- package/dist/core/augmentation/engine.js +7 -7
- package/dist/core/cgdb/cgdb-adapter.d.ts +176 -0
- package/dist/core/cgdb/cgdb-adapter.js +1336 -0
- package/dist/core/cgdb/content-read.d.ts +46 -0
- package/dist/core/cgdb/content-read.js +64 -0
- package/dist/core/cgdb/csv-generator.d.ts +29 -0
- package/dist/core/cgdb/csv-generator.js +523 -0
- package/dist/core/cgdb/pool-adapter.d.ts +93 -0
- package/dist/core/cgdb/pool-adapter.js +550 -0
- package/dist/core/cgdb/schema.d.ts +63 -0
- package/dist/core/cgdb/schema.js +557 -0
- package/dist/core/embeddings/embedder.js +4 -2
- package/dist/core/embeddings/embedding-pipeline.js +4 -4
- package/dist/core/graphstore/cgdb-row-source.d.ts +19 -0
- package/dist/core/graphstore/cgdb-row-source.js +141 -0
- package/dist/core/graphstore/index.d.ts +2 -2
- package/dist/core/graphstore/index.js +4 -4
- package/dist/core/group/bridge-db.d.ts +2 -2
- package/dist/core/group/bridge-db.js +18 -18
- package/dist/core/group/bridge-schema.d.ts +4 -4
- package/dist/core/group/bridge-schema.js +4 -4
- package/dist/core/group/cross-impact.js +3 -3
- package/dist/core/group/service.d.ts +16 -0
- package/dist/core/group/service.js +360 -0
- package/dist/core/group/sync.js +4 -4
- package/dist/core/ingestion/emit-references.d.ts +1 -1
- package/dist/core/ingestion/emit-references.js +1 -1
- package/dist/core/ingestion/feature-cluster-processor.d.ts +62 -0
- package/dist/core/ingestion/feature-cluster-processor.js +626 -0
- package/dist/core/ingestion/finalize-orchestrator.js +1 -1
- package/dist/core/ingestion/model/registration-table.js +1 -0
- package/dist/core/ingestion/model/resolve.d.ts +2 -2
- package/dist/core/ingestion/model/resolve.js +3 -3
- package/dist/core/ingestion/model/semantic-model.d.ts +1 -1
- package/dist/core/ingestion/model/semantic-model.js +1 -1
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/model/symbol-table.js +1 -1
- package/dist/core/ingestion/pipeline-phases/feature-clusters.d.ts +17 -0
- package/dist/core/ingestion/pipeline-phases/feature-clusters.js +88 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline.d.ts +4 -0
- package/dist/core/ingestion/pipeline.js +9 -5
- package/dist/core/run-analyze.d.ts +1 -0
- package/dist/core/run-analyze.js +36 -30
- package/dist/core/search/bm25-index.d.ts +3 -3
- package/dist/core/search/bm25-index.js +9 -9
- package/dist/core/search/hybrid-search.js +2 -2
- package/dist/core/wiki/generator.d.ts +2 -2
- package/dist/core/wiki/generator.js +4 -4
- package/dist/core/wiki/graph-queries.d.ts +2 -2
- package/dist/core/wiki/graph-queries.js +5 -5
- package/dist/mcp/core/cgdb-adapter.d.ts +5 -0
- package/dist/mcp/core/cgdb-adapter.js +5 -0
- package/dist/mcp/core/embedder.js +6 -3
- package/dist/mcp/local/local-backend.d.ts +14 -2
- package/dist/mcp/local/local-backend.js +396 -18
- package/dist/mcp/resources.js +139 -0
- package/dist/mcp/server.js +3 -3
- package/dist/mcp/tools.js +175 -3
- package/dist/server/analyze-worker.js +2 -2
- package/dist/server/api.js +147 -31
- package/dist/storage/repo-manager.d.ts +10 -5
- package/dist/storage/repo-manager.js +10 -6
- package/dist/types/pipeline.d.ts +2 -0
- package/hooks/claude/codragraph-hook.cjs +4 -4
- package/package.json +15 -6
- package/scripts/build.js +21 -21
- package/skills/codragraph-cli.md +17 -1
- package/skills/codragraph-guide.md +6 -2
- package/skills/codragraph-onboarding.md +2 -2
- package/vendor/tree-sitter-proto/bindings/node/index.js +3 -3
- package/vendor/tree-sitter-proto/src/node-types.json +1 -1
package/dist/cli/analyze.js
CHANGED
|
@@ -12,12 +12,16 @@ import { execFileSync } from 'child_process';
|
|
|
12
12
|
import v8 from 'v8';
|
|
13
13
|
import cliProgress from 'cli-progress';
|
|
14
14
|
import * as fsSync from 'node:fs';
|
|
15
|
-
import {
|
|
15
|
+
import { createRequire } from 'module';
|
|
16
|
+
import { closeCgdb } from '../core/cgdb/cgdb-adapter.js';
|
|
16
17
|
import { getStoragePaths, getGlobalRegistryPath, RegistryNameCollisionError, } from '../storage/repo-manager.js';
|
|
17
18
|
import { getGitRoot, hasGitDir } from '../storage/git.js';
|
|
18
19
|
import { runFullAnalysis } from '../core/run-analyze.js';
|
|
19
20
|
import { getMaxFileSizeBannerMessage } from '../core/ingestion/utils/max-file-size.js';
|
|
20
21
|
import fs from 'fs/promises';
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const pkg = require('../../package.json');
|
|
24
|
+
const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
|
|
21
25
|
const HEAP_MB = 8192;
|
|
22
26
|
const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
|
|
23
27
|
/** Increase default stack size (KB) to prevent stack overflow on deep class hierarchies. */
|
|
@@ -79,7 +83,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
79
83
|
// gracefully falls back to name-only matches instead of tokenising
|
|
80
84
|
// base64 garbage. Surface the trade-off so users know what they're
|
|
81
85
|
// opting into.
|
|
82
|
-
console.warn(` Note: --compress ${options.compress} reduces .codragraph/
|
|
86
|
+
console.warn(` Note: --compress ${options.compress} reduces .codragraph/cgdb size.\n` +
|
|
83
87
|
` BM25 search will index symbol names only (function bodies are not tokenised\n` +
|
|
84
88
|
` when compressed); embeddings, graph queries, and \`context\` / \`impact\` are\n` +
|
|
85
89
|
` unaffected. Run with --compress none if you rely on full-text search inside\n` +
|
|
@@ -105,25 +109,6 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
105
109
|
}
|
|
106
110
|
});
|
|
107
111
|
}
|
|
108
|
-
// ── First-run auto-setup ───────────────────────────────────────────
|
|
109
|
-
// Makes `npx @codragraph/cli analyze` a true one-command entry. We detect
|
|
110
|
-
// first-run by the absence of the global registry — analyze writes to it on
|
|
111
|
-
// every successful index, so it's a reliable "this user has never run us
|
|
112
|
-
// before" signal. Opt out with `--no-setup` for CI / headless contexts;
|
|
113
|
-
// commander maps `--no-setup` to `options.setup === false`.
|
|
114
|
-
if (options?.setup !== false) {
|
|
115
|
-
let registryExists = true;
|
|
116
|
-
try {
|
|
117
|
-
await fs.access(getGlobalRegistryPath());
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
registryExists = false;
|
|
121
|
-
}
|
|
122
|
-
if (!registryExists) {
|
|
123
|
-
const { runSetup } = await import('./setup.js');
|
|
124
|
-
await runSetup({ skipNextSteps: true, compactHeader: true });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
112
|
console.log('\n CodraGraph Analyzer\n');
|
|
128
113
|
let repoPath;
|
|
129
114
|
if (inputPath) {
|
|
@@ -153,6 +138,23 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
153
138
|
if (!repoHasGit) {
|
|
154
139
|
console.log(' Warning: no .git directory found \u2014 commit-tracking and incremental updates disabled.\n');
|
|
155
140
|
}
|
|
141
|
+
// ── First-run auto-setup ───────────────────────────────────────────
|
|
142
|
+
// Makes `npx @codragraph/cli analyze` a true one-command entry. Validate
|
|
143
|
+
// the target repo first so invalid invocations fail fast without mutating
|
|
144
|
+
// editor/global config.
|
|
145
|
+
if (options?.setup !== false) {
|
|
146
|
+
let registryExists = true;
|
|
147
|
+
try {
|
|
148
|
+
await fs.access(getGlobalRegistryPath());
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
registryExists = false;
|
|
152
|
+
}
|
|
153
|
+
if (!registryExists) {
|
|
154
|
+
const { runSetup } = await import('./setup.js');
|
|
155
|
+
await runSetup({ skipNextSteps: true, compactHeader: true });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
156
158
|
// KuzuDB migration cleanup is handled by runFullAnalysis internally.
|
|
157
159
|
// Note: --skills is handled after runFullAnalysis using the returned pipelineResult.
|
|
158
160
|
if (process.env.CODRAGRAPH_NO_GITIGNORE) {
|
|
@@ -182,7 +184,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
182
184
|
aborted = true;
|
|
183
185
|
bar.stop();
|
|
184
186
|
console.log('\n Interrupted — cleaning up...');
|
|
185
|
-
|
|
187
|
+
closeCgdb()
|
|
186
188
|
.catch(() => { })
|
|
187
189
|
.finally(() => process.exit(130));
|
|
188
190
|
};
|
|
@@ -299,7 +301,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
299
301
|
nodes: s.nodes ?? 0,
|
|
300
302
|
edges: s.edges ?? 0,
|
|
301
303
|
communities: s.communities,
|
|
302
|
-
clusters:
|
|
304
|
+
clusters: result.pipelineResult?.featureClusterResult?.stats.totalClusters ??
|
|
305
|
+
aggregatedClusterCount,
|
|
303
306
|
processes: s.processes,
|
|
304
307
|
}, skillResult.skills, { skipAgentsMd: options?.skipAgentsMd, noStats: options?.noStats });
|
|
305
308
|
}
|
|
@@ -392,8 +395,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
392
395
|
console.error(' Suggestions:');
|
|
393
396
|
console.error(' 1. Clear the npm cache: npm cache clean --force');
|
|
394
397
|
console.error(' 2. Update npm: npm install -g npm@latest');
|
|
395
|
-
console.error(
|
|
396
|
-
console.error(
|
|
398
|
+
console.error(` 3. Reinstall codragraph: npm install -g ${CLI_PACKAGE_SPEC}`);
|
|
399
|
+
console.error(` 4. Or try npx directly: npx ${CLI_PACKAGE_SPEC} analyze`);
|
|
397
400
|
console.error('');
|
|
398
401
|
}
|
|
399
402
|
else if (msg.includes('MODULE_NOT_FOUND') ||
|
|
@@ -401,8 +404,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
401
404
|
msg.includes('ERR_MODULE_NOT_FOUND')) {
|
|
402
405
|
console.error(' A required module could not be loaded. The installation may be corrupt.');
|
|
403
406
|
console.error(' Suggestions:');
|
|
404
|
-
console.error(
|
|
405
|
-
console.error(
|
|
407
|
+
console.error(` 1. Reinstall: npm install -g ${CLI_PACKAGE_SPEC}`);
|
|
408
|
+
console.error(` 2. Clear cache: npm cache clean --force && npx ${CLI_PACKAGE_SPEC} analyze`);
|
|
406
409
|
console.error('');
|
|
407
410
|
}
|
|
408
411
|
process.exitCode = 1;
|
package/dist/cli/graphstore.js
CHANGED
|
@@ -12,7 +12,7 @@ import fs from 'node:fs/promises';
|
|
|
12
12
|
import { FsCAS, createBranch, createCommit, DEFAULT_BRANCH, deleteBranch, diffSemantic, diffSnapshots, gc as runGc, getJson, listBranches, materializeSnapshot, parseObjectId, readCommit, readHead, resolveHeadCommit, setHead, threeWayMerge, walkCommits, writeHeadBranch, writeHeadDetached, } from '@codragraph/graphstore';
|
|
13
13
|
import { findRepo, loadMeta, saveMeta } from '../storage/repo-manager.js';
|
|
14
14
|
import { GRAPHSTORE_SUBDIR } from '../core/graphstore/index.js';
|
|
15
|
-
import {
|
|
15
|
+
import { initCgdb, closeCgdb } from '../core/cgdb/cgdb-adapter.js';
|
|
16
16
|
import { recordAnalysisSnapshot } from '../core/graphstore/index.js';
|
|
17
17
|
import { getCurrentCommit, hasGitDir } from '../storage/git.js';
|
|
18
18
|
const resolveGraphstore = async (cwd) => {
|
|
@@ -164,7 +164,7 @@ export const commitCommand = async (opts = {}) => {
|
|
|
164
164
|
const repo = await findRepo(process.cwd());
|
|
165
165
|
if (!repo)
|
|
166
166
|
throw new Error('No CodraGraph index found');
|
|
167
|
-
await
|
|
167
|
+
await initCgdb(repo.cgdbPath);
|
|
168
168
|
try {
|
|
169
169
|
const result = await recordAnalysisSnapshot({
|
|
170
170
|
storagePath: repo.storagePath,
|
|
@@ -191,7 +191,7 @@ export const commitCommand = async (opts = {}) => {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
finally {
|
|
194
|
-
await
|
|
194
|
+
await closeCgdb();
|
|
195
195
|
}
|
|
196
196
|
// Avoid unused warnings while keeping the import live for future callers.
|
|
197
197
|
void ctx;
|
|
@@ -221,10 +221,10 @@ export const branchCreateCommand = async (name, opts = {}) => {
|
|
|
221
221
|
// ──────────────────────────────────────────────────────────────────────
|
|
222
222
|
//
|
|
223
223
|
// Two modes:
|
|
224
|
-
// - default : just move HEAD (no
|
|
224
|
+
// - default : just move HEAD (no cgdb rewrite). Cheap; good for
|
|
225
225
|
// log/diff inspection from a different vantage point.
|
|
226
226
|
// - --materialize : also rebuild the live LadybugDB from the target
|
|
227
|
-
// snapshot. Destructive of the current
|
|
227
|
+
// snapshot. Destructive of the current cgdb state —
|
|
228
228
|
// run `commit` first if you have unsaved changes.
|
|
229
229
|
export const checkoutCommand = async (target, opts = {}) => {
|
|
230
230
|
const ctx = await resolveGraphstore(process.cwd());
|
|
@@ -247,9 +247,9 @@ export const checkoutCommand = async (target, opts = {}) => {
|
|
|
247
247
|
throw new Error('No CodraGraph index found');
|
|
248
248
|
const commit = await readCommit(ctx.cas, commitId);
|
|
249
249
|
process.stdout.write(`materializing snapshot ${commit.snapshot.slice(7, 7 + 12)}...\n`);
|
|
250
|
-
// Wipe and reinit
|
|
251
|
-
await
|
|
252
|
-
for (const f of [repo.
|
|
250
|
+
// Wipe and reinit cgdb.
|
|
251
|
+
await closeCgdb();
|
|
252
|
+
for (const f of [repo.cgdbPath, `${repo.cgdbPath}.wal`, `${repo.cgdbPath}.lock`]) {
|
|
253
253
|
try {
|
|
254
254
|
await fs.rm(f, { recursive: true, force: true });
|
|
255
255
|
}
|
|
@@ -257,9 +257,9 @@ export const checkoutCommand = async (target, opts = {}) => {
|
|
|
257
257
|
/* swallow */
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
-
await
|
|
260
|
+
await initCgdb(repo.cgdbPath);
|
|
261
261
|
try {
|
|
262
|
-
const sink = await
|
|
262
|
+
const sink = await createCgdbRowSinkForCheckout(repo.cgdbPath);
|
|
263
263
|
const result = await materializeSnapshot({
|
|
264
264
|
cas: ctx.cas,
|
|
265
265
|
snapshotId: commit.snapshot,
|
|
@@ -269,14 +269,14 @@ export const checkoutCommand = async (target, opts = {}) => {
|
|
|
269
269
|
`${result.stats.edgeRowCount} edges\n`);
|
|
270
270
|
}
|
|
271
271
|
finally {
|
|
272
|
-
await
|
|
272
|
+
await closeCgdb();
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
};
|
|
276
276
|
// ──────────────────────────────────────────────────────────────────────
|
|
277
277
|
// codragraph materialize <target> --into <path>
|
|
278
278
|
// ──────────────────────────────────────────────────────────────────────
|
|
279
|
-
// Read-only inspection: rebuild a snapshot into a fresh sibling
|
|
279
|
+
// Read-only inspection: rebuild a snapshot into a fresh sibling cgdb
|
|
280
280
|
// without touching the live one. Useful for "let me query the graph as
|
|
281
281
|
// it was at commit X" without disturbing current work.
|
|
282
282
|
export const materializeCommand = async (target, opts) => {
|
|
@@ -295,9 +295,9 @@ export const materializeCommand = async (target, opts) => {
|
|
|
295
295
|
catch {
|
|
296
296
|
/* doesn't exist, good */
|
|
297
297
|
}
|
|
298
|
-
await
|
|
298
|
+
await initCgdb(into);
|
|
299
299
|
try {
|
|
300
|
-
const sink = await
|
|
300
|
+
const sink = await createCgdbRowSinkForCheckout(into);
|
|
301
301
|
const result = await materializeSnapshot({
|
|
302
302
|
cas: ctx.cas,
|
|
303
303
|
snapshotId: commit.snapshot,
|
|
@@ -310,7 +310,7 @@ export const materializeCommand = async (target, opts) => {
|
|
|
310
310
|
`edges: ${result.stats.edgeRowCount}\n`);
|
|
311
311
|
}
|
|
312
312
|
finally {
|
|
313
|
-
await
|
|
313
|
+
await closeCgdb();
|
|
314
314
|
}
|
|
315
315
|
};
|
|
316
316
|
// ──────────────────────────────────────────────────────────────────────
|
|
@@ -386,7 +386,7 @@ const locateSymbolHash = (manifest, symbolId, tableHint) => {
|
|
|
386
386
|
return null;
|
|
387
387
|
};
|
|
388
388
|
// ──────────────────────────────────────────────────────────────────────
|
|
389
|
-
//
|
|
389
|
+
// CgdbRowSink — used by checkout --materialize and `materialize` command.
|
|
390
390
|
// ──────────────────────────────────────────────────────────────────────
|
|
391
391
|
//
|
|
392
392
|
// Bulk-loads rows back into a fresh LadybugDB instance. Phase 4 keeps it
|
|
@@ -394,9 +394,9 @@ const locateSymbolHash = (manifest, symbolId, tableHint) => {
|
|
|
394
394
|
// `executeWithReusedStatement` helper. For typical repos (≤100k rows)
|
|
395
395
|
// this finishes in seconds; CSV-based bulk loading is a Phase 4.5
|
|
396
396
|
// optimization once we measure where the bottleneck actually is.
|
|
397
|
-
const
|
|
398
|
-
const { executeQuery } = await import('../core/
|
|
399
|
-
const { SCHEMA_QUERIES } = await import('../core/
|
|
397
|
+
const createCgdbRowSinkForCheckout = async (cgdbPath) => {
|
|
398
|
+
const { executeQuery } = await import('../core/cgdb/cgdb-adapter.js');
|
|
399
|
+
const { SCHEMA_QUERIES } = await import('../core/cgdb/schema.js');
|
|
400
400
|
// Recreate the schema; the path was just wiped, so this is a clean install.
|
|
401
401
|
for (const ddl of SCHEMA_QUERIES) {
|
|
402
402
|
try {
|
|
@@ -424,7 +424,7 @@ const createLbugRowSinkForCheckout = async (lbugPath) => {
|
|
|
424
424
|
endEdges: async () => { },
|
|
425
425
|
finalize: async () => { },
|
|
426
426
|
};
|
|
427
|
-
void
|
|
427
|
+
void cgdbPath;
|
|
428
428
|
return sink;
|
|
429
429
|
};
|
|
430
430
|
const insertNode = async (executeQuery, table, row) => {
|
|
@@ -468,7 +468,7 @@ const cypherLiteral = (v) => {
|
|
|
468
468
|
return JSON.stringify(v);
|
|
469
469
|
if (typeof v === 'number' || typeof v === 'boolean')
|
|
470
470
|
return String(v);
|
|
471
|
-
// Fall back to JSON for arrays / nested objects; the underlying
|
|
471
|
+
// Fall back to JSON for arrays / nested objects; the underlying cgdb
|
|
472
472
|
// schema is mostly scalar so this is rare.
|
|
473
473
|
return JSON.stringify(v);
|
|
474
474
|
};
|
package/dist/cli/index-repo.js
CHANGED
|
@@ -49,7 +49,7 @@ export const indexCommand = async (inputPathParts, options) => {
|
|
|
49
49
|
process.exitCode = 1;
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
|
-
const { storagePath,
|
|
52
|
+
const { storagePath, cgdbPath } = getStoragePaths(repoPath);
|
|
53
53
|
// ── Verify .codragraph/ exists ──────────────────────────────────────
|
|
54
54
|
try {
|
|
55
55
|
await fs.access(storagePath);
|
|
@@ -60,9 +60,9 @@ export const indexCommand = async (inputPathParts, options) => {
|
|
|
60
60
|
process.exitCode = 1;
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
// ── Verify
|
|
63
|
+
// ── Verify cgdb database exists ───────────────────────────────────
|
|
64
64
|
try {
|
|
65
|
-
await fs.access(
|
|
65
|
+
await fs.access(cgdbPath);
|
|
66
66
|
}
|
|
67
67
|
catch {
|
|
68
68
|
console.log(` .codragraph/ folder exists but contains no LadybugDB index.`);
|
package/dist/cli/index.js
CHANGED
|
@@ -135,6 +135,43 @@ program
|
|
|
135
135
|
.description('Execute raw Cypher query against the knowledge graph')
|
|
136
136
|
.option('-r, --repo <name>', 'Target repository')
|
|
137
137
|
.action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
|
|
138
|
+
program
|
|
139
|
+
.command('feature-clusters')
|
|
140
|
+
.description('List human-facing feature clusters for targeted context building')
|
|
141
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
142
|
+
.option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
|
|
143
|
+
.action(createLazyAction(() => import('./tool.js'), 'featureClustersCommand'));
|
|
144
|
+
program
|
|
145
|
+
.command('cluster-query [query]')
|
|
146
|
+
.description('Alias for feature-clusters: list product/domain clusters')
|
|
147
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
148
|
+
.option('-l, --limit <n>', 'Max feature clusters to return (default: 100)')
|
|
149
|
+
.action(createLazyAction(() => import('./tool.js'), 'clusterQueryCommand'));
|
|
150
|
+
program
|
|
151
|
+
.command('feature-context <name>')
|
|
152
|
+
.description('Show members, line ranges, and dependencies for a feature cluster')
|
|
153
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
154
|
+
.option('-l, --limit <n>', 'Max members to return (default: 100)')
|
|
155
|
+
.action(createLazyAction(() => import('./tool.js'), 'featureContextCommand'));
|
|
156
|
+
program
|
|
157
|
+
.command('cluster-context <name>')
|
|
158
|
+
.description('Alias for feature-context: show a feature cluster context pack')
|
|
159
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
160
|
+
.option('-l, --limit <n>', 'Max members to return (default: 100)')
|
|
161
|
+
.action(createLazyAction(() => import('./tool.js'), 'clusterContextCommand'));
|
|
162
|
+
program
|
|
163
|
+
.command('context-pack <name>')
|
|
164
|
+
.description('Generate the compact agent context pack for a feature cluster')
|
|
165
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
166
|
+
.option('-l, --limit <n>', 'Max members to return (default: 100)')
|
|
167
|
+
.action(createLazyAction(() => import('./tool.js'), 'contextPackCommand'));
|
|
168
|
+
program
|
|
169
|
+
.command('cluster-impact <name>')
|
|
170
|
+
.description('Feature-level blast radius analysis for a cluster')
|
|
171
|
+
.option('-d, --direction <dir>', 'upstream, downstream, or both', 'upstream')
|
|
172
|
+
.option('-r, --repo <name>', 'Target repository')
|
|
173
|
+
.option('-l, --limit <n>', 'Max context-pack members to include (default: 100)')
|
|
174
|
+
.action(createLazyAction(() => import('./tool.js'), 'clusterImpactCommand'));
|
|
138
175
|
program
|
|
139
176
|
.command('detect-changes')
|
|
140
177
|
.alias('detect_changes')
|
package/dist/cli/setup.js
CHANGED
|
@@ -11,12 +11,16 @@ import os from 'os';
|
|
|
11
11
|
import { execFile, execFileSync } from 'child_process';
|
|
12
12
|
import { promisify } from 'util';
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
|
+
import { createRequire } from 'module';
|
|
14
15
|
import { glob } from 'glob';
|
|
15
16
|
import { parseTree, modify, applyEdits } from 'jsonc-parser';
|
|
16
17
|
import { getGlobalDir } from '../storage/repo-manager.js';
|
|
17
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
19
|
const __dirname = path.dirname(__filename);
|
|
19
20
|
const execFileAsync = promisify(execFile);
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const pkg = require('../../package.json');
|
|
23
|
+
const CLI_PACKAGE_SPEC = `@codragraph/cli@${pkg.version}`;
|
|
20
24
|
/**
|
|
21
25
|
* Resolve the absolute path to the `codragraph` binary if it's installed
|
|
22
26
|
* globally (or via npm -g / yarn global). Returns null when not found.
|
|
@@ -59,7 +63,7 @@ function resolveCodragraphBin() {
|
|
|
59
63
|
* The MCP server entry for all editors.
|
|
60
64
|
*
|
|
61
65
|
* Prefers the globally-installed `codragraph` binary (starts in ~1 s) over
|
|
62
|
-
* `npx -y @codragraph/cli
|
|
66
|
+
* `npx -y @codragraph/cli@<version>` (cold-cache install of native deps can take
|
|
63
67
|
* >60 s, exceeding Claude Code's 30 s MCP connection timeout).
|
|
64
68
|
*
|
|
65
69
|
* Falls back to npx when the binary isn't on PATH — e.g. first-time
|
|
@@ -84,12 +88,12 @@ function getMcpEntry() {
|
|
|
84
88
|
if (process.platform === 'win32') {
|
|
85
89
|
return {
|
|
86
90
|
command: 'cmd',
|
|
87
|
-
args: ['/c', 'npx', '-y',
|
|
91
|
+
args: ['/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
|
|
88
92
|
};
|
|
89
93
|
}
|
|
90
94
|
return {
|
|
91
95
|
command: 'npx',
|
|
92
|
-
args: ['-y',
|
|
96
|
+
args: ['-y', CLI_PACKAGE_SPEC, 'mcp'],
|
|
93
97
|
};
|
|
94
98
|
}
|
|
95
99
|
/**
|
|
@@ -107,10 +111,10 @@ function getOpenCodeMcpEntry() {
|
|
|
107
111
|
if (process.platform === 'win32') {
|
|
108
112
|
return {
|
|
109
113
|
type: 'local',
|
|
110
|
-
command: ['cmd', '/c', 'npx', '-y',
|
|
114
|
+
command: ['cmd', '/c', 'npx', '-y', CLI_PACKAGE_SPEC, 'mcp'],
|
|
111
115
|
};
|
|
112
116
|
}
|
|
113
|
-
return { type: 'local', command: ['npx', '-y',
|
|
117
|
+
return { type: 'local', command: ['npx', '-y', CLI_PACKAGE_SPEC, 'mcp'] };
|
|
114
118
|
}
|
|
115
119
|
/**
|
|
116
120
|
* Merge codragraph entry into an existing MCP config JSON object.
|
package/dist/cli/tool.d.ts
CHANGED
|
@@ -36,6 +36,31 @@ export declare function impactCommand(target: string, options?: {
|
|
|
36
36
|
export declare function cypherCommand(query: string, options?: {
|
|
37
37
|
repo?: string;
|
|
38
38
|
}): Promise<void>;
|
|
39
|
+
export declare function featureClustersCommand(options?: {
|
|
40
|
+
repo?: string;
|
|
41
|
+
limit?: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
export declare function clusterQueryCommand(query?: string, options?: {
|
|
44
|
+
repo?: string;
|
|
45
|
+
limit?: string;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
export declare function featureContextCommand(name: string, options?: {
|
|
48
|
+
repo?: string;
|
|
49
|
+
limit?: string;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
export declare function clusterContextCommand(name: string, options?: {
|
|
52
|
+
repo?: string;
|
|
53
|
+
limit?: string;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
export declare function contextPackCommand(name: string, options?: {
|
|
56
|
+
repo?: string;
|
|
57
|
+
limit?: string;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
export declare function clusterImpactCommand(name: string, options?: {
|
|
60
|
+
direction?: string;
|
|
61
|
+
repo?: string;
|
|
62
|
+
limit?: string;
|
|
63
|
+
}): Promise<void>;
|
|
39
64
|
export declare function detectChangesCommand(options?: {
|
|
40
65
|
scope?: string;
|
|
41
66
|
baseRef?: string;
|
package/dist/cli/tool.js
CHANGED
|
@@ -128,6 +128,80 @@ export async function cypherCommand(query, options) {
|
|
|
128
128
|
});
|
|
129
129
|
output(result);
|
|
130
130
|
}
|
|
131
|
+
export async function featureClustersCommand(options) {
|
|
132
|
+
const backend = await getBackend();
|
|
133
|
+
const result = await backend.callTool('feature_clusters', {
|
|
134
|
+
repo: options?.repo,
|
|
135
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
136
|
+
});
|
|
137
|
+
output(result);
|
|
138
|
+
}
|
|
139
|
+
export async function clusterQueryCommand(query, options) {
|
|
140
|
+
const backend = await getBackend();
|
|
141
|
+
const result = await backend.callTool('cluster_query', {
|
|
142
|
+
query,
|
|
143
|
+
repo: options?.repo,
|
|
144
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
145
|
+
});
|
|
146
|
+
output(result);
|
|
147
|
+
}
|
|
148
|
+
export async function featureContextCommand(name, options) {
|
|
149
|
+
if (!name?.trim()) {
|
|
150
|
+
console.error('Usage: codragraph feature-context <name>');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const backend = await getBackend();
|
|
154
|
+
const result = await backend.callTool('feature_context', {
|
|
155
|
+
name,
|
|
156
|
+
repo: options?.repo,
|
|
157
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
158
|
+
});
|
|
159
|
+
output(result);
|
|
160
|
+
emitTokenStats(result);
|
|
161
|
+
}
|
|
162
|
+
export async function clusterContextCommand(name, options) {
|
|
163
|
+
if (!name?.trim()) {
|
|
164
|
+
console.error('Usage: codragraph cluster-context <name>');
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
const backend = await getBackend();
|
|
168
|
+
const result = await backend.callTool('cluster_context', {
|
|
169
|
+
name,
|
|
170
|
+
repo: options?.repo,
|
|
171
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
172
|
+
});
|
|
173
|
+
output(result);
|
|
174
|
+
emitTokenStats(result);
|
|
175
|
+
}
|
|
176
|
+
export async function contextPackCommand(name, options) {
|
|
177
|
+
if (!name?.trim()) {
|
|
178
|
+
console.error('Usage: codragraph context-pack <name>');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
const backend = await getBackend();
|
|
182
|
+
const result = await backend.callTool('context_pack', {
|
|
183
|
+
name,
|
|
184
|
+
repo: options?.repo,
|
|
185
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
186
|
+
});
|
|
187
|
+
output(result);
|
|
188
|
+
emitTokenStats(result);
|
|
189
|
+
}
|
|
190
|
+
export async function clusterImpactCommand(name, options) {
|
|
191
|
+
if (!name?.trim()) {
|
|
192
|
+
console.error('Usage: codragraph cluster-impact <name>');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
const backend = await getBackend();
|
|
196
|
+
const result = await backend.callTool('cluster_impact', {
|
|
197
|
+
name,
|
|
198
|
+
direction: options?.direction,
|
|
199
|
+
repo: options?.repo,
|
|
200
|
+
limit: options?.limit ? parseInt(options.limit, 10) : undefined,
|
|
201
|
+
});
|
|
202
|
+
output(result);
|
|
203
|
+
emitTokenStats(result);
|
|
204
|
+
}
|
|
131
205
|
function formatDetectChangesResult(result) {
|
|
132
206
|
if (result?.error)
|
|
133
207
|
return `Error: ${result.error}`;
|
package/dist/cli/wiki.js
CHANGED
|
@@ -91,7 +91,7 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
// ── Check for existing index ────────────────────────────────────────
|
|
94
|
-
const { storagePath,
|
|
94
|
+
const { storagePath, cgdbPath } = getStoragePaths(repoPath);
|
|
95
95
|
const meta = await loadMeta(storagePath);
|
|
96
96
|
if (!meta) {
|
|
97
97
|
console.log(' Error: No CodraGraph index found.');
|
|
@@ -333,7 +333,7 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
333
333
|
concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined,
|
|
334
334
|
reviewOnly: options?.review,
|
|
335
335
|
};
|
|
336
|
-
const generator = new WikiGenerator(repoPath, storagePath,
|
|
336
|
+
const generator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, wikiOptions, (phase, percent, detail) => {
|
|
337
337
|
const label = detail || phase;
|
|
338
338
|
if (label !== lastPhase) {
|
|
339
339
|
lastPhase = label;
|
|
@@ -406,7 +406,7 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
406
406
|
...wikiOptions,
|
|
407
407
|
reviewOnly: false,
|
|
408
408
|
};
|
|
409
|
-
const continueGenerator = new WikiGenerator(repoPath, storagePath,
|
|
409
|
+
const continueGenerator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, continueOptions, (phase, percent, detail) => {
|
|
410
410
|
const label = detail || phase;
|
|
411
411
|
if (label !== lastPhase) {
|
|
412
412
|
lastPhase = label;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Re-export SupportedLanguages from codragraph
|
|
2
|
+
* Re-export SupportedLanguages from @codragraph/shared (single source of truth).
|
|
3
3
|
*
|
|
4
4
|
* HOW TO ADD A NEW LANGUAGE:
|
|
5
5
|
*
|
|
6
|
-
* 1. Add the enum member in
|
|
6
|
+
* 1. Add the enum member in packages/shared/src/languages.ts
|
|
7
7
|
* 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
|
|
8
8
|
* 3. Use the checklist in each ingestion file for what to add
|
|
9
|
-
* 4. Add tree-sitter-<lang> to
|
|
9
|
+
* 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
|
|
10
10
|
* 5. Add file extension mapping in utils.ts getLanguageFromFilename()
|
|
11
11
|
* 6. Run full test suite
|
|
12
12
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Re-export SupportedLanguages from codragraph
|
|
2
|
+
* Re-export SupportedLanguages from @codragraph/shared (single source of truth).
|
|
3
3
|
*
|
|
4
4
|
* HOW TO ADD A NEW LANGUAGE:
|
|
5
5
|
*
|
|
6
|
-
* 1. Add the enum member in
|
|
6
|
+
* 1. Add the enum member in packages/shared/src/languages.ts
|
|
7
7
|
* 2. Run `tsc --noEmit` — compiler errors guide you to every dispatch table
|
|
8
8
|
* 3. Use the checklist in each ingestion file for what to add
|
|
9
|
-
* 4. Add tree-sitter-<lang> to
|
|
9
|
+
* 4. Add tree-sitter-<lang> to packages/core/package.json dependencies
|
|
10
10
|
* 5. Add file extension mapping in utils.ts getLanguageFromFilename()
|
|
11
11
|
* 6. Run full test suite
|
|
12
12
|
*/
|
|
@@ -56,7 +56,7 @@ async function findRepoForCwd(cwd) {
|
|
|
56
56
|
return {
|
|
57
57
|
name: bestMatch.name,
|
|
58
58
|
storagePath: bestMatch.storagePath,
|
|
59
|
-
|
|
59
|
+
cgdbPath: path.join(bestMatch.storagePath, 'cgdb'),
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
catch {
|
|
@@ -81,16 +81,16 @@ export async function augment(pattern, cwd) {
|
|
|
81
81
|
const repo = await findRepoForCwd(workDir);
|
|
82
82
|
if (!repo)
|
|
83
83
|
return '';
|
|
84
|
-
// Lazy-load
|
|
85
|
-
const {
|
|
86
|
-
const {
|
|
84
|
+
// Lazy-load cgdb adapter (skip unnecessary init)
|
|
85
|
+
const { initCgdb, executeQuery, isCgdbReady } = await import('../cgdb/pool-adapter.js');
|
|
86
|
+
const { searchFTSFromCgdb } = await import('../search/bm25-index.js');
|
|
87
87
|
const repoId = repo.name.toLowerCase();
|
|
88
88
|
// Init LadybugDB if not already
|
|
89
|
-
if (!
|
|
90
|
-
await
|
|
89
|
+
if (!isCgdbReady(repoId)) {
|
|
90
|
+
await initCgdb(repoId, repo.cgdbPath);
|
|
91
91
|
}
|
|
92
92
|
// Step 1: BM25 search (fast, no embeddings)
|
|
93
|
-
const bm25Results = await
|
|
93
|
+
const bm25Results = await searchFTSFromCgdb(pattern, 10, repoId);
|
|
94
94
|
if (bm25Results.length === 0)
|
|
95
95
|
return '';
|
|
96
96
|
// Step 2: Map BM25 file results to symbols
|