gitnexus 1.4.0 → 1.4.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.
Files changed (131) hide show
  1. package/README.md +194 -214
  2. package/dist/cli/ai-context.d.ts +1 -2
  3. package/dist/cli/ai-context.js +90 -117
  4. package/dist/cli/analyze.d.ts +0 -2
  5. package/dist/cli/analyze.js +2 -20
  6. package/dist/cli/index.js +25 -17
  7. package/dist/cli/setup.js +19 -17
  8. package/dist/core/augmentation/engine.js +20 -20
  9. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  10. package/dist/core/graph/types.d.ts +2 -5
  11. package/dist/core/ingestion/ast-cache.js +2 -3
  12. package/dist/core/ingestion/call-processor.d.ts +5 -5
  13. package/dist/core/ingestion/call-processor.js +258 -173
  14. package/dist/core/ingestion/cluster-enricher.js +16 -16
  15. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -2
  16. package/dist/core/ingestion/entry-point-scoring.js +22 -81
  17. package/dist/core/ingestion/framework-detection.d.ts +1 -5
  18. package/dist/core/ingestion/framework-detection.js +8 -39
  19. package/dist/core/ingestion/heritage-processor.d.ts +4 -13
  20. package/dist/core/ingestion/heritage-processor.js +28 -92
  21. package/dist/core/ingestion/import-processor.d.ts +19 -17
  22. package/dist/core/ingestion/import-processor.js +695 -170
  23. package/dist/core/ingestion/parsing-processor.d.ts +10 -1
  24. package/dist/core/ingestion/parsing-processor.js +177 -41
  25. package/dist/core/ingestion/pipeline.js +26 -49
  26. package/dist/core/ingestion/process-processor.js +1 -2
  27. package/dist/core/ingestion/symbol-table.d.ts +1 -12
  28. package/dist/core/ingestion/symbol-table.js +12 -19
  29. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  30. package/dist/core/ingestion/tree-sitter-queries.js +485 -590
  31. package/dist/core/ingestion/utils.d.ts +0 -67
  32. package/dist/core/ingestion/utils.js +9 -692
  33. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -20
  34. package/dist/core/ingestion/workers/parse-worker.js +345 -84
  35. package/dist/core/ingestion/workers/worker-pool.js +0 -8
  36. package/dist/core/kuzu/csv-generator.js +3 -19
  37. package/dist/core/kuzu/kuzu-adapter.js +19 -14
  38. package/dist/core/kuzu/schema.d.ts +3 -3
  39. package/dist/core/kuzu/schema.js +288 -303
  40. package/dist/core/search/bm25-index.js +6 -7
  41. package/dist/core/search/hybrid-search.js +3 -3
  42. package/dist/core/wiki/diagrams.d.ts +27 -0
  43. package/dist/core/wiki/diagrams.js +163 -0
  44. package/dist/core/wiki/generator.d.ts +50 -2
  45. package/dist/core/wiki/generator.js +548 -49
  46. package/dist/core/wiki/graph-queries.d.ts +42 -0
  47. package/dist/core/wiki/graph-queries.js +276 -97
  48. package/dist/core/wiki/html-viewer.js +192 -192
  49. package/dist/core/wiki/llm-client.js +73 -11
  50. package/dist/core/wiki/prompts.d.ts +52 -8
  51. package/dist/core/wiki/prompts.js +200 -86
  52. package/dist/mcp/core/kuzu-adapter.d.ts +3 -1
  53. package/dist/mcp/core/kuzu-adapter.js +44 -13
  54. package/dist/mcp/local/local-backend.js +128 -128
  55. package/dist/mcp/resources.js +42 -42
  56. package/dist/mcp/server.js +19 -18
  57. package/dist/mcp/tools.js +104 -103
  58. package/hooks/claude/gitnexus-hook.cjs +155 -238
  59. package/hooks/claude/pre-tool-use.sh +79 -79
  60. package/hooks/claude/session-start.sh +42 -42
  61. package/package.json +96 -96
  62. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  63. package/skills/gitnexus-cli.md +82 -82
  64. package/skills/gitnexus-debugging.md +89 -89
  65. package/skills/gitnexus-exploring.md +78 -78
  66. package/skills/gitnexus-guide.md +64 -64
  67. package/skills/gitnexus-impact-analysis.md +97 -97
  68. package/skills/gitnexus-pr-review.md +163 -163
  69. package/skills/gitnexus-refactoring.md +121 -121
  70. package/vendor/leiden/index.cjs +355 -355
  71. package/vendor/leiden/utils.cjs +392 -392
  72. package/dist/cli/lazy-action.d.ts +0 -6
  73. package/dist/cli/lazy-action.js +0 -18
  74. package/dist/cli/skill-gen.d.ts +0 -26
  75. package/dist/cli/skill-gen.js +0 -549
  76. package/dist/core/ingestion/constants.d.ts +0 -16
  77. package/dist/core/ingestion/constants.js +0 -16
  78. package/dist/core/ingestion/export-detection.d.ts +0 -18
  79. package/dist/core/ingestion/export-detection.js +0 -230
  80. package/dist/core/ingestion/language-config.d.ts +0 -46
  81. package/dist/core/ingestion/language-config.js +0 -167
  82. package/dist/core/ingestion/mro-processor.d.ts +0 -45
  83. package/dist/core/ingestion/mro-processor.js +0 -369
  84. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  85. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  86. package/dist/core/ingestion/resolvers/csharp.d.ts +0 -22
  87. package/dist/core/ingestion/resolvers/csharp.js +0 -109
  88. package/dist/core/ingestion/resolvers/go.d.ts +0 -19
  89. package/dist/core/ingestion/resolvers/go.js +0 -42
  90. package/dist/core/ingestion/resolvers/index.d.ts +0 -16
  91. package/dist/core/ingestion/resolvers/index.js +0 -11
  92. package/dist/core/ingestion/resolvers/jvm.d.ts +0 -23
  93. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  94. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  95. package/dist/core/ingestion/resolvers/php.js +0 -35
  96. package/dist/core/ingestion/resolvers/rust.d.ts +0 -15
  97. package/dist/core/ingestion/resolvers/rust.js +0 -73
  98. package/dist/core/ingestion/resolvers/standard.d.ts +0 -28
  99. package/dist/core/ingestion/resolvers/standard.js +0 -145
  100. package/dist/core/ingestion/resolvers/utils.d.ts +0 -33
  101. package/dist/core/ingestion/resolvers/utils.js +0 -120
  102. package/dist/core/ingestion/symbol-resolver.d.ts +0 -32
  103. package/dist/core/ingestion/symbol-resolver.js +0 -83
  104. package/dist/core/ingestion/type-env.d.ts +0 -27
  105. package/dist/core/ingestion/type-env.js +0 -86
  106. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +0 -2
  107. package/dist/core/ingestion/type-extractors/c-cpp.js +0 -60
  108. package/dist/core/ingestion/type-extractors/csharp.d.ts +0 -2
  109. package/dist/core/ingestion/type-extractors/csharp.js +0 -89
  110. package/dist/core/ingestion/type-extractors/go.d.ts +0 -2
  111. package/dist/core/ingestion/type-extractors/go.js +0 -105
  112. package/dist/core/ingestion/type-extractors/index.d.ts +0 -21
  113. package/dist/core/ingestion/type-extractors/index.js +0 -29
  114. package/dist/core/ingestion/type-extractors/jvm.d.ts +0 -3
  115. package/dist/core/ingestion/type-extractors/jvm.js +0 -121
  116. package/dist/core/ingestion/type-extractors/php.d.ts +0 -2
  117. package/dist/core/ingestion/type-extractors/php.js +0 -31
  118. package/dist/core/ingestion/type-extractors/python.d.ts +0 -2
  119. package/dist/core/ingestion/type-extractors/python.js +0 -41
  120. package/dist/core/ingestion/type-extractors/rust.d.ts +0 -2
  121. package/dist/core/ingestion/type-extractors/rust.js +0 -39
  122. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -17
  123. package/dist/core/ingestion/type-extractors/shared.js +0 -97
  124. package/dist/core/ingestion/type-extractors/swift.d.ts +0 -2
  125. package/dist/core/ingestion/type-extractors/swift.js +0 -43
  126. package/dist/core/ingestion/type-extractors/types.d.ts +0 -14
  127. package/dist/core/ingestion/type-extractors/types.js +0 -1
  128. package/dist/core/ingestion/type-extractors/typescript.d.ts +0 -2
  129. package/dist/core/ingestion/type-extractors/typescript.js +0 -46
  130. package/dist/mcp/compatible-stdio-transport.d.ts +0 -25
  131. package/dist/mcp/compatible-stdio-transport.js +0 -200
