@codragraph/cli 1.6.3 → 1.6.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 (37) hide show
  1. package/README.md +16 -16
  2. package/dist/cli/ai-context.js +2 -2
  3. package/dist/cli/analyze.js +4 -4
  4. package/dist/cli/index.js +1 -4
  5. package/dist/cli/setup.js +53 -18
  6. package/dist/cli/skill-gen.js +1 -1
  7. package/dist/config/ignore-service.js +1 -1
  8. package/dist/core/group/extractors/grpc-patterns/proto.js +1 -12
  9. package/dist/core/ingestion/call-processor.js +2 -2
  10. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1 -1
  11. package/dist/core/ingestion/cobol/jcl-parser.d.ts +1 -1
  12. package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
  13. package/dist/core/ingestion/cobol-processor.d.ts +1 -1
  14. package/dist/core/ingestion/cobol-processor.js +1 -1
  15. package/dist/core/ingestion/heritage-extractors/generic.js +1 -1
  16. package/dist/core/ingestion/heritage-processor.js +1 -1
  17. package/dist/core/ingestion/import-processor.js +1 -1
  18. package/dist/core/ingestion/mro-processor.js +1 -1
  19. package/dist/core/ingestion/parsing-processor.js +1 -1
  20. package/dist/core/ingestion/type-extractors/c-cpp.js +1 -1
  21. package/dist/core/ingestion/type-extractors/python.js +1 -1
  22. package/dist/core/ingestion/type-extractors/shared.js +0 -3
  23. package/dist/core/lbug/lbug-adapter.js +4 -4
  24. package/dist/core/lbug/pool-adapter.js +51 -44
  25. package/dist/core/search/bm25-index.js +90 -1
  26. package/dist/core/wiki/generator.js +4 -4
  27. package/dist/mcp/resources.js +2 -3
  28. package/hooks/claude/codragraph-hook.cjs +13 -3
  29. package/hooks/claude/pre-tool-use.sh +6 -1
  30. package/package.json +2 -2
  31. package/skills/codragraph-cli.md +5 -5
  32. package/skills/codragraph-debugging.md +1 -1
  33. package/skills/codragraph-exploring.md +1 -1
  34. package/skills/codragraph-guide.md +1 -1
  35. package/skills/codragraph-impact-analysis.md +1 -1
  36. package/skills/codragraph-pr-review.md +1 -1
  37. package/skills/codragraph-refactoring.md +1 -1
package/README.md CHANGED
@@ -18,12 +18,12 @@ AI coding tools don't understand your codebase structure. They edit a function w
18
18
 
19
19
  ```bash
20
20
  # Index your repo (run from repo root)
21
- npx codragraph analyze
21
+ npx @codragraph/cli analyze
22
22
  ```
23
23
 
24
24
  That's it. This indexes the codebase, installs agent skills, registers Claude Code hooks, and creates `AGENTS.md` / `CLAUDE.md` context files — all in one command.
25
25
 
26
- To configure MCP for your editor, run `npx codragraph setup` once — or set it up manually below.
26
+ To configure MCP for your editor, run `npx @codragraph/cli setup` once — or set it up manually below.
27
27
 
28
28
  `codragraph setup` auto-detects your editors and writes the correct global MCP config. You only need to run it once.
29
29
 
@@ -53,16 +53,16 @@ If you prefer to configure manually instead of using `codragraph setup`:
53
53
 
54
54
  ```bash
55
55
  # macOS / Linux
56
- claude mcp add codragraph -- npx -y codragraph@latest mcp
56
+ claude mcp add codragraph -- npx -y @codragraph/cli@latest mcp
57
57
 
58
58
  # Windows
59
- claude mcp add codragraph -- cmd /c npx -y codragraph@latest mcp
59
+ claude mcp add codragraph -- cmd /c npx -y @codragraph/cli@latest mcp
60
60
  ```
61
61
 
62
62
  ### Codex (full support — MCP + skills)
63
63
 
64
64
  ```bash
65
- codex mcp add codragraph -- npx -y codragraph@latest mcp
65
+ codex mcp add codragraph -- npx -y @codragraph/cli@latest mcp
66
66
  ```
67
67
 
68
68
  ### Cursor / Windsurf
@@ -74,7 +74,7 @@ Add to `~/.cursor/mcp.json` (global — works for all projects):
74
74
  "mcpServers": {
75
75
  "codragraph": {
76
76
  "command": "npx",
77
- "args": ["-y", "codragraph@latest", "mcp"]
77
+ "args": ["-y", "@codragraph/cli@latest", "mcp"]
78
78
  }
79
79
  }
80
80
  }
@@ -89,7 +89,7 @@ Add to `~/.config/opencode/config.json`:
89
89
  "mcp": {
90
90
  "codragraph": {
91
91
  "command": "npx",
92
- "args": ["-y", "codragraph@latest", "mcp"]
92
+ "args": ["-y", "@codragraph/cli@latest", "mcp"]
93
93
  }
94
94
  }
95
95
  }
@@ -244,9 +244,9 @@ merges are skipped.)
244
244
 
245
245
  ```bash
246
246
  # Try the latest release candidate (pre-stable — may change at any time)
247
- npm install -g codragraph@rc
247
+ npm install -g @codragraph/cli@rc
248
248
  # — or —
249
- npx codragraph@rc analyze
249
+ npx @codragraph/cli@rc analyze
250
250
  ```
251
251
 
252
252
  Release-candidate versions follow the standard semver prerelease format
