@veewo/gitnexus 1.3.11 → 1.4.6-rc
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 +37 -80
- package/dist/benchmark/agent-context/tool-runner.js +2 -2
- package/dist/benchmark/neonspark-candidates.js +3 -3
- package/dist/benchmark/tool-runner.js +2 -2
- package/dist/cli/ai-context.d.ts +2 -1
- package/dist/cli/ai-context.js +16 -12
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +68 -48
- package/dist/cli/augment.js +1 -1
- package/dist/cli/eval-server.d.ts +8 -1
- package/dist/cli/eval-server.js +30 -13
- package/dist/cli/index.js +28 -82
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/mcp.js +3 -1
- package/dist/cli/setup.js +87 -48
- package/dist/cli/setup.test.js +18 -13
- 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 +50 -16
- package/dist/cli/wiki.js +8 -4
- package/dist/config/ignore-service.d.ts +25 -0
- package/dist/config/ignore-service.js +76 -0
- package/dist/config/supported-languages.d.ts +4 -1
- package/dist/config/supported-languages.js +3 -2
- package/dist/core/augmentation/engine.js +94 -67
- 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 +52 -25
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/types.d.ts +7 -2
- package/dist/core/ingestion/ast-cache.js +3 -2
- package/dist/core/ingestion/call-processor.d.ts +8 -6
- package/dist/core/ingestion/call-processor.js +468 -206
- package/dist/core/ingestion/call-routing.d.ts +53 -0
- package/dist/core/ingestion/call-routing.js +108 -0
- 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 +116 -23
- 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 +19 -4
- package/dist/core/ingestion/framework-detection.js +182 -6
- 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 +199 -579
- 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 +4 -1
- package/dist/core/ingestion/parsing-processor.js +107 -109
- package/dist/core/ingestion/pipeline.d.ts +6 -3
- package/dist/core/ingestion/pipeline.js +208 -114
- package/dist/core/ingestion/process-processor.js +8 -2
- 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 +13 -10
- package/dist/core/ingestion/tree-sitter-queries.js +297 -7
- 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/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +103 -0
- package/dist/core/ingestion/utils.js +1085 -4
- package/dist/core/ingestion/workers/parse-worker.d.ts +51 -4
- package/dist/core/ingestion/workers/parse-worker.js +634 -222
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/{kuzu → lbug}/csv-generator.d.ts +12 -10
- package/dist/core/{kuzu → lbug}/csv-generator.js +82 -101
- package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +20 -25
- package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +150 -122
- package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
- package/dist/core/{kuzu → lbug}/schema.js +23 -22
- package/dist/core/lbug/schema.test.d.ts +1 -0
- package/dist/core/search/bm25-index.d.ts +4 -4
- package/dist/core/search/bm25-index.js +12 -11
- package/dist/core/search/hybrid-search.d.ts +2 -2
- package/dist/core/search/hybrid-search.js +6 -6
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/wiki/generator.d.ts +2 -2
- package/dist/core/wiki/generator.js +6 -6
- package/dist/core/wiki/graph-queries.d.ts +4 -4
- package/dist/core/wiki/graph-queries.js +7 -7
- 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} +11 -10
- package/dist/mcp/core/lbug-adapter.js +327 -0
- package/dist/mcp/local/local-backend.d.ts +21 -16
- package/dist/mcp/local/local-backend.js +306 -706
- package/dist/mcp/local/unity-parity-seed-loader.d.ts +6 -1
- package/dist/mcp/local/unity-parity-seed-loader.js +119 -9
- package/dist/mcp/local/unity-parity-seed-loader.test.js +95 -7
- package/dist/mcp/resources.js +2 -2
- package/dist/mcp/server.js +28 -13
- package/dist/mcp/staleness.js +2 -2
- package/dist/mcp/tools.js +12 -3
- 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/git.js +4 -1
- package/dist/storage/repo-manager.d.ts +20 -2
- package/dist/storage/repo-manager.js +74 -4
- package/dist/types/pipeline.d.ts +1 -1
- package/hooks/claude/gitnexus-hook.cjs +149 -46
- package/hooks/claude/pre-tool-use.sh +2 -1
- package/hooks/claude/session-start.sh +0 -0
- package/package.json +20 -4
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/skills/gitnexus-cli.md +8 -8
- package/skills/gitnexus-debugging.md +1 -1
- package/skills/gitnexus-exploring.md +1 -1
- package/skills/gitnexus-guide.md +1 -1
- package/skills/gitnexus-impact-analysis.md +1 -1
- package/skills/gitnexus-pr-review.md +163 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/dist/cli/claude-hooks.d.ts +0 -22
- package/dist/cli/claude-hooks.js +0 -97
- package/dist/mcp/core/kuzu-adapter.js +0 -231
- /package/dist/core/{kuzu/csv-generator.test.d.ts → ingestion/type-extractors/types.js} +0 -0
- /package/dist/core/{kuzu/relationship-pair-buckets.test.d.ts → lbug/csv-generator.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/csv-generator.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.d.ts +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.js +0 -0
- /package/dist/core/{kuzu/schema.test.d.ts → lbug/relationship-pair-buckets.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/schema.test.js +0 -0
|
@@ -35,10 +35,64 @@ export const getStoragePaths = (repoPath) => {
|
|
|
35
35
|
const storagePath = getStoragePath(repoPath);
|
|
36
36
|
return {
|
|
37
37
|
storagePath,
|
|
38
|
-
|
|
38
|
+
lbugPath: path.join(storagePath, 'lbug'),
|
|
39
39
|
metaPath: path.join(storagePath, 'meta.json'),
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Check whether a KuzuDB index exists in the given storage path.
|
|
44
|
+
* Non-destructive — safe to call from status commands.
|
|
45
|
+
*/
|
|
46
|
+
export const hasKuzuIndex = async (storagePath) => {
|
|
47
|
+
try {
|
|
48
|
+
await fs.stat(path.join(storagePath, 'kuzu'));
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Clean up stale KuzuDB files after migration to LadybugDB.
|
|
57
|
+
*
|
|
58
|
+
* Returns:
|
|
59
|
+
* found — true if .gitnexus/kuzu existed and was deleted
|
|
60
|
+
* needsReindex — true if kuzu existed but lbug does not (re-analyze required)
|
|
61
|
+
*
|
|
62
|
+
* Callers own the user-facing messaging; this function only deletes files.
|
|
63
|
+
*/
|
|
64
|
+
export const cleanupOldKuzuFiles = async (storagePath) => {
|
|
65
|
+
const oldPath = path.join(storagePath, 'kuzu');
|
|
66
|
+
const newPath = path.join(storagePath, 'lbug');
|
|
67
|
+
try {
|
|
68
|
+
await fs.stat(oldPath);
|
|
69
|
+
// Old kuzu file/dir exists — determine if lbug is already present
|
|
70
|
+
let needsReindex = false;
|
|
71
|
+
try {
|
|
72
|
+
await fs.stat(newPath);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
needsReindex = true;
|
|
76
|
+
}
|
|
77
|
+
// Delete kuzu database file and its sidecars (.wal, .lock)
|
|
78
|
+
for (const suffix of ['', '.wal', '.lock']) {
|
|
79
|
+
try {
|
|
80
|
+
await fs.unlink(oldPath + suffix);
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
}
|
|
84
|
+
// Also handle the case where kuzu was stored as a directory
|
|
85
|
+
try {
|
|
86
|
+
await fs.rm(oldPath, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
return { found: true, needsReindex };
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Old path doesn't exist — nothing to do
|
|
93
|
+
return { found: false, needsReindex: false };
|
|
94
|
+
}
|
|
95
|
+
};
|
|
42
96
|
/**
|
|
43
97
|
* Load metadata from an indexed repo
|
|
44
98
|
*/
|
|
@@ -169,12 +223,20 @@ export const registerRepo = async (repoPath, meta, options) => {
|
|
|
169
223
|
const { storagePath } = getStoragePaths(resolved);
|
|
170
224
|
const entries = await readRegistry();
|
|
171
225
|
if (alias) {
|
|
172
|
-
const aliasConflict = entries.find((e) => e.name === alias &&
|
|
226
|
+
const aliasConflict = entries.find((e) => e.name === alias && (process.platform === 'win32'
|
|
227
|
+
? path.resolve(e.path).toLowerCase() !== resolved.toLowerCase()
|
|
228
|
+
: path.resolve(e.path) !== resolved));
|
|
173
229
|
if (aliasConflict) {
|
|
174
230
|
throw new Error(`Repo alias "${alias}" is already registered for ${aliasConflict.path}`);
|
|
175
231
|
}
|
|
176
232
|
}
|
|
177
|
-
const existing = entries.findIndex((e) =>
|
|
233
|
+
const existing = entries.findIndex((e) => {
|
|
234
|
+
const a = path.resolve(e.path);
|
|
235
|
+
const b = resolved;
|
|
236
|
+
return process.platform === 'win32'
|
|
237
|
+
? a.toLowerCase() === b.toLowerCase()
|
|
238
|
+
: a === b;
|
|
239
|
+
});
|
|
178
240
|
const entry = {
|
|
179
241
|
name,
|
|
180
242
|
path: resolved,
|
|
@@ -253,5 +315,13 @@ export const loadCLIConfig = async () => {
|
|
|
253
315
|
export const saveCLIConfig = async (config) => {
|
|
254
316
|
const dir = getGlobalDir();
|
|
255
317
|
await fs.mkdir(dir, { recursive: true });
|
|
256
|
-
|
|
318
|
+
const configPath = getGlobalConfigPath();
|
|
319
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
320
|
+
// Restrict file permissions on Unix (config may contain API keys)
|
|
321
|
+
if (process.platform !== 'win32') {
|
|
322
|
+
try {
|
|
323
|
+
await fs.chmod(configPath, 0o600);
|
|
324
|
+
}
|
|
325
|
+
catch { /* best-effort */ }
|
|
326
|
+
}
|
|
257
327
|
};
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface PipelineRunOptions {
|
|
|
21
21
|
}
|
|
22
22
|
export interface PipelineResult {
|
|
23
23
|
graph: KnowledgeGraph;
|
|
24
|
-
/** Absolute path to the repo root — used for lazy file reads during
|
|
24
|
+
/** Absolute path to the repo root — used for lazy file reads during LadybugDB loading */
|
|
25
25
|
repoPath: string;
|
|
26
26
|
/** Total files scanned (for stats) */
|
|
27
27
|
totalFileCount: number;
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* GitNexus Claude Code Hook
|
|
4
4
|
*
|
|
5
|
-
* PreToolUse
|
|
6
|
-
*
|
|
5
|
+
* PreToolUse — intercepts Grep/Glob/Bash searches and augments
|
|
6
|
+
* with graph context from the GitNexus index.
|
|
7
|
+
* PostToolUse — detects stale index after git mutations and notifies
|
|
8
|
+
* the agent to reindex.
|
|
7
9
|
*
|
|
8
10
|
* NOTE: SessionStart hooks are broken on Windows (Claude Code bug).
|
|
9
11
|
* Session context is injected via CLAUDE.md / skills instead.
|
|
@@ -11,7 +13,7 @@
|
|
|
11
13
|
|
|
12
14
|
const fs = require('fs');
|
|
13
15
|
const path = require('path');
|
|
14
|
-
const {
|
|
16
|
+
const { spawnSync } = require('child_process');
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Read JSON input from stdin synchronously.
|
|
@@ -26,19 +28,19 @@ function readInput() {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
31
|
+
* Find the .gitnexus directory by walking up from startDir.
|
|
32
|
+
* Returns the path to .gitnexus/ or null if not found.
|
|
30
33
|
*/
|
|
31
|
-
function
|
|
34
|
+
function findGitNexusDir(startDir) {
|
|
32
35
|
let dir = startDir || process.cwd();
|
|
33
36
|
for (let i = 0; i < 5; i++) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
+
const candidate = path.join(dir, '.gitnexus');
|
|
38
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
37
39
|
const parent = path.dirname(dir);
|
|
38
40
|
if (parent === dir) break;
|
|
39
41
|
dir = parent;
|
|
40
42
|
}
|
|
41
|
-
return
|
|
43
|
+
return null;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -83,52 +85,153 @@ function extractPattern(toolName, toolInput) {
|
|
|
83
85
|
return null;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the gitnexus CLI path.
|
|
90
|
+
* 1. Relative path (works when script is inside npm package)
|
|
91
|
+
* 2. require.resolve (works when gitnexus is globally installed)
|
|
92
|
+
* 3. Fall back to npx (returns empty string)
|
|
93
|
+
*/
|
|
94
|
+
function resolveCliPath() {
|
|
95
|
+
let cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
|
96
|
+
if (!fs.existsSync(cliPath)) {
|
|
97
|
+
try {
|
|
98
|
+
cliPath = require.resolve('gitnexus/dist/cli/index.js');
|
|
99
|
+
} catch {
|
|
100
|
+
cliPath = '';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return cliPath;
|
|
104
|
+
}
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Spawn a gitnexus CLI command synchronously.
|
|
108
|
+
* Returns the stderr output (KuzuDB captures stdout at OS level).
|
|
109
|
+
*/
|
|
110
|
+
function runGitNexusCli(cliPath, args, cwd, timeout) {
|
|
111
|
+
const isWin = process.platform === 'win32';
|
|
112
|
+
if (cliPath) {
|
|
113
|
+
return spawnSync(
|
|
114
|
+
process.execPath,
|
|
115
|
+
[cliPath, ...args],
|
|
116
|
+
{ encoding: 'utf-8', timeout, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// On Windows, invoke npx.cmd directly (no shell needed)
|
|
120
|
+
return spawnSync(
|
|
121
|
+
isWin ? 'npx.cmd' : 'npx',
|
|
122
|
+
['-y', 'gitnexus', ...args],
|
|
123
|
+
{ encoding: 'utf-8', timeout: timeout + 5000, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
95
126
|
|
|
96
|
-
|
|
97
|
-
|
|
127
|
+
/**
|
|
128
|
+
* PreToolUse handler — augment searches with graph context.
|
|
129
|
+
*/
|
|
130
|
+
function handlePreToolUse(input) {
|
|
131
|
+
const cwd = input.cwd || process.cwd();
|
|
132
|
+
if (!path.isAbsolute(cwd)) return;
|
|
133
|
+
if (!findGitNexusDir(cwd)) return;
|
|
98
134
|
|
|
99
|
-
|
|
135
|
+
const toolName = input.tool_name || '';
|
|
136
|
+
const toolInput = input.tool_input || {};
|
|
100
137
|
|
|
101
|
-
|
|
102
|
-
if (!pattern || pattern.length < 3) return;
|
|
138
|
+
if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Bash') return;
|
|
103
139
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const cliPath = path.resolve(__dirname, '..', '..', 'dist', 'cli', 'index.js');
|
|
140
|
+
const pattern = extractPattern(toolName, toolInput);
|
|
141
|
+
if (!pattern || pattern.length < 3) return;
|
|
107
142
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const child = spawnSync(
|
|
114
|
-
process.execPath,
|
|
115
|
-
[cliPath, 'augment', pattern],
|
|
116
|
-
{ encoding: 'utf-8', timeout: 8000, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
|
|
117
|
-
);
|
|
143
|
+
const cliPath = resolveCliPath();
|
|
144
|
+
let result = '';
|
|
145
|
+
try {
|
|
146
|
+
const child = runGitNexusCli(cliPath, ['augment', '--', pattern], cwd, 7000);
|
|
147
|
+
if (!child.error && child.status === 0) {
|
|
118
148
|
result = child.stderr || '';
|
|
119
|
-
} catch { /* graceful failure */ }
|
|
120
|
-
|
|
121
|
-
if (result && result.trim()) {
|
|
122
|
-
console.log(JSON.stringify({
|
|
123
|
-
hookSpecificOutput: {
|
|
124
|
-
hookEventName: 'PreToolUse',
|
|
125
|
-
additionalContext: result.trim()
|
|
126
|
-
}
|
|
127
|
-
}));
|
|
128
149
|
}
|
|
150
|
+
} catch { /* graceful failure */ }
|
|
151
|
+
|
|
152
|
+
if (result && result.trim()) {
|
|
153
|
+
sendHookResponse('PreToolUse', result.trim());
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Emit a PostToolUse hook response with additional context for the agent.
|
|
159
|
+
*/
|
|
160
|
+
function sendHookResponse(hookEventName, message) {
|
|
161
|
+
console.log(JSON.stringify({
|
|
162
|
+
hookSpecificOutput: { hookEventName, additionalContext: message }
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* PostToolUse handler — detect index staleness after git mutations.
|
|
168
|
+
*
|
|
169
|
+
* Instead of spawning a full `npx -y @veewo/gitnexus@latest analyze` synchronously (which blocks
|
|
170
|
+
* the agent for up to 120s and risks KuzuDB corruption on timeout), we do a
|
|
171
|
+
* lightweight staleness check: compare `git rev-parse HEAD` against the
|
|
172
|
+
* lastCommit stored in `.gitnexus/meta.json`. If they differ, notify the
|
|
173
|
+
* agent so it can decide when to reindex.
|
|
174
|
+
*/
|
|
175
|
+
function handlePostToolUse(input) {
|
|
176
|
+
const toolName = input.tool_name || '';
|
|
177
|
+
if (toolName !== 'Bash') return;
|
|
178
|
+
|
|
179
|
+
const command = (input.tool_input || {}).command || '';
|
|
180
|
+
if (!/\bgit\s+(commit|merge|rebase|cherry-pick|pull)(\s|$)/.test(command)) return;
|
|
181
|
+
|
|
182
|
+
// Only proceed if the command succeeded
|
|
183
|
+
const toolOutput = input.tool_output || {};
|
|
184
|
+
if (toolOutput.exit_code !== undefined && toolOutput.exit_code !== 0) return;
|
|
185
|
+
|
|
186
|
+
const cwd = input.cwd || process.cwd();
|
|
187
|
+
if (!path.isAbsolute(cwd)) return;
|
|
188
|
+
const gitNexusDir = findGitNexusDir(cwd);
|
|
189
|
+
if (!gitNexusDir) return;
|
|
190
|
+
|
|
191
|
+
// Compare HEAD against last indexed commit — skip if unchanged
|
|
192
|
+
let currentHead = '';
|
|
193
|
+
try {
|
|
194
|
+
const headResult = spawnSync('git', ['rev-parse', 'HEAD'], {
|
|
195
|
+
encoding: 'utf-8', timeout: 3000, cwd, stdio: ['pipe', 'pipe', 'pipe'],
|
|
196
|
+
});
|
|
197
|
+
currentHead = (headResult.stdout || '').trim();
|
|
198
|
+
} catch { return; }
|
|
199
|
+
|
|
200
|
+
if (!currentHead) return;
|
|
201
|
+
|
|
202
|
+
let lastCommit = '';
|
|
203
|
+
let hadEmbeddings = false;
|
|
204
|
+
try {
|
|
205
|
+
const meta = JSON.parse(fs.readFileSync(path.join(gitNexusDir, 'meta.json'), 'utf-8'));
|
|
206
|
+
lastCommit = meta.lastCommit || '';
|
|
207
|
+
hadEmbeddings = (meta.stats && meta.stats.embeddings > 0);
|
|
208
|
+
} catch { /* no meta — treat as stale */ }
|
|
209
|
+
|
|
210
|
+
// If HEAD matches last indexed commit, no reindex needed
|
|
211
|
+
if (currentHead && currentHead === lastCommit) return;
|
|
212
|
+
|
|
213
|
+
const analyzeCmd = `npx -y @veewo/gitnexus@latest analyze${hadEmbeddings ? ' --embeddings' : ''}`;
|
|
214
|
+
sendHookResponse('PostToolUse',
|
|
215
|
+
`GitNexus index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` +
|
|
216
|
+
`Run \`${analyzeCmd}\` to update the knowledge graph.`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Dispatch map for hook events
|
|
221
|
+
const handlers = {
|
|
222
|
+
PreToolUse: handlePreToolUse,
|
|
223
|
+
PostToolUse: handlePostToolUse,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
function main() {
|
|
227
|
+
try {
|
|
228
|
+
const input = readInput();
|
|
229
|
+
const handler = handlers[input.hook_event_name || ''];
|
|
230
|
+
if (handler) handler(input);
|
|
129
231
|
} catch (err) {
|
|
130
|
-
|
|
131
|
-
|
|
232
|
+
if (process.env.GITNEXUS_DEBUG) {
|
|
233
|
+
console.error('GitNexus hook error:', (err.message || '').slice(0, 200));
|
|
234
|
+
}
|
|
132
235
|
}
|
|
133
236
|
}
|
|
134
237
|
|
|
@@ -63,7 +63,8 @@ if [ "$found" = false ]; then
|
|
|
63
63
|
fi
|
|
64
64
|
|
|
65
65
|
# Run gitnexus 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 @veewo/gitnexus@latest augment "$PATTERN" 2>&1 1>/dev/null)
|
|
67
68
|
|
|
68
69
|
if [ -n "$RESULT" ]; then
|
|
69
70
|
ESCAPED=$(echo "$RESULT" | jq -Rs .)
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veewo/gitnexus",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.6-rc",
|
|
4
4
|
"description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
|
|
5
5
|
"author": "Abhigyan Patwari",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -32,13 +32,21 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
34
34
|
"hooks",
|
|
35
|
+
"scripts",
|
|
35
36
|
"skills",
|
|
36
37
|
"vendor"
|
|
37
38
|
],
|
|
38
39
|
"scripts": {
|
|
39
40
|
"build": "rm -rf dist && tsc",
|
|
40
41
|
"dev": "tsx watch src/cli/index.ts",
|
|
42
|
+
"test": "vitest run test/unit",
|
|
43
|
+
"test:integration": "vitest run test/integration",
|
|
44
|
+
"test:all": "vitest run",
|
|
45
|
+
"test:watch": "vitest",
|
|
46
|
+
"test:coverage": "vitest run --coverage",
|
|
41
47
|
"prepare": "npm run build",
|
|
48
|
+
"postinstall": "node scripts/patch-tree-sitter-swift.cjs",
|
|
49
|
+
"prepack": "npm run build && chmod +x dist/cli/index.js",
|
|
42
50
|
"check:release-paths": "node hooks/check-release-path-hygiene.mjs",
|
|
43
51
|
"check:neonspark-target": "node -e \"const p=(process.env.GITNEXUS_NEONSPARK_TARGET_PATH||'').trim();if(!p){console.error('Missing env: GITNEXUS_NEONSPARK_TARGET_PATH');process.exit(1);}\"",
|
|
44
52
|
"check:u2-e2e-target": "node -e \"const p=(process.env.GITNEXUS_U2_E2E_TARGET_PATH||'').trim();if(!p){console.error('Missing env: GITNEXUS_U2_E2E_TARGET_PATH');process.exit(1);}\"",
|
|
@@ -70,7 +78,8 @@
|
|
|
70
78
|
"graphology": "^0.25.4",
|
|
71
79
|
"graphology-indices": "^0.17.0",
|
|
72
80
|
"graphology-utils": "^2.3.0",
|
|
73
|
-
"
|
|
81
|
+
"@ladybugdb/core": "^0.15.1",
|
|
82
|
+
"ignore": "^7.0.5",
|
|
74
83
|
"lru-cache": "^11.0.0",
|
|
75
84
|
"mnemonist": "^0.39.0",
|
|
76
85
|
"pandemonium": "^2.4.0",
|
|
@@ -83,18 +92,25 @@
|
|
|
83
92
|
"tree-sitter-javascript": "^0.21.0",
|
|
84
93
|
"tree-sitter-php": "^0.23.12",
|
|
85
94
|
"tree-sitter-python": "^0.21.0",
|
|
95
|
+
"tree-sitter-ruby": "^0.23.1",
|
|
86
96
|
"tree-sitter-rust": "^0.21.0",
|
|
87
97
|
"tree-sitter-typescript": "^0.21.0",
|
|
88
|
-
"typescript": "^5.4.5",
|
|
89
98
|
"uuid": "^13.0.0"
|
|
90
99
|
},
|
|
100
|
+
"optionalDependencies": {
|
|
101
|
+
"tree-sitter-kotlin": "^0.3.8",
|
|
102
|
+
"tree-sitter-swift": "^0.6.0"
|
|
103
|
+
},
|
|
91
104
|
"devDependencies": {
|
|
92
105
|
"@types/cli-progress": "^3.11.6",
|
|
93
106
|
"@types/cors": "^2.8.17",
|
|
94
107
|
"@types/express": "^4.17.21",
|
|
95
108
|
"@types/node": "^20.0.0",
|
|
96
109
|
"@types/uuid": "^10.0.0",
|
|
97
|
-
"
|
|
110
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
111
|
+
"tsx": "^4.0.0",
|
|
112
|
+
"typescript": "^5.4.5",
|
|
113
|
+
"vitest": "^4.0.18"
|
|
98
114
|
},
|
|
99
115
|
"engines": {
|
|
100
116
|
"node": ">=18.0.0"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* WORKAROUND: tree-sitter-swift@0.6.0 binding.gyp build failure
|
|
4
|
+
*
|
|
5
|
+
* Background:
|
|
6
|
+
* tree-sitter-swift@0.6.0's binding.gyp contains an "actions" array that
|
|
7
|
+
* invokes `tree-sitter generate` to regenerate parser.c from grammar.js.
|
|
8
|
+
* This is intended for grammar developers, but the published npm package
|
|
9
|
+
* already ships pre-generated parser files (parser.c, scanner.c), so the
|
|
10
|
+
* actions are unnecessary for consumers. Since consumers don't have
|
|
11
|
+
* tree-sitter-cli installed, the actions always fail during `npm install`.
|
|
12
|
+
*
|
|
13
|
+
* Why we can't just upgrade:
|
|
14
|
+
* tree-sitter-swift@0.7.1 fixes this (removes postinstall, ships prebuilds),
|
|
15
|
+
* but it requires tree-sitter@^0.22.1. The upstream project pins tree-sitter
|
|
16
|
+
* to ^0.21.0 and all other grammar packages depend on that version.
|
|
17
|
+
* Upgrading tree-sitter would be a separate breaking change.
|
|
18
|
+
*
|
|
19
|
+
* How this workaround works:
|
|
20
|
+
* 1. tree-sitter-swift's own postinstall fails (npm warns but continues)
|
|
21
|
+
* 2. This script runs as gitnexus's postinstall
|
|
22
|
+
* 3. It removes the "actions" array from binding.gyp
|
|
23
|
+
* 4. It rebuilds the native binding with the cleaned binding.gyp
|
|
24
|
+
*
|
|
25
|
+
* TODO: Remove this script when tree-sitter is upgraded to ^0.22.x,
|
|
26
|
+
* which allows using tree-sitter-swift@0.7.1+ directly.
|
|
27
|
+
*/
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
const { execSync } = require('child_process');
|
|
31
|
+
|
|
32
|
+
const swiftDir = path.join(__dirname, '..', 'node_modules', 'tree-sitter-swift');
|
|
33
|
+
const bindingPath = path.join(swiftDir, 'binding.gyp');
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
if (!fs.existsSync(bindingPath)) {
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = fs.readFileSync(bindingPath, 'utf8');
|
|
41
|
+
let needsRebuild = false;
|
|
42
|
+
|
|
43
|
+
if (content.includes('"actions"')) {
|
|
44
|
+
// Strip Python-style comments (#) before JSON parsing
|
|
45
|
+
const cleaned = content.replace(/#[^\n]*/g, '');
|
|
46
|
+
const gyp = JSON.parse(cleaned);
|
|
47
|
+
|
|
48
|
+
if (gyp.targets && gyp.targets[0] && gyp.targets[0].actions) {
|
|
49
|
+
delete gyp.targets[0].actions;
|
|
50
|
+
fs.writeFileSync(bindingPath, JSON.stringify(gyp, null, 2) + '\n');
|
|
51
|
+
console.log('[tree-sitter-swift] Patched binding.gyp (removed actions array)');
|
|
52
|
+
needsRebuild = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if native binding exists
|
|
57
|
+
const bindingNode = path.join(swiftDir, 'build', 'Release', 'tree_sitter_swift_binding.node');
|
|
58
|
+
if (!fs.existsSync(bindingNode)) {
|
|
59
|
+
needsRebuild = true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (needsRebuild) {
|
|
63
|
+
console.log('[tree-sitter-swift] Rebuilding native binding...');
|
|
64
|
+
execSync('npx node-gyp rebuild', {
|
|
65
|
+
cwd: swiftDir,
|
|
66
|
+
stdio: 'pipe',
|
|
67
|
+
timeout: 120000,
|
|
68
|
+
});
|
|
69
|
+
console.log('[tree-sitter-swift] Native binding built successfully');
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.warn('[tree-sitter-swift] Could not build native binding:', err.message);
|
|
73
|
+
console.warn('[tree-sitter-swift] You may need to manually run: cd node_modules/tree-sitter-swift && npx node-gyp rebuild');
|
|
74
|
+
}
|
package/skills/gitnexus-cli.md
CHANGED
|
@@ -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 gitnexus analyze
|
|
15
|
+
npx -y @veewo/gitnexus@latest analyze
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files.
|
|
@@ -22,12 +22,12 @@ Run from the project root. This parses all source files, builds the knowledge gr
|
|
|
22
22
|
| `--force` | Force full re-index even if up to date |
|
|
23
23
|
| `--embeddings` | Enable embedding generation for semantic search (off by default) |
|
|
24
24
|
|
|
25
|
-
**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale.
|
|
25
|
+
**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale. In Claude Code, a PostToolUse hook runs `analyze` automatically after `git commit` and `git merge`, preserving embeddings if previously generated.
|
|
26
26
|
|
|
27
27
|
### status — Check index freshness
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx gitnexus status
|
|
30
|
+
npx -y @veewo/gitnexus@latest status
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Shows whether the current repo has a GitNexus 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 GitNexus index, when it was last updated, a
|
|
|
35
35
|
### clean — Delete the index
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx gitnexus clean
|
|
38
|
+
npx -y @veewo/gitnexus@latest clean
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
Deletes the `.gitnexus/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing GitNexus from a project.
|
|
@@ -48,7 +48,7 @@ Deletes the `.gitnexus/` directory and unregisters the repo from the global regi
|
|
|
48
48
|
### wiki — Generate documentation from the graph
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
npx gitnexus wiki
|
|
51
|
+
npx -y @veewo/gitnexus@latest wiki
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.gitnexus/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 gitnexus list
|
|
68
|
+
npx -y @veewo/gitnexus@latest list
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_repos` tool provides the same information.
|
|
@@ -75,11 +75,11 @@ Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_
|
|
|
75
75
|
For Unity resource retrieval:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npx gitnexus context DoorObj --repo neonnew-core --file Assets/NEON/Code/Game/Doors/DoorObj.cs --unity-resources on --unity-hydration compact
|
|
78
|
+
npx -y @veewo/gitnexus@latest context DoorObj --repo neonnew-core --file Assets/NEON/Code/Game/Doors/DoorObj.cs --unity-resources on --unity-hydration compact
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
npx gitnexus query "DoorObj binding" --repo neonnew-core --unity-resources on --unity-hydration compact
|
|
82
|
+
npx -y @veewo/gitnexus@latest query "DoorObj binding" --repo neonnew-core --unity-resources on --unity-hydration compact
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
Rules:
|
|
@@ -23,7 +23,7 @@ description: "Use when the user is debugging a bug, tracing an error, or asking
|
|
|
23
23
|
5. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
> If "Index is stale" → run `npx gitnexus analyze` in terminal.
|
|
26
|
+
> If "Index is stale" → run `npx -y @veewo/gitnexus@latest analyze` in terminal.
|
|
27
27
|
|
|
28
28
|
## Checklist
|
|
29
29
|
|
|
@@ -24,7 +24,7 @@ description: "Use when the user asks how code works, wants to understand archite
|
|
|
24
24
|
6. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal.
|
|
27
|
+
> If step 2 says "Index is stale" → run `npx -y @veewo/gitnexus@latest analyze` in terminal.
|
|
28
28
|
|
|
29
29
|
## Checklist
|
|
30
30
|
|
package/skills/gitnexus-guide.md
CHANGED
|
@@ -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 gitnexus analyze` in the terminal first.
|
|
18
|
+
> If step 1 warns the index is stale, run `npx -y @veewo/gitnexus@latest 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 gitnexus analyze` in terminal.
|
|
26
|
+
> If "Index is stale" → run `npx -y @veewo/gitnexus@latest analyze` in terminal.
|
|
27
27
|
|
|
28
28
|
## Checklist
|
|
29
29
|
|