@@ -24,111 +24,84 @@ const GITNEXUS_END_MARKER = '<!-- gitnexus:end -->';
24
24
  * - Exact tool commands with parameters — vague directives get ignored
25
25
  * - Self-review checklist — forces model to verify its own work
26
26
  */
27
- function generateGitNexusContent(projectName, stats, generatedSkills) {
28
- const generatedRows = (generatedSkills && generatedSkills.length > 0)
29
- ? generatedSkills.map(s => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/generated/${s.name}/SKILL.md\` |`).join('\n')
30
- : '';
31
- const skillsTable = `| Task | Read this skill file |
32
- |------|---------------------|
33
- | Understand architecture / "How does X work?" | \`.claude/skills/gitnexus/gitnexus-exploring/SKILL.md\` |
34
- | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md\` |
35
- | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus/gitnexus-debugging/SKILL.md\` |
36
- | Rename / extract / split / refactor | \`.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md\` |
37
- | Tools, resources, schema reference | \`.claude/skills/gitnexus/gitnexus-guide/SKILL.md\` |
38
- | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus/gitnexus-cli/SKILL.md\` |${generatedRows ? '\n' + generatedRows : ''}`;
39
- return `${GITNEXUS_START_MARKER}
40
- # GitNexus Code Intelligence
41
-
42
- This project is indexed by GitNexus as **${projectName}** (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
43
-
44
- > If any GitNexus tool warns the index is stale, run \`npx gitnexus analyze\` in terminal first.
45
-
46
- ## Always Do
47
-
48
- - **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run \`gitnexus_impact({target: "symbolName", direction: "upstream"})\` and report the blast radius (direct callers, affected processes, risk level) to the user.
49
- - **MUST run \`gitnexus_detect_changes()\` before committing** to verify your changes only affect expected symbols and execution flows.
50
- - **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
51
- - When exploring unfamiliar code, use \`gitnexus_query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
52
- - When you need full context on a specific symbolcallers, callees, which execution flows it participates in use \`gitnexus_context({name: "symbolName"})\`.
53
-
54
- ## When Debugging
55
-
56
- 1. \`gitnexus_query({query: "<error or symptom>"})\` — find execution flows related to the issue
57
- 2. \`gitnexus_context({name: "<suspect function>"})\` — see all callers, callees, and process participation
58
- 3. \`READ gitnexus://repo/${projectName}/process/{processName}\` trace the full execution flow step by step
59
- 4. For regressions: \`gitnexus_detect_changes({scope: "compare", base_ref: "main"})\` see what your branch changed
60
-
61
- ## When Refactoring
62
-
63
- - **Renaming**: MUST use \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with \`dry_run: false\`.
64
- - **Extracting/Splitting**: MUST run \`gitnexus_context({name: "target"})\` to see all incoming/outgoing refs, then \`gitnexus_impact({target: "target", direction: "upstream"})\` to find all external callers before moving code.
65
- - After any refactor: run \`gitnexus_detect_changes({scope: "all"})\` to verify only expected files changed.
66
-
67
- ## Never Do
68
-
69
- - NEVER edit a function, class, or method without first running \`gitnexus_impact\` on it.
70
- - NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
71
- - NEVER rename symbols with find-and-replace use \`gitnexus_rename\` which understands the call graph.
72
- - NEVER commit changes without running \`gitnexus_detect_changes()\` to check affected scope.
73
-
74
- ## Tools Quick Reference
75
-
76
- | Tool | When to use | Command |
77
- |------|-------------|---------|
78
- | \`query\` | Find code by concept | \`gitnexus_query({query: "auth validation"})\` |
79
- | \`context\` | 360-degree view of one symbol | \`gitnexus_context({name: "validateUser"})\` |
80
- | \`impact\` | Blast radius before editing | \`gitnexus_impact({target: "X", direction: "upstream"})\` |
81
- | \`detect_changes\` | Pre-commit scope check | \`gitnexus_detect_changes({scope: "staged"})\` |
82
- | \`rename\` | Safe multi-file rename | \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` |
83
- | \`cypher\` | Custom graph queries | \`gitnexus_cypher({query: "MATCH ..."})\` |
84
-
85
- ## Impact Risk Levels
86
-
87
- | Depth | Meaning | Action |
88
- |-------|---------|--------|
89
- | d=1 | WILL BREAK — direct callers/importers | MUST update these |
90
- | d=2 | LIKELY AFFECTED — indirect deps | Should test |
91
- | d=3 | MAY NEED TESTING — transitive | Test if critical path |
92
-
93
- ## Resources
94
-
95
- | Resource | Use for |
96
- |----------|---------|
97
- | \`gitnexus://repo/${projectName}/context\` | Codebase overview, check index freshness |
98
- | \`gitnexus://repo/${projectName}/clusters\` | All functional areas |
99
- | \`gitnexus://repo/${projectName}/processes\` | All execution flows |
100
- | \`gitnexus://repo/${projectName}/process/{name}\` | Step-by-step execution trace |
101
-
102
- ## Self-Check Before Finishing
103
-
104
- Before completing any code modification task, verify:
105
- 1. \`gitnexus_impact\` was run for all modified symbols
106
- 2. No HIGH/CRITICAL risk warnings were ignored
107
- 3. \`gitnexus_detect_changes()\` confirms changes match expected scope
108
- 4. All d=1 (WILL BREAK) dependents were updated
109
-
110
- ## Keeping the Index Fresh
111
-
112
- After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
113
-
114
- \`\`\`bash
115
- npx gitnexus analyze
116
- \`\`\`
117
-
118
- If the index previously included embeddings, preserve them by adding \`--embeddings\`:
119
-
120
- \`\`\`bash
121
- npx gitnexus analyze --embeddings
122
- \`\`\`
123
-
124
- To check whether embeddings exist, inspect \`.gitnexus/meta.json\` — the \`stats.embeddings\` field shows the count (0 means no embeddings). **Running analyze without \`--embeddings\` will delete any previously generated embeddings.**
125
-
126
- > Claude Code users: A PostToolUse hook handles this automatically after \`git commit\` and \`git merge\`.
127
-
128
- ## CLI
129
-
130
- ${skillsTable}
131
-
27
+ function generateGitNexusContent(projectName, stats) {
28
+ return `${GITNEXUS_START_MARKER}
29
+ # GitNexus Code Intelligence
30
+
31
+ This project is indexed by GitNexus as **${projectName}** (${stats.nodes || 0} symbols, ${stats.edges || 0} relationships, ${stats.processes || 0} execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
32
+
33
+ > If any GitNexus tool warns the index is stale, run \`npx gitnexus analyze\` in terminal first.
34
+
35
+ ## Always Do
36
+
37
+ - **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run \`gitnexus_impact({target: "symbolName", direction: "upstream"})\` and report the blast radius (direct callers, affected processes, risk level) to the user.
38
+ - **MUST run \`gitnexus_detect_changes()\` before committing** to verify your changes only affect expected symbols and execution flows.
39
+ - **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
40
+ - When exploring unfamiliar code, use \`gitnexus_query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
41
+ - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`gitnexus_context({name: "symbolName"})\`.
42
+
43
+ ## When Debugging
44
+
45
+ 1. \`gitnexus_query({query: "<error or symptom>"})\` — find execution flows related to the issue
46
+ 2. \`gitnexus_context({name: "<suspect function>"})\` — see all callers, callees, and process participation
47
+ 3. \`READ gitnexus://repo/${projectName}/process/{processName}\` — trace the full execution flow step by step
48
+ 4. For regressions: \`gitnexus_detect_changes({scope: "compare", base_ref: "main"})\` see what your branch changed
49
+
50
+ ## When Refactoring
51
+
52
+ - **Renaming**: MUST use \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` first. Review the preview graph edits are safe, text_search edits need manual review. Then run with \`dry_run: false\`.
53
+ - **Extracting/Splitting**: MUST run \`gitnexus_context({name: "target"})\` to see all incoming/outgoing refs, then \`gitnexus_impact({target: "target", direction: "upstream"})\` to find all external callers before moving code.
54
+ - After any refactor: run \`gitnexus_detect_changes({scope: "all"})\` to verify only expected files changed.
55
+
56
+ ## Never Do
57
+
58
+ - NEVER edit a function, class, or method without first running \`gitnexus_impact\` on it.
59
+ - NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
60
+ - NEVER rename symbols with find-and-replace — use \`gitnexus_rename\` which understands the call graph.
61
+ - NEVER commit changes without running \`gitnexus_detect_changes()\` to check affected scope.
62
+
63
+ ## Tools Quick Reference
64
+
65
+ | Tool | When to use | Command |
66
+ |------|-------------|---------|
67
+ | \`query\` | Find code by concept | \`gitnexus_query({query: "auth validation"})\` |
68
+ | \`context\` | 360-degree view of one symbol | \`gitnexus_context({name: "validateUser"})\` |
69
+ | \`impact\` | Blast radius before editing | \`gitnexus_impact({target: "X", direction: "upstream"})\` |
70
+ | \`detect_changes\` | Pre-commit scope check | \`gitnexus_detect_changes({scope: "staged"})\` |
71
+ | \`rename\` | Safe multi-file rename | \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` |
72
+ | \`cypher\` | Custom graph queries | \`gitnexus_cypher({query: "MATCH ..."})\` |
73
+
74
+ ## Impact Risk Levels
75
+
76
+ | Depth | Meaning | Action |
77
+ |-------|---------|--------|
78
+ | d=1 | WILL BREAK direct callers/importers | MUST update these |
79
+ | d=2 | LIKELY AFFECTED indirect deps | Should test |
80
+ | d=3 | MAY NEED TESTING transitive | Test if critical path |
81
+
82
+ ## Resources
83
+
84
+ | Resource | Use for |
85
+ |----------|---------|
86
+ | \`gitnexus://repo/${projectName}/context\` | Codebase overview, check index freshness |
87
+ | \`gitnexus://repo/${projectName}/clusters\` | All functional areas |
88
+ | \`gitnexus://repo/${projectName}/processes\` | All execution flows |
89
+ | \`gitnexus://repo/${projectName}/process/{name}\` | Step-by-step execution trace |
90
+
91
+ ## Self-Check Before Finishing
92
+
93
+ Before completing any code modification task, verify:
94
+ 1. \`gitnexus_impact\` was run for all modified symbols
95
+ 2. No HIGH/CRITICAL risk warnings were ignored
96
+ 3. \`gitnexus_detect_changes()\` confirms changes match expected scope
97
+ 4. All d=1 (WILL BREAK) dependents were updated
98
+
99
+ ## CLI
100
+
101
+ - Re-index: \`npx gitnexus analyze\`
102
+ - Check freshness: \`npx gitnexus status\`
103
+ - Generate docs: \`npx gitnexus wiki\`
104
+
132
105
  ${GITNEXUS_END_MARKER}`;
133
106
  }
134
107
  /**
@@ -220,16 +193,16 @@ async function installSkills(repoPath) {
220
193
  }
221
194
  catch {
222
195
  // Fallback: generate minimal skill content
223
- skillContent = `---
224
- name: ${skill.name}
225
- description: ${skill.description}
226
- ---
227
-
228
- # ${skill.name.charAt(0).toUpperCase() + skill.name.slice(1)}
229
-
230
- ${skill.description}
231
-
232
- Use GitNexus tools to accomplish this task.
196
+ skillContent = `---
197
+ name: ${skill.name}
198
+ description: ${skill.description}
199
+ ---
200
+
201
+ # ${skill.name.charAt(0).toUpperCase() + skill.name.slice(1)}
202
+
203
+ ${skill.description}
204
+
205
+ Use GitNexus tools to accomplish this task.
233
206
  `;
234
207
  }
235
208
  await fs.writeFile(skillPath, skillContent, 'utf-8');
@@ -245,8 +218,8 @@ Use GitNexus tools to accomplish this task.
245
218
  /**
246
219
  * Generate AI context files after indexing
247
220
  */
248
- export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats, generatedSkills) {
249
- const content = generateGitNexusContent(projectName, stats, generatedSkills);
221
+ export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats) {
222
+ const content = generateGitNexusContent(projectName, stats);
250
223
  const createdFiles = [];
251
224
  // Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.)
252
225
  const agentsPath = path.join(repoPath, 'AGENTS.md');
@@ -6,7 +6,5 @@
6
6
  export interface AnalyzeOptions {
7
7
  force?: boolean;
8
8
  embeddings?: boolean;
9
- skills?: boolean;
10
- verbose?: boolean;
11
9
  }
12
10
  export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -16,7 +16,6 @@ import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReuse
16
16
  import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
17
17
  import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
18
18
  import { generateAIContextFiles } from './ai-context.js';
19
- import { generateSkillFiles } from './skill-gen.js';
20
19
  import fs from 'fs/promises';
21
20
  const HEAP_MB = 8192;
22
21
  const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
@@ -59,9 +58,6 @@ const PHASE_LABELS = {
59
58
  export const analyzeCommand = async (inputPath, options) => {
60
59
  if (ensureHeap())
61
60
  return;
62
- if (options?.verbose) {
63
- process.env.GITNEXUS_VERBOSE = '1';
64
- }
65
61
  console.log('\n GitNexus Analyzer\n');
66
62
  let repoPath;
67
63
  if (inputPath) {
@@ -84,7 +80,7 @@ export const analyzeCommand = async (inputPath, options) => {
84
80
  const { storagePath, kuzuPath } = getStoragePaths(repoPath);
85
81
  const currentCommit = getCurrentCommit(repoPath);
86
82
  const existingMeta = await loadMeta(storagePath);
87
- if (existingMeta && !options?.force && !options?.skills && existingMeta.lastCommit === currentCommit) {
83
+ if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
88
84
  console.log(' Already up to date\n');
89
85
  return;
90
86
  }
@@ -246,13 +242,6 @@ export const analyzeCommand = async (inputPath, options) => {
246
242
  }
247
243
  // ── Phase 5: Finalize (98–100%) ───────────────────────────────────
248
244
  updateBar(98, 'Saving metadata...');
249
- // Count embeddings in the index (cached + newly generated)
250
- let embeddingCount = 0;
251
- try {
252
- const embResult = await executeQuery(`MATCH (e:CodeEmbedding) RETURN count(e) AS cnt`);
253
- embeddingCount = embResult?.[0]?.cnt ?? 0;
254
- }
255
- catch { /* table may not exist if embeddings never ran */ }
256
245
  const meta = {
257
246
  repoPath,
258
247
  lastCommit: currentCommit,
@@ -263,7 +252,6 @@ export const analyzeCommand = async (inputPath, options) => {
263
252
  edges: stats.edges,
264
253
  communities: pipelineResult.communityResult?.stats.totalCommunities,
265
254
  processes: pipelineResult.processResult?.stats.totalProcesses,
266
- embeddings: embeddingCount,
267
255
  },
268
256
  };
269
257
  await saveMeta(storagePath, meta);
@@ -279,12 +267,6 @@ export const analyzeCommand = async (inputPath, options) => {
279
267
  }
280
268
  aggregatedClusterCount = Array.from(groups.values()).filter(count => count >= 5).length;
281
269
  }
282
- let generatedSkills = [];
283
- if (options?.skills && pipelineResult.communityResult) {
284
- updateBar(99, 'Generating skill files...');
285
- const skillResult = await generateSkillFiles(repoPath, projectName, pipelineResult);
286
- generatedSkills = skillResult.skills;
287
- }
288
270
  const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
289
271
  files: pipelineResult.totalFileCount,
290
272
  nodes: stats.nodes,
@@ -292,7 +274,7 @@ export const analyzeCommand = async (inputPath, options) => {
292
274
  communities: pipelineResult.communityResult?.stats.totalCommunities,
293
275
  clusters: aggregatedClusterCount,
294
276
  processes: pipelineResult.processResult?.stats.totalProcesses,
295
- }, generatedSkills);
277
+ });
296
278
  await closeKuzu();
297
279
  // Note: we intentionally do NOT call disposeEmbedder() here.
298
280
  // ONNX Runtime's native cleanup segfaults on macOS and some Linux configs.
package/dist/cli/index.js CHANGED
@@ -2,8 +2,18 @@
2
2
  // Heap re-spawn removed — only analyze.ts needs the 8GB heap (via its own ensureHeap()).
3
3
  // Removing it from here improves MCP server startup time significantly.
4
4
  import { Command } from 'commander';
5
+ import { analyzeCommand } from './analyze.js';
6
+ import { serveCommand } from './serve.js';
7
+ import { listCommand } from './list.js';
8
+ import { statusCommand } from './status.js';
9
+ import { mcpCommand } from './mcp.js';
10
+ import { cleanCommand } from './clean.js';
11
+ import { setupCommand } from './setup.js';
12
+ import { augmentCommand } from './augment.js';
13
+ import { wikiCommand } from './wiki.js';
14
+ import { queryCommand, contextCommand, impactCommand, cypherCommand } from './tool.js';
15
+ import { evalServerCommand } from './eval-server.js';
5
16
  import { createRequire } from 'node:module';
6
- import { createLazyAction } from './lazy-action.js';
7
17
  const _require = createRequire(import.meta.url);
8
18
  const pkg = _require('../../package.json');
9
19
  const program = new Command();
@@ -14,39 +24,37 @@ program
14
24
  program
15
25
  .command('setup')
16
26
  .description('One-time setup: configure MCP for Cursor, Claude Code, OpenCode')
17
- .action(createLazyAction(() => import('./setup.js'), 'setupCommand'));
27
+ .action(setupCommand);
18
28
  program
19
29
  .command('analyze [path]')
20
30
  .description('Index a repository (full analysis)')
21
31
  .option('-f, --force', 'Force full re-index even if up to date')
22
32
  .option('--embeddings', 'Enable embedding generation for semantic search (off by default)')
23
- .option('--skills', 'Generate repo-specific skill files from detected communities')
24
- .option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
25
- .action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
33
+ .action(analyzeCommand);
26
34
  program
27
35
  .command('serve')
28
36
  .description('Start local HTTP server for web UI connection')
29
37
  .option('-p, --port <port>', 'Port number', '4747')
30
38
  .option('--host <host>', 'Bind address (default: 127.0.0.1, use 0.0.0.0 for remote access)')
31
- .action(createLazyAction(() => import('./serve.js'), 'serveCommand'));
39
+ .action(serveCommand);
32
40
  program
33
41
  .command('mcp')
34
42
  .description('Start MCP server (stdio) — serves all indexed repos')
35
- .action(createLazyAction(() => import('./mcp.js'), 'mcpCommand'));
43
+ .action(mcpCommand);
36
44
  program
37
45
  .command('list')
38
46
  .description('List all indexed repositories')
39
- .action(createLazyAction(() => import('./list.js'), 'listCommand'));
47
+ .action(listCommand);
40
48
  program
41
49
  .command('status')
42
50
  .description('Show index status for current repo')
43
- .action(createLazyAction(() => import('./status.js'), 'statusCommand'));
51
+ .action(statusCommand);
44
52
  program
45
53
  .command('clean')
46
54
  .description('Delete GitNexus index for current repo')
47
55
  .option('-f, --force', 'Skip confirmation prompt')
48
56
  .option('--all', 'Clean all indexed repos')
49
- .action(createLazyAction(() => import('./clean.js'), 'cleanCommand'));
57
+ .action(cleanCommand);
50
58
  program
51
59
  .command('wiki [path]')
52
60
  .description('Generate repository wiki from knowledge graph')
@@ -56,11 +64,11 @@ program
56
64
  .option('--api-key <key>', 'LLM API key (saved to ~/.gitnexus/config.json)')
57
65
  .option('--concurrency <n>', 'Parallel LLM calls (default: 3)', '3')
58
66
  .option('--gist', 'Publish wiki as a public GitHub Gist after generation')
59
- .action(createLazyAction(() => import('./wiki.js'), 'wikiCommand'));
67
+ .action(wikiCommand);
60
68
  program
61
69
  .command('augment <pattern>')
62
70
  .description('Augment a search pattern with knowledge graph context (used by hooks)')
63
- .action(createLazyAction(() => import('./augment.js'), 'augmentCommand'));
71
+ .action(augmentCommand);
64
72
  // ─── Direct Tool Commands (no MCP overhead) ────────────────────────
65
73
  // These invoke LocalBackend directly for use in eval, scripts, and CI.
66
74
  program
@@ -71,7 +79,7 @@ program
71
79
  .option('-g, --goal <text>', 'What you want to find')
72
80
  .option('-l, --limit <n>', 'Max processes to return (default: 5)')
73
81
  .option('--content', 'Include full symbol source code')
74
- .action(createLazyAction(() => import('./tool.js'), 'queryCommand'));
82
+ .action(queryCommand);
75
83
  program
76
84
  .command('context [name]')
77
85
  .description('360-degree view of a code symbol: callers, callees, processes')
@@ -79,7 +87,7 @@ program
79
87
  .option('-u, --uid <uid>', 'Direct symbol UID (zero-ambiguity lookup)')
80
88
  .option('-f, --file <path>', 'File path to disambiguate common names')
81
89
  .option('--content', 'Include full symbol source code')
82
- .action(createLazyAction(() => import('./tool.js'), 'contextCommand'));
90
+ .action(contextCommand);
83
91
  program
84
92
  .command('impact <target>')
85
93
  .description('Blast radius analysis: what breaks if you change a symbol')
@@ -87,17 +95,17 @@ program
87
95
  .option('-r, --repo <name>', 'Target repository')
88
96
  .option('--depth <n>', 'Max relationship depth (default: 3)')
89
97
  .option('--include-tests', 'Include test files in results')
90
- .action(createLazyAction(() => import('./tool.js'), 'impactCommand'));
98
+ .action(impactCommand);
91
99
  program
92
100
  .command('cypher <query>')
93
101
  .description('Execute raw Cypher query against the knowledge graph')
94
102
  .option('-r, --repo <name>', 'Target repository')
95
- .action(createLazyAction(() => import('./tool.js'), 'cypherCommand'));
103
+ .action(cypherCommand);
96
104
  // ─── Eval Server (persistent daemon for SWE-bench) ─────────────────
97
105
  program
98
106
  .command('eval-server')
99
107
  .description('Start lightweight HTTP server for fast tool calls during evaluation')
100
108
  .option('-p, --port <port>', 'Port number', '4848')
101
109
  .option('--idle-timeout <seconds>', 'Auto-shutdown after N seconds idle (0 = disabled)', '0')
102
- .action(createLazyAction(() => import('./eval-server.js'), 'evalServerCommand'));
110
+ .action(evalServerCommand);
103
111
  program.parse(process.argv);
package/dist/cli/setup.js CHANGED
@@ -147,34 +147,36 @@ async function installClaudeCodeHooks(result) {
147
147
  // even when it's no longer inside the npm package tree
148
148
  const resolvedCli = path.join(__dirname, '..', 'cli', 'index.js');
149
149
  const normalizedCli = path.resolve(resolvedCli).replace(/\\/g, '/');
150
- const jsonCli = JSON.stringify(normalizedCli);
151
- content = content.replace("let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');", `let cliPath = ${jsonCli};`);
150
+ content = content.replace("let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');", `let cliPath = '${normalizedCli}';`);
152
151
  await fs.writeFile(dest, content, 'utf-8');
153
152
  }
154
153
  catch {
155
154
  // Script not found in source — skip
156
155
  }
157
- const hookPath = path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/');
158
- const hookCmd = `node "${hookPath.replace(/"/g, '\\"')}"`;
156
+ const hookCmd = `node "${path.join(destHooksDir, 'gitnexus-hook.cjs').replace(/\\/g, '/')}"`;
159
157
  // Merge hook config into ~/.claude/settings.json
160
158
  const existing = await readJsonFile(settingsPath) || {};
161
159
  if (!existing.hooks)
162
160
  existing.hooks = {};
163
- function ensureHookEntry(eventName, matcher, timeout, statusMessage) {
164
- if (!existing.hooks[eventName])
165
- existing.hooks[eventName] = [];
166
- const hasHook = existing.hooks[eventName].some((h) => h.hooks?.some(hh => hh.command?.includes('gitnexus-hook')));
167
- if (!hasHook) {
168
- existing.hooks[eventName].push({
169
- matcher,
170
- hooks: [{ type: 'command', command: hookCmd, timeout, statusMessage }],
171
- });
172
- }
161
+ // NOTE: SessionStart hooks are broken on Windows (Claude Code bug #23576).
162
+ // Session context is delivered via CLAUDE.md / skills instead.
163
+ // Add PreToolUse hook if not already present
164
+ if (!existing.hooks.PreToolUse)
165
+ existing.hooks.PreToolUse = [];
166
+ const hasPreToolHook = existing.hooks.PreToolUse.some((h) => h.hooks?.some((hh) => hh.command?.includes('gitnexus')));
167
+ if (!hasPreToolHook) {
168
+ existing.hooks.PreToolUse.push({
169
+ matcher: 'Grep|Glob|Bash',
170
+ hooks: [{
171
+ type: 'command',
172
+ command: hookCmd,
173
+ timeout: 8000,
174
+ statusMessage: 'Enriching with GitNexus graph context...',
175
+ }],
176
+ });
173
177
  }
174
- ensureHookEntry('PreToolUse', 'Grep|Glob|Bash', 10, 'Enriching with GitNexus graph context...');
175
- ensureHookEntry('PostToolUse', 'Bash', 10, 'Checking GitNexus index freshness...');
176
178
  await writeJsonFile(settingsPath, existing);
177
- result.configured.push('Claude Code hooks (PreToolUse, PostToolUse)');
179
+ result.configured.push('Claude Code hooks (PreToolUse)');
178
180
  }
179
181
  catch (err) {
180
182
  result.errors.push(`Claude Code hooks: ${err.message}`);
@@ -98,11 +98,11 @@ export async function augment(pattern, cwd) {
98
98
  for (const result of bm25Results.slice(0, 5)) {
99
99
  const escaped = result.filePath.replace(/'/g, "''");
100
100
  try {
101
- const symbols = await executeQuery(repoId, `
102
- MATCH (n) WHERE n.filePath = '${escaped}'
103
- AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}'
104
- RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
105
- LIMIT 3
101
+ const symbols = await executeQuery(repoId, `
102
+ MATCH (n) WHERE n.filePath = '${escaped}'
103
+ AND n.name CONTAINS '${pattern.replace(/'/g, "''").split(/\s+/)[0]}'
104
+ RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath
105
+ LIMIT 3
106
106
  `);
107
107
  for (const sym of symbols) {
108
108
  symbolMatches.push({
@@ -130,10 +130,10 @@ export async function augment(pattern, cwd) {
130
130
  // Callers
131
131
  let callers = [];
132
132
  try {
133
- const rows = await executeQuery(repoId, `
134
- MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${escaped}'})
135
- RETURN caller.name AS name
136
- LIMIT 3
133
+ const rows = await executeQuery(repoId, `
134
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(n {id: '${escaped}'})
135
+ RETURN caller.name AS name
136
+ LIMIT 3
137
137
  `);
138
138
  callers = rows.map((r) => r.name || r[0]).filter(Boolean);
139
139
  }
@@ -141,10 +141,10 @@ export async function augment(pattern, cwd) {
141
141
  // Callees
142
142
  let callees = [];
143
143
  try {
144
- const rows = await executeQuery(repoId, `
145
- MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
146
- RETURN callee.name AS name
147
- LIMIT 3
144
+ const rows = await executeQuery(repoId, `
145
+ MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'CALLS'}]->(callee)
146
+ RETURN callee.name AS name
147
+ LIMIT 3
148
148
  `);
149
149
  callees = rows.map((r) => r.name || r[0]).filter(Boolean);
150
150
  }
@@ -152,9 +152,9 @@ export async function augment(pattern, cwd) {
152
152
  // Processes
153
153
  let processes = [];
154
154
  try {
155
- const rows = await executeQuery(repoId, `
156
- MATCH (n {id: '${escaped}'})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
157
- RETURN p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
155
+ const rows = await executeQuery(repoId, `
156
+ MATCH (n {id: '${escaped}'})-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
157
+ RETURN p.heuristicLabel AS label, r.step AS step, p.stepCount AS stepCount
158
158
  `);
159
159
  processes = rows.map((r) => {
160
160
  const label = r.label || r[0];
@@ -167,10 +167,10 @@ export async function augment(pattern, cwd) {
167
167
  // Cluster cohesion (internal ranking signal)
168
168
  let cohesion = 0;
169
169
  try {
170
- const rows = await executeQuery(repoId, `
171
- MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
172
- RETURN c.cohesion AS cohesion
173
- LIMIT 1
170
+ const rows = await executeQuery(repoId, `
171
+ MATCH (n {id: '${escaped}'})-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
172
+ RETURN c.cohesion AS cohesion
173
+ LIMIT 1
174
174
  `);
175
175
  if (rows.length > 0) {
176
176
  cohesion = (rows[0].cohesion ?? rows[0][0]) || 0;