gitnexus 1.4.1 → 1.4.6
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 +215 -194
- package/dist/cli/ai-context.d.ts +2 -1
- package/dist/cli/ai-context.js +117 -90
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +57 -30
- package/dist/cli/augment.js +1 -1
- package/dist/cli/eval-server.d.ts +1 -1
- package/dist/cli/eval-server.js +14 -6
- package/dist/cli/index.js +18 -25
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/setup.js +42 -32
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/cli/status.js +13 -4
- package/dist/cli/tool.d.ts +3 -2
- package/dist/cli/tool.js +48 -13
- package/dist/cli/wiki.js +2 -2
- package/dist/config/ignore-service.d.ts +25 -0
- package/dist/config/ignore-service.js +76 -0
- package/dist/config/supported-languages.d.ts +1 -0
- package/dist/config/supported-languages.js +1 -1
- package/dist/core/augmentation/engine.js +99 -72
- package/dist/core/embeddings/embedder.d.ts +1 -1
- package/dist/core/embeddings/embedder.js +1 -1
- package/dist/core/embeddings/embedding-pipeline.d.ts +3 -3
- package/dist/core/embeddings/embedding-pipeline.js +74 -47
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/types.d.ts +5 -2
- package/dist/core/ingestion/ast-cache.js +3 -2
- package/dist/core/ingestion/call-processor.d.ts +5 -7
- package/dist/core/ingestion/call-processor.js +430 -283
- package/dist/core/ingestion/call-routing.d.ts +53 -0
- package/dist/core/ingestion/call-routing.js +108 -0
- package/dist/core/ingestion/cluster-enricher.js +16 -16
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
- package/dist/core/ingestion/entry-point-scoring.js +94 -24
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +231 -0
- package/dist/core/ingestion/filesystem-walker.js +4 -3
- package/dist/core/ingestion/framework-detection.d.ts +5 -1
- package/dist/core/ingestion/framework-detection.js +48 -8
- package/dist/core/ingestion/heritage-processor.d.ts +13 -5
- package/dist/core/ingestion/heritage-processor.js +109 -55
- package/dist/core/ingestion/import-processor.d.ts +16 -20
- package/dist/core/ingestion/import-processor.js +202 -696
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +3 -11
- package/dist/core/ingestion/parsing-processor.js +85 -181
- package/dist/core/ingestion/pipeline.d.ts +5 -1
- package/dist/core/ingestion/pipeline.js +192 -116
- package/dist/core/ingestion/process-processor.js +2 -1
- package/dist/core/ingestion/resolution-context.d.ts +53 -0
- package/dist/core/ingestion/resolution-context.js +132 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +18 -0
- package/dist/core/ingestion/resolvers/index.js +13 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/python.d.ts +19 -0
- package/dist/core/ingestion/resolvers/python.js +52 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
- package/dist/core/ingestion/resolvers/ruby.js +15 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +123 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +122 -0
- package/dist/core/ingestion/symbol-table.d.ts +21 -1
- package/dist/core/ingestion/symbol-table.js +40 -12
- package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -11
- package/dist/core/ingestion/tree-sitter-queries.js +642 -485
- package/dist/core/ingestion/type-env.d.ts +49 -0
- package/dist/core/ingestion/type-env.js +611 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +383 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +467 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
- package/dist/core/ingestion/type-extractors/index.js +31 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +681 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +549 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +406 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +389 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +449 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +133 -0
- package/dist/core/ingestion/type-extractors/shared.js +703 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +137 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
- package/dist/core/ingestion/type-extractors/types.js +1 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +98 -0
- package/dist/core/ingestion/utils.js +1064 -9
- package/dist/core/ingestion/workers/parse-worker.d.ts +38 -4
- package/dist/core/ingestion/workers/parse-worker.js +251 -359
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/{kuzu → lbug}/csv-generator.d.ts +1 -1
- package/dist/core/{kuzu → lbug}/csv-generator.js +20 -4
- package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +19 -19
- package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +82 -82
- package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
- package/dist/core/{kuzu → lbug}/schema.js +304 -289
- package/dist/core/search/bm25-index.d.ts +4 -4
- package/dist/core/search/bm25-index.js +17 -16
- package/dist/core/search/hybrid-search.d.ts +2 -2
- package/dist/core/search/hybrid-search.js +9 -9
- package/dist/core/tree-sitter/parser-loader.js +9 -2
- package/dist/core/wiki/generator.d.ts +4 -52
- package/dist/core/wiki/generator.js +53 -552
- package/dist/core/wiki/graph-queries.d.ts +4 -46
- package/dist/core/wiki/graph-queries.js +103 -282
- package/dist/core/wiki/html-viewer.js +192 -192
- package/dist/core/wiki/llm-client.js +11 -73
- package/dist/core/wiki/prompts.d.ts +8 -52
- package/dist/core/wiki/prompts.js +86 -200
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +200 -0
- package/dist/mcp/core/{kuzu-adapter.d.ts → lbug-adapter.d.ts} +7 -9
- package/dist/mcp/core/{kuzu-adapter.js → lbug-adapter.js} +77 -79
- package/dist/mcp/local/local-backend.d.ts +7 -6
- package/dist/mcp/local/local-backend.js +176 -147
- package/dist/mcp/resources.js +42 -42
- package/dist/mcp/server.js +18 -19
- package/dist/mcp/tools.js +103 -104
- package/dist/server/api.js +12 -12
- package/dist/server/mcp-http.d.ts +1 -1
- package/dist/server/mcp-http.js +1 -1
- package/dist/storage/repo-manager.d.ts +20 -2
- package/dist/storage/repo-manager.js +55 -1
- package/dist/types/pipeline.d.ts +1 -1
- package/hooks/claude/gitnexus-hook.cjs +238 -155
- package/hooks/claude/pre-tool-use.sh +79 -79
- package/hooks/claude/session-start.sh +42 -42
- package/package.json +99 -96
- package/scripts/patch-tree-sitter-swift.cjs +74 -74
- package/skills/gitnexus-cli.md +82 -82
- package/skills/gitnexus-debugging.md +89 -89
- package/skills/gitnexus-exploring.md +78 -78
- package/skills/gitnexus-guide.md +64 -64
- package/skills/gitnexus-impact-analysis.md +97 -97
- package/skills/gitnexus-pr-review.md +163 -163
- package/skills/gitnexus-refactoring.md +121 -121
- package/vendor/leiden/index.cjs +355 -355
- package/vendor/leiden/utils.cjs +392 -392
- package/dist/core/wiki/diagrams.d.ts +0 -27
- package/dist/core/wiki/diagrams.js +0 -163
package/dist/cli/ai-context.js
CHANGED
|
@@ -24,84 +24,111 @@ 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) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
|
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
|
88
|
-
|
|
89
|
-
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
|
|
104
|
-
|
|
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 symbol — callers, 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
|
+
|
|
105
132
|
${GITNEXUS_END_MARKER}`;
|
|
106
133
|
}
|
|
107
134
|
/**
|
|
@@ -193,16 +220,16 @@ async function installSkills(repoPath) {
|
|
|
193
220
|
}
|
|
194
221
|
catch {
|
|
195
222
|
// Fallback: generate minimal skill content
|
|
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.
|
|
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.
|
|
206
233
|
`;
|
|
207
234
|
}
|
|
208
235
|
await fs.writeFile(skillPath, skillContent, 'utf-8');
|
|
@@ -218,8 +245,8 @@ Use GitNexus tools to accomplish this task.
|
|
|
218
245
|
/**
|
|
219
246
|
* Generate AI context files after indexing
|
|
220
247
|
*/
|
|
221
|
-
export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats) {
|
|
222
|
-
const content = generateGitNexusContent(projectName, stats);
|
|
248
|
+
export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats, generatedSkills) {
|
|
249
|
+
const content = generateGitNexusContent(projectName, stats, generatedSkills);
|
|
223
250
|
const createdFiles = [];
|
|
224
251
|
// Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.)
|
|
225
252
|
const agentsPath = path.join(repoPath, 'AGENTS.md');
|
package/dist/cli/analyze.d.ts
CHANGED
package/dist/cli/analyze.js
CHANGED
|
@@ -8,14 +8,15 @@ import { execFileSync } from 'child_process';
|
|
|
8
8
|
import v8 from 'v8';
|
|
9
9
|
import cliProgress from 'cli-progress';
|
|
10
10
|
import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
|
|
11
|
-
import {
|
|
11
|
+
import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, createFTSIndex, loadCachedEmbeddings } from '../core/lbug/lbug-adapter.js';
|
|
12
12
|
// Embedding imports are lazy (dynamic import) so onnxruntime-node is never
|
|
13
13
|
// loaded when embeddings are not requested. This avoids crashes on Node
|
|
14
14
|
// versions whose ABI is not yet supported by the native binary (#89).
|
|
15
15
|
// disposeEmbedder intentionally not called — ONNX Runtime segfaults on cleanup (see #38)
|
|
16
|
-
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath } from '../storage/repo-manager.js';
|
|
16
|
+
import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, getGlobalRegistryPath, cleanupOldKuzuFiles } 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';
|
|
19
20
|
import fs from 'fs/promises';
|
|
20
21
|
const HEAP_MB = 8192;
|
|
21
22
|
const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
|
|
@@ -50,7 +51,7 @@ const PHASE_LABELS = {
|
|
|
50
51
|
communities: 'Detecting communities',
|
|
51
52
|
processes: 'Detecting processes',
|
|
52
53
|
complete: 'Pipeline complete',
|
|
53
|
-
|
|
54
|
+
lbug: 'Loading into LadybugDB',
|
|
54
55
|
fts: 'Creating search indexes',
|
|
55
56
|
embeddings: 'Generating embeddings',
|
|
56
57
|
done: 'Done',
|
|
@@ -58,6 +59,9 @@ const PHASE_LABELS = {
|
|
|
58
59
|
export const analyzeCommand = async (inputPath, options) => {
|
|
59
60
|
if (ensureHeap())
|
|
60
61
|
return;
|
|
62
|
+
if (options?.verbose) {
|
|
63
|
+
process.env.GITNEXUS_VERBOSE = '1';
|
|
64
|
+
}
|
|
61
65
|
console.log('\n GitNexus Analyzer\n');
|
|
62
66
|
let repoPath;
|
|
63
67
|
if (inputPath) {
|
|
@@ -77,13 +81,22 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
77
81
|
process.exitCode = 1;
|
|
78
82
|
return;
|
|
79
83
|
}
|
|
80
|
-
const { storagePath,
|
|
84
|
+
const { storagePath, lbugPath } = getStoragePaths(repoPath);
|
|
85
|
+
// Clean up stale KuzuDB files from before the LadybugDB migration.
|
|
86
|
+
// If kuzu existed but lbug doesn't, we're doing a migration re-index — say so.
|
|
87
|
+
const kuzuResult = await cleanupOldKuzuFiles(storagePath);
|
|
88
|
+
if (kuzuResult.found && kuzuResult.needsReindex) {
|
|
89
|
+
console.log(' Migrating from KuzuDB to LadybugDB — rebuilding index...\n');
|
|
90
|
+
}
|
|
81
91
|
const currentCommit = getCurrentCommit(repoPath);
|
|
82
92
|
const existingMeta = await loadMeta(storagePath);
|
|
83
|
-
if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
|
|
93
|
+
if (existingMeta && !options?.force && !options?.skills && existingMeta.lastCommit === currentCommit) {
|
|
84
94
|
console.log(' Already up to date\n');
|
|
85
95
|
return;
|
|
86
96
|
}
|
|
97
|
+
if (process.env.GITNEXUS_NO_GITIGNORE) {
|
|
98
|
+
console.log(' GITNEXUS_NO_GITIGNORE is set — skipping .gitignore (still reading .gitnexusignore)\n');
|
|
99
|
+
}
|
|
87
100
|
// Single progress bar for entire pipeline
|
|
88
101
|
const bar = new cliProgress.SingleBar({
|
|
89
102
|
format: ' {bar} {percentage}% | {phase}',
|
|
@@ -104,7 +117,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
104
117
|
aborted = true;
|
|
105
118
|
bar.stop();
|
|
106
119
|
console.log('\n Interrupted — cleaning up...');
|
|
107
|
-
|
|
120
|
+
closeLbug().catch(() => { }).finally(() => process.exit(130));
|
|
108
121
|
};
|
|
109
122
|
process.on('SIGINT', sigintHandler);
|
|
110
123
|
// Route all console output through bar.log() so the bar doesn't stamp itself
|
|
@@ -150,15 +163,15 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
150
163
|
if (options?.embeddings && existingMeta && !options?.force) {
|
|
151
164
|
try {
|
|
152
165
|
updateBar(0, 'Caching embeddings...');
|
|
153
|
-
await
|
|
166
|
+
await initLbug(lbugPath);
|
|
154
167
|
const cached = await loadCachedEmbeddings();
|
|
155
168
|
cachedEmbeddingNodeIds = cached.embeddingNodeIds;
|
|
156
169
|
cachedEmbeddings = cached.embeddings;
|
|
157
|
-
await
|
|
170
|
+
await closeLbug();
|
|
158
171
|
}
|
|
159
172
|
catch {
|
|
160
173
|
try {
|
|
161
|
-
await
|
|
174
|
+
await closeLbug();
|
|
162
175
|
}
|
|
163
176
|
catch { }
|
|
164
177
|
}
|
|
@@ -169,26 +182,26 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
169
182
|
const scaled = Math.round(progress.percent * 0.6);
|
|
170
183
|
updateBar(scaled, phaseLabel);
|
|
171
184
|
});
|
|
172
|
-
// ── Phase 2:
|
|
173
|
-
updateBar(60, 'Loading into
|
|
174
|
-
await
|
|
175
|
-
const
|
|
176
|
-
for (const f of
|
|
185
|
+
// ── Phase 2: LadybugDB (60–85%) ──────────────────────────────────────
|
|
186
|
+
updateBar(60, 'Loading into LadybugDB...');
|
|
187
|
+
await closeLbug();
|
|
188
|
+
const lbugFiles = [lbugPath, `${lbugPath}.wal`, `${lbugPath}.lock`];
|
|
189
|
+
for (const f of lbugFiles) {
|
|
177
190
|
try {
|
|
178
191
|
await fs.rm(f, { recursive: true, force: true });
|
|
179
192
|
}
|
|
180
193
|
catch { }
|
|
181
194
|
}
|
|
182
|
-
const
|
|
183
|
-
await
|
|
184
|
-
let
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const progress = Math.min(84, 60 + Math.round((
|
|
195
|
+
const t0Lbug = Date.now();
|
|
196
|
+
await initLbug(lbugPath);
|
|
197
|
+
let lbugMsgCount = 0;
|
|
198
|
+
const lbugResult = await loadGraphToLbug(pipelineResult.graph, pipelineResult.repoPath, storagePath, (msg) => {
|
|
199
|
+
lbugMsgCount++;
|
|
200
|
+
const progress = Math.min(84, 60 + Math.round((lbugMsgCount / (lbugMsgCount + 10)) * 24));
|
|
188
201
|
updateBar(progress, msg);
|
|
189
202
|
});
|
|
190
|
-
const
|
|
191
|
-
const
|
|
203
|
+
const lbugTime = ((Date.now() - t0Lbug) / 1000).toFixed(1);
|
|
204
|
+
const lbugWarnings = lbugResult.warnings;
|
|
192
205
|
// ── Phase 3: FTS (85–90%) ─────────────────────────────────────────
|
|
193
206
|
updateBar(85, 'Creating search indexes...');
|
|
194
207
|
const t0Fts = Date.now();
|
|
@@ -217,7 +230,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
// ── Phase 4: Embeddings (90–98%) ──────────────────────────────────
|
|
220
|
-
const stats = await
|
|
233
|
+
const stats = await getLbugStats();
|
|
221
234
|
let embeddingTime = '0.0';
|
|
222
235
|
let embeddingSkipped = true;
|
|
223
236
|
let embeddingSkipReason = 'off (use --embeddings to enable)';
|
|
@@ -242,6 +255,13 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
242
255
|
}
|
|
243
256
|
// ── Phase 5: Finalize (98–100%) ───────────────────────────────────
|
|
244
257
|
updateBar(98, 'Saving metadata...');
|
|
258
|
+
// Count embeddings in the index (cached + newly generated)
|
|
259
|
+
let embeddingCount = 0;
|
|
260
|
+
try {
|
|
261
|
+
const embResult = await executeQuery(`MATCH (e:CodeEmbedding) RETURN count(e) AS cnt`);
|
|
262
|
+
embeddingCount = embResult?.[0]?.cnt ?? 0;
|
|
263
|
+
}
|
|
264
|
+
catch { /* table may not exist if embeddings never ran */ }
|
|
245
265
|
const meta = {
|
|
246
266
|
repoPath,
|
|
247
267
|
lastCommit: currentCommit,
|
|
@@ -252,6 +272,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
252
272
|
edges: stats.edges,
|
|
253
273
|
communities: pipelineResult.communityResult?.stats.totalCommunities,
|
|
254
274
|
processes: pipelineResult.processResult?.stats.totalProcesses,
|
|
275
|
+
embeddings: embeddingCount,
|
|
255
276
|
},
|
|
256
277
|
};
|
|
257
278
|
await saveMeta(storagePath, meta);
|
|
@@ -267,6 +288,12 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
267
288
|
}
|
|
268
289
|
aggregatedClusterCount = Array.from(groups.values()).filter(count => count >= 5).length;
|
|
269
290
|
}
|
|
291
|
+
let generatedSkills = [];
|
|
292
|
+
if (options?.skills && pipelineResult.communityResult) {
|
|
293
|
+
updateBar(99, 'Generating skill files...');
|
|
294
|
+
const skillResult = await generateSkillFiles(repoPath, projectName, pipelineResult);
|
|
295
|
+
generatedSkills = skillResult.skills;
|
|
296
|
+
}
|
|
270
297
|
const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
|
|
271
298
|
files: pipelineResult.totalFileCount,
|
|
272
299
|
nodes: stats.nodes,
|
|
@@ -274,8 +301,8 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
274
301
|
communities: pipelineResult.communityResult?.stats.totalCommunities,
|
|
275
302
|
clusters: aggregatedClusterCount,
|
|
276
303
|
processes: pipelineResult.processResult?.stats.totalProcesses,
|
|
277
|
-
});
|
|
278
|
-
await
|
|
304
|
+
}, generatedSkills);
|
|
305
|
+
await closeLbug();
|
|
279
306
|
// Note: we intentionally do NOT call disposeEmbedder() here.
|
|
280
307
|
// ONNX Runtime's native cleanup segfaults on macOS and some Linux configs.
|
|
281
308
|
// Since the process exits immediately after, Node.js reclaims everything.
|
|
@@ -291,18 +318,18 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
291
318
|
const embeddingsCached = cachedEmbeddings.length > 0;
|
|
292
319
|
console.log(`\n Repository indexed successfully (${totalTime}s)${embeddingsCached ? ` [${cachedEmbeddings.length} embeddings cached]` : ''}\n`);
|
|
293
320
|
console.log(` ${stats.nodes.toLocaleString()} nodes | ${stats.edges.toLocaleString()} edges | ${pipelineResult.communityResult?.stats.totalCommunities || 0} clusters | ${pipelineResult.processResult?.stats.totalProcesses || 0} flows`);
|
|
294
|
-
console.log(`
|
|
321
|
+
console.log(` LadybugDB ${lbugTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`);
|
|
295
322
|
console.log(` ${repoPath}`);
|
|
296
323
|
if (aiContext.files.length > 0) {
|
|
297
324
|
console.log(` Context: ${aiContext.files.join(', ')}`);
|
|
298
325
|
}
|
|
299
326
|
// Show a quiet summary if some edge types needed fallback insertion
|
|
300
|
-
if (
|
|
301
|
-
const totalFallback =
|
|
327
|
+
if (lbugWarnings.length > 0) {
|
|
328
|
+
const totalFallback = lbugWarnings.reduce((sum, w) => {
|
|
302
329
|
const m = w.match(/\((\d+) edges\)/);
|
|
303
330
|
return sum + (m ? parseInt(m[1]) : 0);
|
|
304
331
|
}, 0);
|
|
305
|
-
console.log(` Note: ${totalFallback} edges across ${
|
|
332
|
+
console.log(` Note: ${totalFallback} edges across ${lbugWarnings.length} types inserted via fallback (schema will be updated in next release)`);
|
|
306
333
|
}
|
|
307
334
|
try {
|
|
308
335
|
await fs.access(getGlobalRegistryPath());
|
|
@@ -311,7 +338,7 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
311
338
|
console.log('\n Tip: Run `gitnexus setup` to configure MCP for your editor.');
|
|
312
339
|
}
|
|
313
340
|
console.log('');
|
|
314
|
-
//
|
|
341
|
+
// LadybugDB's native module holds open handles that prevent Node from exiting.
|
|
315
342
|
// ONNX Runtime also registers native atexit hooks that segfault on some
|
|
316
343
|
// platforms (#38, #40). Force-exit to ensure clean termination.
|
|
317
344
|
process.exit(0);
|
package/dist/cli/augment.js
CHANGED
|
@@ -19,7 +19,7 @@ export async function augmentCommand(pattern) {
|
|
|
19
19
|
const result = await augment(pattern, process.cwd());
|
|
20
20
|
if (result) {
|
|
21
21
|
// IMPORTANT: Write to stderr, NOT stdout.
|
|
22
|
-
//
|
|
22
|
+
// LadybugDB's native module captures stdout fd at OS level during init,
|
|
23
23
|
// which makes stdout permanently broken in subprocess contexts.
|
|
24
24
|
// stderr is never captured, so it works reliably everywhere.
|
|
25
25
|
// The hook reads from the subprocess's stderr.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Eval Server — Lightweight HTTP server for SWE-bench evaluation
|
|
3
3
|
*
|
|
4
|
-
* Keeps
|
|
4
|
+
* Keeps LadybugDB warm in memory so tool calls from the agent are near-instant.
|
|
5
5
|
* Designed to run inside Docker containers during SWE-bench evaluation.
|
|
6
6
|
*
|
|
7
7
|
* KEY DESIGN: Returns LLM-friendly text, not raw JSON.
|
package/dist/cli/eval-server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Eval Server — Lightweight HTTP server for SWE-bench evaluation
|
|
3
3
|
*
|
|
4
|
-
* Keeps
|
|
4
|
+
* Keeps LadybugDB warm in memory so tool calls from the agent are near-instant.
|
|
5
5
|
* Designed to run inside Docker containers during SWE-bench evaluation.
|
|
6
6
|
*
|
|
7
7
|
* KEY DESIGN: Returns LLM-friendly text, not raw JSON.
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* POST /shutdown — Graceful shutdown.
|
|
25
25
|
*/
|
|
26
26
|
import http from 'http';
|
|
27
|
+
import { writeSync } from 'node:fs';
|
|
27
28
|
import { LocalBackend } from '../mcp/local/local-backend.js';
|
|
28
29
|
// ─── Text Formatters ──────────────────────────────────────────────────
|
|
29
30
|
// Convert structured JSON results into compact, LLM-friendly text.
|
|
@@ -121,8 +122,10 @@ export function formatContextResult(result) {
|
|
|
121
122
|
return lines.join('\n').trim();
|
|
122
123
|
}
|
|
123
124
|
export function formatImpactResult(result) {
|
|
124
|
-
if (result.error)
|
|
125
|
-
|
|
125
|
+
if (result.error) {
|
|
126
|
+
const suggestion = result.suggestion ? `\nSuggestion: ${result.suggestion}` : '';
|
|
127
|
+
return `Error: ${result.error}${suggestion}`;
|
|
128
|
+
}
|
|
126
129
|
const target = result.target;
|
|
127
130
|
const direction = result.direction;
|
|
128
131
|
const byDepth = result.byDepth || {};
|
|
@@ -132,7 +135,11 @@ export function formatImpactResult(result) {
|
|
|
132
135
|
}
|
|
133
136
|
const lines = [];
|
|
134
137
|
const dirLabel = direction === 'upstream' ? 'depends on this (will break if changed)' : 'this depends on';
|
|
135
|
-
lines.push(`Blast radius for ${target?.kind || ''} ${target?.name} (${direction}): ${total} symbol(s) ${dirLabel}
|
|
138
|
+
lines.push(`Blast radius for ${target?.kind || ''} ${target?.name} (${direction}): ${total} symbol(s) ${dirLabel}`);
|
|
139
|
+
if (result.partial) {
|
|
140
|
+
lines.push('⚠️ Partial results — graph traversal was interrupted. Deeper impacts may exist.');
|
|
141
|
+
}
|
|
142
|
+
lines.push('');
|
|
136
143
|
const depthLabels = {
|
|
137
144
|
1: 'WILL BREAK (direct)',
|
|
138
145
|
2: 'LIKELY AFFECTED (indirect)',
|
|
@@ -346,10 +353,11 @@ export async function evalServerCommand(options) {
|
|
|
346
353
|
console.error(` Auto-shutdown after ${idleTimeoutSec}s idle`);
|
|
347
354
|
}
|
|
348
355
|
try {
|
|
349
|
-
process.stdout
|
|
356
|
+
// Use fd 1 directly — LadybugDB captures process.stdout (#324)
|
|
357
|
+
writeSync(1, `GITNEXUS_EVAL_SERVER_READY:${port}\n`);
|
|
350
358
|
}
|
|
351
359
|
catch {
|
|
352
|
-
// stdout may not be available
|
|
360
|
+
// stdout may not be available (e.g., broken pipe)
|
|
353
361
|
}
|
|
354
362
|
});
|
|
355
363
|
resetIdleTimer();
|