@@ -265,9 +265,9 @@ certain npm/arborist versions ([npm/cli#8126](https://github.com/npm/cli/issues/
265
265
  It is fixed in **codragraph v1.6.2+**. Upgrade to the latest version:
266
266
 
267
267
  ```bash
268
- npx codragraph@latest analyze # always uses the newest release
268
+ npx @codragraph/cli@latest analyze # always uses the newest release
269
269
  # — or —
270
- npm install -g codragraph@latest # upgrade a global install
270
+ npm install -g @codragraph/cli@latest # upgrade a global install
271
271
  ```
272
272
 
273
273
  If you still hit npm install issues after upgrading, these generic workarounds
@@ -282,7 +282,7 @@ npm cache clean --force # clear a possibly corrupt cache
282
282
 
283
283
  Some optional language grammars (Dart, Kotlin, Swift) require native compilation. If they fail, CodraGraph still works — those languages will be skipped.
284
284
 
285
- If `npm install -g codragraph` fails on native modules:
285
+ If `npm install -g @codragraph/cli` fails on native modules:
286
286
 
287
287
  ```bash
288
288
  # Ensure build tools are available (Linux/macOS)
@@ -290,7 +290,7 @@ If `npm install -g codragraph` fails on native modules:
290
290
  # macOS: xcode-select --install
291
291
 
292
292
  # Retry installation
293
- npm install -g codragraph
293
+ npm install -g @codragraph/cli
294
294
  ```
295
295
 
296
296
  ### Analysis runs out of memory
@@ -299,7 +299,7 @@ For very large repositories:
299
299
 
300
300
  ```bash
301
301
  # Increase Node.js heap size
302
- NODE_OPTIONS="--max-old-space-size=16384" npx codragraph analyze
302
+ NODE_OPTIONS="--max-old-space-size=16384" npx @codragraph/cli analyze
303
303
 
304
304
  # Exclude large directories
305
305
  echo "vendor/" >> .codragraphignore
@@ -312,11 +312,11 @@ By default the walker skips files larger than **512 KB** (see log line `Skipped
312
312
 
313
313
  ```bash
314
314
  # CLI flag (takes precedence over the env var)
315
- npx codragraph analyze --max-file-size 2048 # skip only files > 2 MB
315
+ npx @codragraph/cli analyze --max-file-size 2048 # skip only files > 2 MB
316
316
 
317
317
  # Environment variable (persists across commands)
318
318
  export CODRAGRAPH_MAX_FILE_SIZE=2048
319
- npx codragraph analyze
319
+ npx @codragraph/cli analyze
320
320
  ```
321
321
 
322
322
  Values above **32768 KB (32 MB)** are clamped to the tree-sitter parser ceiling; invalid values fall back to the 512 KB default with a one-time warning. When an override is active, `analyze` prints the effective threshold in its startup banner (e.g. `CODRAGRAPH_MAX_FILE_SIZE: effective threshold 2048KB (default 512KB)`).
@@ -86,7 +86,7 @@ function generateCodraGraphContent(projectName, stats, generatedSkills, groupNam
86
86
 
87
87
  This project is indexed by CodraGraph as **${projectName}**${noStats ? '' : ` (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows)`}. Use the CodraGraph MCP tools to understand code, assess impact, and navigate safely.
88
88
 
89
- > If any CodraGraph tool warns the index is stale, run \`npx codragraph analyze\` in terminal first.
89
+ > If any CodraGraph tool warns the index is stale, run \`npx @codragraph/cli analyze\` in terminal first.
90
90
 
91
91
  ## Always Do
92
92
 
@@ -115,7 +115,7 @@ This project is indexed by CodraGraph as **${projectName}**${noStats ? '' : ` ($
115
115
  ${groupNames && groupNames.length > 0
116
116
  ? `## Cross-Repo Groups
117
117
 
118
- This repository is listed under CodraGraph **group(s): ${groupNames.join(', ')}** (see \`~/.codragraph/groups/\`). For cross-repo analysis, use MCP tools \`impact\`, \`query\`, and \`context\` with \`repo\` set to \`@<groupName>\` or \`@<groupName>/<memberPath>\` (paths match keys in that group’s \`group.yaml\`). Use \`group_list\` / \`group_sync\` for membership and sync. From the terminal: \`npx codragraph group list\`, \`npx codragraph group sync <name>\`, \`npx codragraph group impact <name> --target <symbol> --repo <group-path>\`.
118
+ This repository is listed under CodraGraph **group(s): ${groupNames.join(', ')}** (see \`~/.codragraph/groups/\`). For cross-repo analysis, use MCP tools \`impact\`, \`query\`, and \`context\` with \`repo\` set to \`@<groupName>\` or \`@<groupName>/<memberPath>\` (paths match keys in that group’s \`group.yaml\`). Use \`group_list\` / \`group_sync\` for membership and sync. From the terminal: \`npx @codragraph/cli group list\`, \`npx @codragraph/cli group sync <name>\`, \`npx @codragraph/cli group impact <name> --target <symbol> --repo <group-path>\`.
119
119
 
120
120
  `
121
121
  : ''}## CLI
@@ -289,8 +289,8 @@ export const analyzeCommand = async (inputPath, options) => {
289
289
  console.error(' Suggestions:');
290
290
  console.error(' 1. Clear the npm cache: npm cache clean --force');
291
291
  console.error(' 2. Update npm: npm install -g npm@latest');
292
- console.error(' 3. Reinstall codragraph: npm install -g codragraph@latest');
293
- console.error(' 4. Or try npx directly: npx codragraph@latest analyze');
292
+ console.error(' 3. Reinstall codragraph: npm install -g @codragraph/cli@latest');
293
+ console.error(' 4. Or try npx directly: npx @codragraph/cli@latest analyze');
294
294
  console.error('');
295
295
  }
296
296
  else if (msg.includes('MODULE_NOT_FOUND') ||
@@ -298,8 +298,8 @@ export const analyzeCommand = async (inputPath, options) => {
298
298
  msg.includes('ERR_MODULE_NOT_FOUND')) {
299
299
  console.error(' A required module could not be loaded. The installation may be corrupt.');
300
300
  console.error(' Suggestions:');
301
- console.error(' 1. Reinstall: npm install -g codragraph@latest');
302
- console.error(' 2. Clear cache: npm cache clean --force && npx codragraph@latest analyze');
301
+ console.error(' 1. Reinstall: npm install -g @codragraph/cli@latest');
302
+ console.error(' 2. Clear cache: npm cache clean --force && npx @codragraph/cli@latest analyze');
303
303
  console.error('');
304
304
  }
305
305
  process.exitCode = 1;
package/dist/cli/index.js CHANGED
@@ -8,10 +8,7 @@ import { registerGroupCommands } from './group.js';
8
8
  const _require = createRequire(import.meta.url);
9
9
  const pkg = _require('../../package.json');
10
10
  const program = new Command();
11
- program
12
- .name('codragraph')
13
- .description('CodraGraph local CLI and MCP server')
14
- .version(pkg.version);
11
+ program.name('codragraph').description('CodraGraph local CLI and MCP server').version(pkg.version);
15
12
  program
16
13
  .command('setup')
17
14
  .description('One-time setup: configure MCP for Cursor, Claude Code, OpenCode, Codex')
package/dist/cli/setup.js CHANGED
@@ -18,20 +18,38 @@ const __filename = fileURLToPath(import.meta.url);
18
18
  const __dirname = path.dirname(__filename);
19
19
  const execFileAsync = promisify(execFile);
20
20
  /**
21
- * Resolve the absolute path to the `@codragraph/cli` binary if it's installed
21
+ * Resolve the absolute path to the `codragraph` binary if it's installed
22
22
  * globally (or via npm -g / yarn global). Returns null when not found.
23
+ *
24
+ * Note: the npm package is `@codragraph/cli`, but the executable it installs
25
+ * is `codragraph` (see package.json `bin`). PATH lookup must use the bin name.
26
+ *
27
+ * Windows specifics: `where codragraph` returns every PATH entry, in order:
28
+ * the extensionless Unix shim npm creates first (a sh script — Node's
29
+ * spawn/execFile cannot launch it on Windows), then `codragraph.cmd`,
30
+ * then `codragraph.ps1`. We must pick the .cmd / .exe / .bat entry; writing
31
+ * the extensionless path into a downstream MCP config produces a launcher
32
+ * that fails on every spawn.
23
33
  */
24
34
  function resolveCodragraphBin() {
25
35
  try {
26
36
  const cmd = process.platform === 'win32' ? 'where' : 'which';
27
- const resolved = execFileSync(cmd, ['@codragraph/cli'], {
37
+ const stdout = execFileSync(cmd, ['codragraph'], {
28
38
  encoding: 'utf-8',
29
39
  timeout: 5000,
30
40
  stdio: ['ignore', 'pipe', 'ignore'],
31
- })
32
- .split('\n')[0]
33
- .trim();
34
- return resolved || null;
41
+ });
42
+ const lines = stdout
43
+ .split('\n')
44
+ .map((l) => l.trim())
45
+ .filter(Boolean);
46
+ if (process.platform === 'win32') {
47
+ // Prefer a Windows-executable shim. If none is on PATH, return null so
48
+ // the caller falls through to the `cmd /c npx -y @codragraph/cli` path.
49
+ const exe = lines.find((l) => /\.(cmd|exe|bat)$/i.test(l));
50
+ return exe ?? null;
51
+ }
52
+ return lines[0] ?? null;
35
53
  }
36
54
  catch {
37
55
  return null;
@@ -40,28 +58,38 @@ function resolveCodragraphBin() {
40
58
  /**
41
59
  * The MCP server entry for all editors.
42
60
  *
43
- * Prefers the globally-installed `@codragraph/cli` binary (starts in ~1 s) over
44
- * `npx -y codragraph@latest` (cold-cache install of native deps can take
61
+ * Prefers the globally-installed `codragraph` binary (starts in ~1 s) over
62
+ * `npx -y @codragraph/cli@latest` (cold-cache install of native deps can take
45
63
  * >60 s, exceeding Claude Code's 30 s MCP connection timeout).
46
64
  *
47
65
  * Falls back to npx when the binary isn't on PATH — e.g. first-time
48
- * users who ran `npx codragraph analyze` but haven't done `npm i -g`.
66
+ * users who ran `npx @codragraph/cli analyze` but haven't done `npm i -g`.
67
+ *
68
+ * Windows note: even when the bin is on PATH, we launch via `cmd /c codragraph
69
+ * mcp` rather than writing the resolved path. Reason: `where codragraph`
70
+ * returns the extensionless Unix shim before `codragraph.cmd`, and Node's
71
+ * spawn/execFile cannot launch the extensionless shim on Windows. Letting
72
+ * cmd resolve via PATHEXT is the only reliable path that works for npm-,
73
+ * pnpm-, and yarn-installed shims alike.
49
74
  */
50
75
  function getMcpEntry() {
51
76
  const bin = resolveCodragraphBin();
52
77
  if (bin) {
78
+ if (process.platform === 'win32') {
79
+ return { command: 'cmd', args: ['/c', 'codragraph', 'mcp'] };
80
+ }
53
81
  return { command: bin, args: ['mcp'] };
54
82
  }
55
83
  // Fallback: npx (works without a global install, but slow cold-start)
56
84
  if (process.platform === 'win32') {
57
85
  return {
58
86
  command: 'cmd',
59
- args: ['/c', 'npx', '-y', 'codragraph@latest', 'mcp'],
87
+ args: ['/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
60
88
  };
61
89
  }
62
90
  return {
63
91
  command: 'npx',
64
- args: ['-y', 'codragraph@latest', 'mcp'],
92
+ args: ['-y', '@codragraph/cli@latest', 'mcp'],
65
93
  };
66
94
  }
67
95
  /**
@@ -71,12 +99,18 @@ function getMcpEntry() {
71
99
  function getOpenCodeMcpEntry() {
72
100
  const bin = resolveCodragraphBin();
73
101
  if (bin) {
102
+ if (process.platform === 'win32') {
103
+ return { type: 'local', command: ['cmd', '/c', 'codragraph', 'mcp'] };
104
+ }
74
105
  return { type: 'local', command: [bin, 'mcp'] };
75
106
  }
76
107
  if (process.platform === 'win32') {
77
- return { type: 'local', command: ['cmd', '/c', 'npx', '-y', 'codragraph@latest', 'mcp'] };
108
+ return {
109
+ type: 'local',
110
+ command: ['cmd', '/c', 'npx', '-y', '@codragraph/cli@latest', 'mcp'],
111
+ };
78
112
  }
79
- return { type: 'local', command: ['npx', '-y', 'codragraph@latest', 'mcp'] };
113
+ return { type: 'local', command: ['npx', '-y', '@codragraph/cli@latest', 'mcp'] };
80
114
  }
81
115
  /**
82
116
  * Merge codragraph entry into an existing MCP config JSON object.
@@ -239,7 +273,7 @@ async function installClaudeCodeHooks(result) {
239
273
  // Source hooks bundled within the codragraph package (hooks/claude/)
240
274
  const pluginHooksPath = path.join(__dirname, '..', '..', 'hooks', 'claude');
241
275
  // Copy unified hook script to ~/.claude/hooks/codragraph/
242
- const destHooksDir = path.join(claudeDir, 'hooks', '@codragraph/cli');
276
+ const destHooksDir = path.join(claudeDir, 'hooks', 'codragraph');
243
277
  try {
244
278
  await fs.mkdir(destHooksDir, { recursive: true });
245
279
  const src = path.join(pluginHooksPath, 'codragraph-hook.cjs');
@@ -291,7 +325,7 @@ async function setupOpenCode(result) {
291
325
  }
292
326
  const configPath = path.join(opencodeDir, 'opencode.json');
293
327
  try {
294
- const ok = await mergeJsoncFile(configPath, ['mcp', '@codragraph/cli'], getOpenCodeMcpEntry());
328
+ const ok = await mergeJsoncFile(configPath, ['mcp', 'codragraph'], getOpenCodeMcpEntry());
295
329
  if (ok) {
296
330
  result.configured.push('OpenCode');
297
331
  }
@@ -339,9 +373,10 @@ async function setupCodex(result) {
339
373
  }
340
374
  try {
341
375
  const entry = getMcpEntry();
342
- await execFileAsync('codex', ['mcp', 'add', '@codragraph/cli', '--', entry.command, ...entry.args], {
343
- shell: process.platform === 'win32',
344
- });
376
+ // On Windows, npm-installed CLIs ship as .cmd shims that Node's execFile
377
+ // can only invoke when the file extension is explicit (no PATHEXT lookup).
378
+ const codexBin = process.platform === 'win32' ? 'codex.cmd' : 'codex';
379
+ await execFileAsync(codexBin, ['mcp', 'add', 'codragraph', '--', entry.command, ...entry.args]);
345
380
  result.configured.push('Codex');
346
381
  return;
347
382
  }
@@ -103,7 +103,7 @@ export const generateSkillFiles = async (repoPath, projectName, pipelineResult)
103
103
  * @param {string} repoPath - Repository root for path normalization
104
104
  * @returns {CommunityNode[]} Synthetic community nodes built from membership data
105
105
  */
106
- const buildCommunitiesFromMemberships = (memberships, graph, repoPath) => {
106
+ const buildCommunitiesFromMemberships = (memberships, graph, _repoPath) => {
107
107
  // Group memberships by communityId
108
108
  const groups = new Map();
109
109
  for (const m of memberships) {
@@ -285,7 +285,7 @@ export const shouldIgnorePath = (filePath) => {
285
285
  // Ignore hidden files (starting with .)
286
286
  if (fileName.startsWith('.') && fileName !== '.') {
287
287
  // But allow some important config files
288
- const allowedDotFiles = ['.env', '.gitignore']; // Already in IGNORED_FILES, so this is redundant
288
+ const _allowedDotFiles = ['.env', '.gitignore']; // Already in IGNORED_FILES, so this is redundant
289
289
  // Actually, let's NOT ignore all dot files - many are important configs
290
290
  // Just rely on the explicit lists above
291
291
  }
@@ -31,7 +31,6 @@ if (ProtoGrammar) {
31
31
  // test runners (vitest forks) when SyntaxNode isn't fully initialized
32
32
  // yet. Catching that here ensures `PROTO_GRPC_PLUGIN` stays null and
33
33
  // the orchestrator falls back to the manual parser.
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
34
  const _Parser = _require('tree-sitter');
36
35
  // Smoke-test: parse + setLanguage to verify the grammar is
37
36
  // end-to-end compatible with this tree-sitter runtime.
@@ -72,24 +71,14 @@ if (ProtoGrammar) {
72
71
  }
73
72
  }
74
73
  function buildPlugin() {
75
- if (!ProtoGrammar || !PACKAGE_PATTERNS || !SERVICE_PATTERNS)
74
+ if (!ProtoGrammar || !SERVICE_PATTERNS)
76
75
  return null;
77
- const pkgPatterns = PACKAGE_PATTERNS;
78
76
  const svcPatterns = SERVICE_PATTERNS;
79
77
  return {
80
78
  name: 'proto-grpc',
81
79
  language: ProtoGrammar,
82
80
  scan(tree) {
83
81
  const out = [];
84
- // Extract `package` declaration (first match wins).
85
- let pkg = '';
86
- for (const match of runCompiledPatterns(pkgPatterns, tree)) {
87
- const pkgNode = match.captures.pkg;
88
- if (pkgNode) {
89
- pkg = pkgNode.text;
90
- break;
91
- }
92
- }
93
82
  // Extract `service → rpc` pairs. The query returns one match per
94
83
  // (service, rpc) combination thanks to the nested structure.
95
84
  for (const match of runCompiledPatterns(svcPatterns, tree)) {
@@ -616,7 +616,7 @@ importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
616
616
  bufferSize: getTreeSitterBufferSize(file.content.length),
617
617
  });
618
618
  }
619
- catch (parseError) {
619
+ catch (_parseError) {
620
620
  continue;
621
621
  }
622
622
  astCache.set(file.path, tree);
@@ -704,7 +704,7 @@ importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
704
704
  // loop above, so verifyConstructorBindings sees all provider bindings
705
705
  // regardless of file processing order.
706
706
  for (let i = 0; i < prepared.length; i++) {
707
- const { file, language, provider, tree, matches, parentMap, typeEnv } = prepared[i];
707
+ const { file, language, provider, tree: _tree, matches, parentMap, typeEnv } = prepared[i];
708
708
  enclosingFnExtractCache.clear();
709
709
  onProgress?.(i + 1, files.length);
710
710
  if (i % 20 === 0)
@@ -1404,7 +1404,7 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1404
1404
  if (anonRedefMatch) {
1405
1405
  // Check it's truly anonymous: the second capture is not a valid data name
1406
1406
  // followed by more clauses — it's the REDEFINES target directly after level
1407
- const level = parseInt(anonRedefMatch[1], 10);
1407
+ const _level = parseInt(anonRedefMatch[1], 10);
1408
1408
  // Only skip if this is genuinely "NN REDEFINES target" with no name between
1409
1409
  // We detect this by checking the full data item regex does NOT match
1410
1410
  // (because RE_DATA_ITEM expects a name before any clauses)
@@ -65,4 +65,4 @@ export interface JclParseResults {
65
65
  * @param filePath - Path for diagnostics (not used in extraction)
66
66
  * @returns Parsed JCL results
67
67
  */
68
- export declare function parseJcl(content: string, filePath: string): JclParseResults;
68
+ export declare function parseJcl(content: string, _filePath: string): JclParseResults;
@@ -73,7 +73,7 @@ function extractDisp(params) {
73
73
  * @param filePath - Path for diagnostics (not used in extraction)
74
74
  * @returns Parsed JCL results
75
75
  */
76
- export function parseJcl(content, filePath) {
76
+ export function parseJcl(content, _filePath) {
77
77
  const results = {
78
78
  jobs: [],
79
79
  steps: [],
@@ -50,5 +50,5 @@ export declare function isJclFile(filePath: string): boolean;
50
50
  * @param allPathSet - Set of all file paths in the repository
51
51
  * @returns Summary of what was extracted
52
52
  */
53
- export declare const processCobol: (graph: KnowledgeGraph, files: CobolFile[], allPathSet: ReadonlySet<string>) => CobolProcessResult;
53
+ export declare const processCobol: (graph: KnowledgeGraph, files: CobolFile[], _allPathSet: ReadonlySet<string>) => CobolProcessResult;
54
54
  export {};
@@ -47,7 +47,7 @@ function isCopybook(filePath) {
47
47
  * @param allPathSet - Set of all file paths in the repository
48
48
  * @returns Summary of what was extracted
49
49
  */
50
- export const processCobol = (graph, files, allPathSet) => {
50
+ export const processCobol = (graph, files, _allPathSet) => {
51
51
  const result = {
52
52
  programs: 0,
53
53
  paragraphs: 0,
@@ -12,7 +12,7 @@ export function createHeritageExtractor(config) {
12
12
  const callNameSet = actualConfig.callBasedHeritage?.callNames;
13
13
  return {
14
14
  language: actualConfig.language,
15
- extract(captureMap, context) {
15
+ extract(captureMap, _context) {
16
16
  const classNode = captureMap['heritage.class'];
17
17
  if (!classNode)
18
18
  return [];
@@ -151,7 +151,7 @@ export const processHeritage = async (graph, files, astCache, ctx, onProgress) =
151
151
  bufferSize: getTreeSitterBufferSize(file.content.length),
152
152
  });
153
153
  }
154
- catch (parseError) {
154
+ catch (_parseError) {
155
155
  // Skip files that can't be parsed
156
156
  continue;
157
157
  }
@@ -245,7 +245,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
245
245
  bufferSize: getTreeSitterBufferSize(file.content.length),
246
246
  });
247
247
  }
248
- catch (parseError) {
248
+ catch (_parseError) {
249
249
  continue;
250
250
  }
251
251
  wasReparsed = true;
@@ -316,7 +316,7 @@ function parameterTypesMatch(a, b, aParamCount, bParamCount) {
316
316
  */
317
317
  function emitMethodImplementsEdges(graph, parentMap, methodMap, parentEdgeType, ancestorsMap, edgeTypesMap) {
318
318
  let edgeCount = 0;
319
- for (const [classId, parentIds] of parentMap) {
319
+ for (const [classId, _parentIds] of parentMap) {
320
320
  const classNode = graph.getNode(classId);
321
321
  if (!classNode)
322
322
  continue;
@@ -273,7 +273,7 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, sco
273
273
  bufferSize: getTreeSitterBufferSize(parseContent.length),
274
274
  });
275
275
  }
276
- catch (parseError) {
276
+ catch (_parseError) {
277
277
  console.warn(`Skipping unparseable file: ${file.path}`);
278
278
  continue;
279
279
  }
@@ -479,7 +479,7 @@ const inferLiteralType = (node) => {
479
479
  };
480
480
  /** C++: detect constructor type from smart pointer factory calls (make_shared<Dog>()).
481
481
  * Extracts the template type argument as the constructor type for virtual dispatch. */
482
- const detectCppConstructorType = (node, classNames) => {
482
+ const detectCppConstructorType = (node, _classNames) => {
483
483
  // Navigate to the initializer value in the declaration
484
484
  const declarator = node.childForFieldName('declarator');
485
485
  const initDecl = declarator?.type === 'init_declarator' ? declarator : undefined;
@@ -149,7 +149,7 @@ const scanConstructorBinding = (node) => {
149
149
  };
150
150
  const FOR_LOOP_NODE_TYPES = new Set(['for_statement']);
151
151
  /** Python function/method node types that carry a parameters list. */
152
- const PY_FUNCTION_NODE_TYPES = new Set(['function_definition', 'decorated_definition']);
152
+ const _PY_FUNCTION_NODE_TYPES = new Set(['function_definition', 'decorated_definition']);
153
153
  /**
154
154
  * Extract element type from a Python type annotation AST node.
155
155
  * Handles:
@@ -564,16 +564,13 @@ export function extractElementTypeFromString(typeStr, pos = 'last') {
564
564
  const openAngle = typeStr.indexOf('<');
565
565
  const openSquare = typeStr.indexOf('[');
566
566
  let openIdx = -1;
567
- let openChar = '';
568
567
  let closeChar = '';
569
568
  if (openAngle >= 0 && (openSquare < 0 || openAngle < openSquare)) {
570
569
  openIdx = openAngle;
571
- openChar = '<';
572
570
  closeChar = '>';
573
571
  }
574
572
  else if (openSquare >= 0) {
575
573
  openIdx = openSquare;
576
- openChar = '[';
577
574
  closeChar = ']';
578
575
  }
579
576
  if (openIdx < 0)
@@ -322,7 +322,7 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
322
322
  try {
323
323
  await conn.query(copyQuery);
324
324
  }
325
- catch (err) {
325
+ catch (_err) {
326
326
  try {
327
327
  const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
328
328
  await conn.query(retryQuery);
@@ -360,7 +360,7 @@ export const loadGraphToLbug = async (graph, repoPath, storagePath, onProgress)
360
360
  try {
361
361
  await conn.query(copyQuery);
362
362
  }
363
- catch (err) {
363
+ catch (_err) {
364
364
  try {
365
365
  const retryQuery = copyQuery.replace('auto_detect=false)', 'auto_detect=false, IGNORE_ERRORS=true)');
366
366
  await conn.query(retryQuery);
@@ -671,7 +671,7 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
671
671
  await tempConn.query(query);
672
672
  inserted++;
673
673
  }
674
- catch (e) {
674
+ catch (_e) {
675
675
  // Don't console.error here - it corrupts MCP JSON-RPC on stderr
676
676
  failed++;
677
677
  }
@@ -991,7 +991,7 @@ export const deleteNodesForFile = async (filePath, dbPath) => {
991
991
  deletedNodes += count;
992
992
  }
993
993
  }
994
- catch (e) {
994
+ catch (_e) {
995
995
  // Some tables may not support this query, skip
996
996
  }
997
997
  }
@@ -280,29 +280,18 @@ async function doInitLbug(repoId, dbPath) {
280
280
  finally {
281
281
  preWarmActive = false;
282
282
  }
283
- // Load FTS extension once per shared Database.
284
- // Done BEFORE pool registration so no concurrent checkout can grab
285
- // the connection while the async FTS load is in progress.
286
- if (!shared.ftsLoaded) {
287
- try {
288
- await available[0].query('LOAD EXTENSION fts');
289
- shared.ftsLoaded = true;
290
- }
291
- catch {
292
- // Extension may not be installed FTS queries will fail gracefully
293
- }
294
- }
295
- // Load VECTOR extension once per shared Database for semantic search support.
296
- if (!shared.vectorLoaded) {
297
- try {
298
- await available[0].query('INSTALL VECTOR');
299
- await available[0].query('LOAD EXTENSION VECTOR');
300
- shared.vectorLoaded = true;
301
- }
302
- catch {
303
- // VECTOR extension may not be available
304
- }
305
- }
283
+ // Load FTS + VECTOR extensions on EVERY connection in the pool.
284
+ //
285
+ // CRITICAL: LadybugDB's extension state is per-connection on macOS (and
286
+ // possibly other platforms) — loading on `available[0]` does NOT propagate
287
+ // to the other connections. Since checkout pops from the end of the array,
288
+ // queries would otherwise hit unloaded connections and `QUERY_FTS_INDEX`
289
+ // would silently return 0 rows (catch in queryFTSViaExecutor swallows the
290
+ // "extension not loaded" error). That broke every search on macOS.
291
+ //
292
+ // Load on all connections concurrently, but BEFORE the pool is registered
293
+ // so no checkout can race the load.
294
+ await loadExtensionsOnConnections(available, shared);
306
295
  // Register pool entry only after all connections are pre-warmed and FTS is
307
296
  // loaded. Concurrent executeQuery calls see either "not initialized"
308
297
  // (and throw cleanly) or a fully ready pool — never a half-built one.
@@ -317,6 +306,42 @@ async function doInitLbug(repoId, dbPath) {
317
306
  });
318
307
  ensureIdleTimer();
319
308
  }
309
+ /**
310
+ * Load FTS + VECTOR extensions on every connection in the pool.
311
+ * Per-connection load is required because LadybugDB's extension state is
312
+ * not always shared across connections of the same Database (observed on
313
+ * macOS native bindings — silent FTS failures otherwise).
314
+ */
315
+ async function loadExtensionsOnConnections(conns, shared) {
316
+ // FTS: every connection needs `LOAD EXTENSION fts` independently.
317
+ await Promise.all(conns.map(async (c) => {
318
+ try {
319
+ await c.query('LOAD EXTENSION fts');
320
+ }
321
+ catch {
322
+ // already-loaded / not-installed — FTS queries fail gracefully if missing
323
+ }
324
+ }));
325
+ shared.ftsLoaded = true;
326
+ // VECTOR: install once on the Database (idempotent), then LOAD on every
327
+ // connection. INSTALL is a no-op after the first call but we run it on
328
+ // conn[0] to guarantee the package is present before fanning out LOADs.
329
+ try {
330
+ await conns[0].query('INSTALL VECTOR');
331
+ }
332
+ catch {
333
+ /* not available — semantic search will be a no-op */
334
+ }
335
+ await Promise.all(conns.map(async (c) => {
336
+ try {
337
+ await c.query('LOAD EXTENSION VECTOR');
338
+ }
339
+ catch {
340
+ /* not available — semantic search will be a no-op */
341
+ }
342
+ }));
343
+ shared.vectorLoaded = true;
344
+ }
320
345
  /**
321
346
  * Initialize a pool entry from a pre-existing Database object.
322
347
  *
@@ -354,27 +379,9 @@ export async function initLbugWithDb(repoId, existingDb, dbPath) {
354
379
  finally {
355
380
  preWarmActive = false;
356
381
  }
357
- // Load FTS extension if not already loaded on this Database
358
- if (!shared.ftsLoaded) {
359
- try {
360
- await available[0].query('LOAD EXTENSION fts');
361
- shared.ftsLoaded = true;
362
- }
363
- catch {
364
- // Extension may already be loaded or not installed
365
- }
366
- }
367
- // Load VECTOR extension for semantic search support
368
- if (!shared.vectorLoaded) {
369
- try {
370
- await available[0].query('INSTALL VECTOR');
371
- await available[0].query('LOAD EXTENSION VECTOR');
372
- shared.vectorLoaded = true;
373
- }
374
- catch {
375
- // VECTOR extension may not be available
376
- }
377
- }
382
+ // Load FTS + VECTOR on every connection (see loadExtensionsOnConnections
383
+ // for why per-connection load is required on macOS native bindings).
384
+ await loadExtensionsOnConnections(available, shared);
378
385
  pool.set(repoId, {
379
386
  db: existingDb,
380
387
  available,
@@ -10,7 +10,7 @@
10
10
  * small repos / CI runners) at the cost of paying that overhead on the
11
11
  * first `query`/`context` call in a session.
12
12
  */
13
- import { queryFTS, ensureFTSIndex } from '../lbug/lbug-adapter.js';
13
+ import { queryFTS, ensureFTSIndex, executeQuery as executeCoreQuery, } from '../lbug/lbug-adapter.js';
14
14
  /**
15
15
  * FTS schema served by `searchFTSFromLbug`. Centralised so that both the
16
16
  * CLI/pipeline path and the MCP pool path use identical (table, index,
@@ -23,6 +23,13 @@ const FTS_INDEXES = [
23
23
  { table: 'Method', indexName: 'method_fts', properties: ['name', 'content'] },
24
24
  { table: 'Interface', indexName: 'interface_fts', properties: ['name', 'content'] },
25
25
  ];
26
+ const FALLBACK_SCAN_LIMIT = 50_000;
27
+ const BOOLEAN_QUERY_TOKENS = new Set(['and', 'or', 'not']);
28
+ const FALLBACK_FIELD_WEIGHTS = {
29
+ name: 4,
30
+ content: 2,
31
+ description: 1,
32
+ };
26
33
  /**
27
34
  * Per-process cache for the MCP pool path: tracks which `(repoId, table)`
28
35
  * pairs have been ensured. The CLI/pipeline path gets its own cache inside
@@ -122,6 +129,68 @@ async function queryFTSViaExecutor(executor, tableName, indexName, query, limit)
122
129
  return [];
123
130
  }
124
131
  }
132
+ function searchTerms(query) {
133
+ const terms = query
134
+ .toLowerCase()
135
+ .match(/[\p{L}\p{N}_]+/gu)
136
+ ?.filter((term) => term.length > 1 && !BOOLEAN_QUERY_TOKENS.has(term));
137
+ return [...new Set(terms ?? [])];
138
+ }
139
+ function scoreFallbackNode(node, query, properties) {
140
+ const terms = searchTerms(query);
141
+ if (terms.length === 0)
142
+ return 0;
143
+ const phrase = query.trim().toLowerCase();
144
+ let score = 0;
145
+ for (const property of properties) {
146
+ const raw = node[property];
147
+ if (raw === null || raw === undefined)
148
+ continue;
149
+ const value = String(raw).toLowerCase();
150
+ if (!value)
151
+ continue;
152
+ const weight = FALLBACK_FIELD_WEIGHTS[property] ?? 1;
153
+ if (phrase.length > 1 && value.includes(phrase)) {
154
+ score += weight * (terms.length + 1);
155
+ }
156
+ for (const term of terms) {
157
+ if (value.includes(term))
158
+ score += weight;
159
+ }
160
+ }
161
+ return score;
162
+ }
163
+ async function queryFallbackViaExecutor(executor, tableName, properties, query, limit) {
164
+ try {
165
+ const rows = await executor(`
166
+ MATCH (node:${tableName})
167
+ RETURN node
168
+ LIMIT ${FALLBACK_SCAN_LIMIT}
169
+ `);
170
+ return rows
171
+ .map((row) => {
172
+ const node = row.node || row[0] || {};
173
+ return {
174
+ filePath: node.filePath || '',
175
+ score: scoreFallbackNode(node, query, properties),
176
+ nodeId: node.nodeId || node.id || '',
177
+ };
178
+ })
179
+ .filter((result) => result.filePath && result.score > 0)
180
+ .sort((a, b) => b.score - a.score)
181
+ .slice(0, limit);
182
+ }
183
+ catch {
184
+ return [];
185
+ }
186
+ }
187
+ async function fallbackSearchAllTables(executor, query, limit) {
188
+ const results = [];
189
+ for (const { table, properties } of FTS_INDEXES) {
190
+ results.push(await queryFallbackViaExecutor(executor, table, properties, query, limit));
191
+ }
192
+ return results;
193
+ }
125
194
  /**
126
195
  * Search using LadybugDB's built-in FTS (always fresh, reads from disk)
127
196
  *
@@ -134,6 +203,8 @@ async function queryFTSViaExecutor(executor, tableName, indexName, query, limit)
134
203
  * @returns Ranked search results from FTS indexes
135
204
  */
136
205
  export const searchFTSFromLbug = async (query, limit = 20, repoId) => {
206
+ if (!query.trim() || limit <= 0)
207
+ return [];
137
208
  let fileResults, functionResults, classResults, methodResults, interfaceResults;
138
209
  if (repoId) {
139
210
  // Use MCP connection pool via dynamic import
@@ -157,6 +228,15 @@ export const searchFTSFromLbug = async (query, limit = 20, repoId) => {
157
228
  classResults = await queryFTSViaExecutor(executor, 'Class', 'class_fts', query, limit);
158
229
  methodResults = await queryFTSViaExecutor(executor, 'Method', 'method_fts', query, limit);
159
230
  interfaceResults = await queryFTSViaExecutor(executor, 'Interface', 'interface_fts', query, limit);
231
+ if (fileResults.length +
232
+ functionResults.length +
233
+ classResults.length +
234
+ methodResults.length +
235
+ interfaceResults.length ===
236
+ 0) {
237
+ [fileResults, functionResults, classResults, methodResults, interfaceResults] =
238
+ await fallbackSearchAllTables(executor, query, limit);
239
+ }
160
240
  }
161
241
  else {
162
242
  // Use core lbug adapter (CLI / pipeline context) — also sequential for safety.
@@ -169,6 +249,15 @@ export const searchFTSFromLbug = async (query, limit = 20, repoId) => {
169
249
  classResults = await queryFTS('Class', 'class_fts', query, limit, false).catch(() => []);
170
250
  methodResults = await queryFTS('Method', 'method_fts', query, limit, false).catch(() => []);
171
251
  interfaceResults = await queryFTS('Interface', 'interface_fts', query, limit, false).catch(() => []);
252
+ if (fileResults.length +
253
+ functionResults.length +
254
+ classResults.length +
255
+ methodResults.length +
256
+ interfaceResults.length ===
257
+ 0) {
258
+ [fileResults, functionResults, classResults, methodResults, interfaceResults] =
259
+ await fallbackSearchAllTables(executeCoreQuery, query, limit);
260
+ }
172
261
  }
173
262
  // Collect all node scores per filePath to track which nodes actually matched
174
263
  const fileNodeScores = new Map();
@@ -221,7 +221,7 @@ export class WikiGenerator {
221
221
  reportProgress(node.name);
222
222
  return 1;
223
223
  }
224
- catch (err) {
224
+ catch (_err) {
225
225
  this.failedModules.push(node.name);
226
226
  reportProgress(`Failed: ${node.name}`);
227
227
  return 0;
@@ -239,7 +239,7 @@ export class WikiGenerator {
239
239
  pagesGenerated++;
240
240
  reportProgress(node.name);
241
241
  }
242
- catch (err) {
242
+ catch (_err) {
243
243
  this.failedModules.push(node.name);
244
244
  reportProgress(`Failed: ${node.name}`);
245
245
  }
@@ -607,7 +607,7 @@ export class WikiGenerator {
607
607
  this.onProgress('incremental', percent, `${incProcessed}/${affectedNodes.length} — ${node.name}`);
608
608
  return 1;
609
609
  }
610
- catch (err) {
610
+ catch (_err) {
611
611
  this.failedModules.push(node.name);
612
612
  incProcessed++;
613
613
  return 0;
@@ -807,7 +807,7 @@ export class WikiGenerator {
807
807
  let activeConcurrency = this.concurrency;
808
808
  let running = 0;
809
809
  let idx = 0;
810
- return new Promise((resolve, reject) => {
810
+ return new Promise((resolve, _reject) => {
811
811
  const next = () => {
812
812
  while (running < activeConcurrency && idx < items.length) {
813
813
  const item = items[idx++];
@@ -318,7 +318,7 @@ async function getContextResource(backend, repoName) {
318
318
  lines.push(' - cypher: Raw graph queries');
319
319
  lines.push(' - list_repos: Discover all indexed repositories');
320
320
  lines.push('');
321
- lines.push('re_index: Run `npx codragraph analyze` in terminal if data is stale');
321
+ lines.push('re_index: Run `npx @codragraph/cli analyze` in terminal if data is stale');
322
322
  lines.push('');
323
323
  lines.push('resources_available:');
324
324
  lines.push(' - codragraph://repos: All indexed repositories');
@@ -520,7 +520,7 @@ async function getProcessDetailResource(name, backend, repoName) {
520
520
  async function getSetupResource(backend) {
521
521
  const repos = await backend.listRepos();
522
522
  if (repos.length === 0) {
523
- return '# CodraGraph\n\nNo repositories indexed. Run: `npx codragraph analyze` in a repository.';
523
+ return '# CodraGraph\n\nNo repositories indexed. Run: `npx @codragraph/cli analyze` in a repository.';
524
524
  }
525
525
  const sections = [];
526
526
  for (const repo of repos) {
@@ -625,7 +625,6 @@ async function getRecipesResource(backend, repoName, taskFamily) {
625
625
  let result;
626
626
  try {
627
627
  const harnessModuleId = '@codragraph/harness/mcp/handler';
628
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
629
628
  const mod = (await import(/* @vite-ignore */ harnessModuleId));
630
629
  result = await mod.handleHarnessRecipesList({
631
630
  task_family: taskFamily,
@@ -133,8 +133,18 @@ function runCodraGraphCli(cliPath, args, cwd, timeout) {
133
133
  stdio: ['pipe', 'pipe', 'pipe'],
134
134
  });
135
135
  }
136
- // On Windows, invoke npx.cmd directly (no shell needed)
137
- return spawnSync(isWin ? 'npx.cmd' : 'npx', ['-y', '@codragraph/cli', ...args], {
136
+ // npx fallback: on Windows, Node 22's spawn refuses to launch `npx.cmd`
137
+ // directly (returns EINVAL), so route through `cmd /c` and let PATHEXT
138
+ // resolve the shim. POSIX direct-spawn is fine.
139
+ if (isWin) {
140
+ return spawnSync('cmd', ['/c', 'npx', '-y', '@codragraph/cli', ...args], {
141
+ encoding: 'utf-8',
142
+ timeout: timeout + 5000,
143
+ cwd,
144
+ stdio: ['pipe', 'pipe', 'pipe'],
145
+ });
146
+ }
147
+ return spawnSync('npx', ['-y', '@codragraph/cli', ...args], {
138
148
  encoding: 'utf-8',
139
149
  timeout: timeout + 5000,
140
150
  cwd,
@@ -239,7 +249,7 @@ function handlePostToolUse(input) {
239
249
  // If HEAD matches last indexed commit, no reindex needed
240
250
  if (currentHead && currentHead === lastCommit) return;
241
251
 
242
- const analyzeCmd = `npx codragraph analyze${hadEmbeddings ? ' --embeddings' : ''}`;
252
+ const analyzeCmd = `npx @codragraph/cli analyze${hadEmbeddings ? ' --embeddings' : ''}`;
243
253
  sendHookResponse(
244
254
  'PostToolUse',
245
255
  `CodraGraph index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` +
@@ -64,7 +64,12 @@ fi
64
64
 
65
65
  # Run codragraph augment — must be fast (<500ms target)
66
66
  # augment writes to stderr (KuzuDB captures stdout at OS level), so capture stderr and discard stdout
67
- RESULT=$(cd "$CWD" && npx -y codragraph augment "$PATTERN" 2>&1 1>/dev/null)
67
+ # Prefer the global bin if present; fall back to npx (npm package is @codragraph/cli, bin is `codragraph`)
68
+ if command -v codragraph >/dev/null 2>&1; then
69
+ RESULT=$(cd "$CWD" && codragraph augment "$PATTERN" 2>&1 1>/dev/null)
70
+ else
71
+ RESULT=$(cd "$CWD" && npx -y @codragraph/cli augment "$PATTERN" 2>&1 1>/dev/null)
72
+ fi
68
73
 
69
74
  if [ -n "$RESULT" ]; then
70
75
  ESCAPED=$(echo "$RESULT" | jq -Rs .)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codragraph/cli",
3
- "version": "1.6.3",
3
+ "version": "1.6.4",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": {
6
6
  "name": "Anit Chaudhary",
@@ -59,7 +59,7 @@
59
59
  "@huggingface/transformers": "^4.1.0",
60
60
  "@ladybugdb/core": "^0.15.2",
61
61
  "@modelcontextprotocol/sdk": "^1.0.0",
62
- "@codragraph/graphstore": "*",
62
+ "@codragraph/graphstore": "^0.1.1",
63
63
  "@scarf/scarf": "^1.4.0",
64
64
  "cli-progress": "^3.12.0",
65
65
  "commander": "^14.0.3",
@@ -12,7 +12,7 @@ All commands work via `npx` — no global install required.
12
12
  ### analyze — Build or refresh the index
13
13
 
14
14
  ```bash
15
- npx codragraph analyze
15
+ npx @codragraph/cli analyze
16
16
  ```
17
17
 
18
18
  Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.codragraph/`, and generates CLAUDE.md / AGENTS.md context files.
@@ -27,7 +27,7 @@ Run from the project root. This parses all source files, builds the knowledge gr
27
27
  ### status — Check index freshness
28
28
 
29
29
  ```bash
30
- npx codragraph status
30
+ npx @codragraph/cli status
31
31
  ```
32
32
 
33
33
  Shows whether the current repo has a CodraGraph index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed.
@@ -35,7 +35,7 @@ Shows whether the current repo has a CodraGraph index, when it was last updated,
35
35
  ### clean — Delete the index
36
36
 
37
37
  ```bash
38
- npx codragraph clean
38
+ npx @codragraph/cli clean
39
39
  ```
40
40
 
41
41
  Deletes the `.codragraph/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing CodraGraph from a project.
@@ -48,7 +48,7 @@ Deletes the `.codragraph/` directory and unregisters the repo from the global re
48
48
  ### wiki — Generate documentation from the graph
49
49
 
50
50
  ```bash
51
- npx codragraph wiki
51
+ npx @codragraph/cli wiki
52
52
  ```
53
53
 
54
54
  Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.codragraph/config.json` on first use).
@@ -65,7 +65,7 @@ Generates repository documentation from the knowledge graph using an LLM. Requir
65
65
  ### list — Show all indexed repos
66
66
 
67
67
  ```bash
68
- npx codragraph list
68
+ npx @codragraph/cli list
69
69
  ```
70
70
 
71
71
  Lists all repositories registered in `~/.codragraph/registry.json`. The MCP `list_repos` tool provides the same information.
@@ -22,7 +22,7 @@ description: "Use when the user is debugging a bug, tracing an error, or asking
22
22
  4. codragraph_cypher({query: "MATCH path..."}) → Custom traces if needed
23
23
  ```
24
24
 
25
- > If "Index is stale" → run `npx codragraph analyze` in terminal.
25
+ > If "Index is stale" → run `npx @codragraph/cli analyze` in terminal.
26
26
 
27
27
  ## Checklist
28
28
 
@@ -23,7 +23,7 @@ description: "Use when the user asks how code works, wants to understand archite
23
23
  5. READ codragraph://repo/{name}/process/{name} → Trace full execution flow
24
24
  ```
25
25
 
26
- > If step 2 says "Index is stale" → run `npx codragraph analyze` in terminal.
26
+ > If step 2 says "Index is stale" → run `npx @codragraph/cli analyze` in terminal.
27
27
 
28
28
  ## Checklist
29
29
 
@@ -15,7 +15,7 @@ For any task involving code understanding, debugging, impact analysis, or refact
15
15
  2. **Match your task to a skill below** and **read that skill file**
16
16
  3. **Follow the skill's workflow and checklist**
17
17
 
18
- > If step 1 warns the index is stale, run `npx codragraph analyze` in the terminal first.
18
+ > If step 1 warns the index is stale, run `npx @codragraph/cli analyze` in the terminal first.
19
19
 
20
20
  ## Skills
21
21
 
@@ -23,7 +23,7 @@ description: "Use when the user wants to know what will break if they change som
23
23
  4. Assess risk and report to user
24
24
  ```
25
25
 
26
- > If "Index is stale" → run `npx codragraph analyze` in terminal.
26
+ > If "Index is stale" → run `npx @codragraph/cli analyze` in terminal.
27
27
 
28
28
  ## Checklist
29
29
 
@@ -26,7 +26,7 @@ description: "Use when the user wants to review a pull request, understand what
26
26
  6. Summarize findings with risk assessment
27
27
  ```
28
28
 
29
- > If "Index is stale" → run `npx codragraph analyze` in terminal before reviewing.
29
+ > If "Index is stale" → run `npx @codragraph/cli analyze` in terminal before reviewing.
30
30
 
31
31
  ## Checklist
32
32
 
@@ -22,7 +22,7 @@ description: "Use when the user wants to rename, extract, split, move, or restru
22
22
  4. Plan update order: interfaces → implementations → callers → tests
23
23
  ```
24
24
 
25
- > If "Index is stale" → run `npx codragraph analyze` in terminal.
25
+ > If "Index is stale" → run `npx @codragraph/cli analyze` in terminal.
26
26
 
27
27
  ## Checklists
28
28