gitnexus 1.3.9 → 1.3.11
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 +194 -194
- package/dist/cli/ai-context.js +105 -87
- package/dist/cli/analyze.js +8 -0
- package/dist/cli/index.js +15 -25
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/setup.js +17 -19
- package/dist/core/augmentation/engine.js +20 -20
- package/dist/core/embeddings/embedding-pipeline.js +26 -26
- package/dist/core/ingestion/ast-cache.js +3 -2
- package/dist/core/ingestion/cluster-enricher.js +16 -16
- package/dist/core/ingestion/pipeline.js +23 -2
- package/dist/core/ingestion/tree-sitter-queries.js +484 -484
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/kuzu/kuzu-adapter.js +11 -11
- package/dist/core/kuzu/schema.js +287 -287
- package/dist/core/search/bm25-index.js +7 -6
- package/dist/core/search/hybrid-search.js +3 -3
- package/dist/core/wiki/graph-queries.js +52 -52
- package/dist/core/wiki/html-viewer.js +192 -192
- package/dist/core/wiki/prompts.js +82 -82
- 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.js +6 -18
- package/dist/mcp/local/local-backend.js +128 -128
- package/dist/mcp/resources.js +42 -42
- package/dist/mcp/server.js +18 -18
- package/dist/mcp/tools.js +86 -86
- 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 +96 -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/mcp/tools.js
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
export const GITNEXUS_TOOLS = [
|
|
8
8
|
{
|
|
9
9
|
name: 'list_repos',
|
|
10
|
-
description: `List all indexed repositories available to GitNexus.
|
|
11
|
-
|
|
12
|
-
Returns each repo's name, path, indexed date, last commit, and stats.
|
|
13
|
-
|
|
14
|
-
WHEN TO USE: First step when multiple repos are indexed, or to discover available repos.
|
|
15
|
-
AFTER THIS: READ gitnexus://repo/{name}/context for the repo you want to work with.
|
|
16
|
-
|
|
17
|
-
When multiple repos are indexed, you MUST specify the "repo" parameter
|
|
10
|
+
description: `List all indexed repositories available to GitNexus.
|
|
11
|
+
|
|
12
|
+
Returns each repo's name, path, indexed date, last commit, and stats.
|
|
13
|
+
|
|
14
|
+
WHEN TO USE: First step when multiple repos are indexed, or to discover available repos.
|
|
15
|
+
AFTER THIS: READ gitnexus://repo/{name}/context for the repo you want to work with.
|
|
16
|
+
|
|
17
|
+
When multiple repos are indexed, you MUST specify the "repo" parameter
|
|
18
18
|
on other tools (query, context, impact, etc.) to target the correct one.`,
|
|
19
19
|
inputSchema: {
|
|
20
20
|
type: 'object',
|
|
@@ -24,17 +24,17 @@ on other tools (query, context, impact, etc.) to target the correct one.`,
|
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
name: 'query',
|
|
27
|
-
description: `Query the code knowledge graph for execution flows related to a concept.
|
|
28
|
-
Returns processes (call chains) ranked by relevance, each with its symbols and file locations.
|
|
29
|
-
|
|
30
|
-
WHEN TO USE: Understanding how code works together. Use this when you need execution flows and relationships, not just file matches. Complements grep/IDE search.
|
|
31
|
-
AFTER THIS: Use context() on a specific symbol for 360-degree view (callers, callees, categorized refs).
|
|
32
|
-
|
|
33
|
-
Returns results grouped by process (execution flow):
|
|
34
|
-
- processes: ranked execution flows with relevance priority
|
|
35
|
-
- process_symbols: all symbols in those flows with file locations and module (functional area)
|
|
36
|
-
- definitions: standalone types/interfaces not in any process
|
|
37
|
-
|
|
27
|
+
description: `Query the code knowledge graph for execution flows related to a concept.
|
|
28
|
+
Returns processes (call chains) ranked by relevance, each with its symbols and file locations.
|
|
29
|
+
|
|
30
|
+
WHEN TO USE: Understanding how code works together. Use this when you need execution flows and relationships, not just file matches. Complements grep/IDE search.
|
|
31
|
+
AFTER THIS: Use context() on a specific symbol for 360-degree view (callers, callees, categorized refs).
|
|
32
|
+
|
|
33
|
+
Returns results grouped by process (execution flow):
|
|
34
|
+
- processes: ranked execution flows with relevance priority
|
|
35
|
+
- process_symbols: all symbols in those flows with file locations and module (functional area)
|
|
36
|
+
- definitions: standalone types/interfaces not in any process
|
|
37
|
+
|
|
38
38
|
Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank Fusion.`,
|
|
39
39
|
inputSchema: {
|
|
40
40
|
type: 'object',
|
|
@@ -52,34 +52,34 @@ Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank
|
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
name: 'cypher',
|
|
55
|
-
description: `Execute Cypher query against the code knowledge graph.
|
|
56
|
-
|
|
57
|
-
WHEN TO USE: Complex structural queries that search/explore can't answer. READ gitnexus://repo/{name}/schema first for the full schema.
|
|
58
|
-
AFTER THIS: Use context() on result symbols for deeper context.
|
|
59
|
-
|
|
60
|
-
SCHEMA:
|
|
61
|
-
- Nodes: File, Folder, Function, Class, Interface, Method, CodeElement, Community, Process
|
|
62
|
-
- Multi-language nodes (use backticks): \`Struct\`, \`Enum\`, \`Trait\`, \`Impl\`, etc.
|
|
63
|
-
- All edges via single CodeRelation table with 'type' property
|
|
64
|
-
- Edge types: CONTAINS, DEFINES, CALLS, IMPORTS, EXTENDS, IMPLEMENTS, MEMBER_OF, STEP_IN_PROCESS
|
|
65
|
-
- Edge properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)
|
|
66
|
-
|
|
67
|
-
EXAMPLES:
|
|
68
|
-
• Find callers of a function:
|
|
69
|
-
MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b:Function {name: "validateUser"}) RETURN a.name, a.filePath
|
|
70
|
-
|
|
71
|
-
• Find community members:
|
|
72
|
-
MATCH (f)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) WHERE c.heuristicLabel = "Auth" RETURN f.name
|
|
73
|
-
|
|
74
|
-
• Trace a process:
|
|
75
|
-
MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) WHERE p.heuristicLabel = "UserLogin" RETURN s.name, r.step ORDER BY r.step
|
|
76
|
-
|
|
77
|
-
OUTPUT: Returns { markdown, row_count } — results formatted as a Markdown table for easy reading.
|
|
78
|
-
|
|
79
|
-
TIPS:
|
|
80
|
-
- All relationships use single CodeRelation table — filter with {type: 'CALLS'} etc.
|
|
81
|
-
- Community = auto-detected functional area (Leiden algorithm)
|
|
82
|
-
- Process = execution flow trace from entry point to terminal
|
|
55
|
+
description: `Execute Cypher query against the code knowledge graph.
|
|
56
|
+
|
|
57
|
+
WHEN TO USE: Complex structural queries that search/explore can't answer. READ gitnexus://repo/{name}/schema first for the full schema.
|
|
58
|
+
AFTER THIS: Use context() on result symbols for deeper context.
|
|
59
|
+
|
|
60
|
+
SCHEMA:
|
|
61
|
+
- Nodes: File, Folder, Function, Class, Interface, Method, CodeElement, Community, Process
|
|
62
|
+
- Multi-language nodes (use backticks): \`Struct\`, \`Enum\`, \`Trait\`, \`Impl\`, etc.
|
|
63
|
+
- All edges via single CodeRelation table with 'type' property
|
|
64
|
+
- Edge types: CONTAINS, DEFINES, CALLS, IMPORTS, EXTENDS, IMPLEMENTS, MEMBER_OF, STEP_IN_PROCESS
|
|
65
|
+
- Edge properties: type (STRING), confidence (DOUBLE), reason (STRING), step (INT32)
|
|
66
|
+
|
|
67
|
+
EXAMPLES:
|
|
68
|
+
• Find callers of a function:
|
|
69
|
+
MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b:Function {name: "validateUser"}) RETURN a.name, a.filePath
|
|
70
|
+
|
|
71
|
+
• Find community members:
|
|
72
|
+
MATCH (f)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) WHERE c.heuristicLabel = "Auth" RETURN f.name
|
|
73
|
+
|
|
74
|
+
• Trace a process:
|
|
75
|
+
MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process) WHERE p.heuristicLabel = "UserLogin" RETURN s.name, r.step ORDER BY r.step
|
|
76
|
+
|
|
77
|
+
OUTPUT: Returns { markdown, row_count } — results formatted as a Markdown table for easy reading.
|
|
78
|
+
|
|
79
|
+
TIPS:
|
|
80
|
+
- All relationships use single CodeRelation table — filter with {type: 'CALLS'} etc.
|
|
81
|
+
- Community = auto-detected functional area (Leiden algorithm)
|
|
82
|
+
- Process = execution flow trace from entry point to terminal
|
|
83
83
|
- Use heuristicLabel (not label) for human-readable community/process names`,
|
|
84
84
|
inputSchema: {
|
|
85
85
|
type: 'object',
|
|
@@ -92,12 +92,12 @@ TIPS:
|
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
name: 'context',
|
|
95
|
-
description: `360-degree view of a single code symbol.
|
|
96
|
-
Shows categorized incoming/outgoing references (calls, imports, extends, implements), process participation, and file location.
|
|
97
|
-
|
|
98
|
-
WHEN TO USE: After query() to understand a specific symbol in depth. When you need to know all callers, callees, and what execution flows a symbol participates in.
|
|
99
|
-
AFTER THIS: Use impact() if planning changes, or READ gitnexus://repo/{name}/process/{processName} for full execution trace.
|
|
100
|
-
|
|
95
|
+
description: `360-degree view of a single code symbol.
|
|
96
|
+
Shows categorized incoming/outgoing references (calls, imports, extends, implements), process participation, and file location.
|
|
97
|
+
|
|
98
|
+
WHEN TO USE: After query() to understand a specific symbol in depth. When you need to know all callers, callees, and what execution flows a symbol participates in.
|
|
99
|
+
AFTER THIS: Use impact() if planning changes, or READ gitnexus://repo/{name}/process/{processName} for full execution trace.
|
|
100
|
+
|
|
101
101
|
Handles disambiguation: if multiple symbols share the same name, returns candidates for you to pick from. Use uid param for zero-ambiguity lookup from prior results.`,
|
|
102
102
|
inputSchema: {
|
|
103
103
|
type: 'object',
|
|
@@ -113,12 +113,12 @@ Handles disambiguation: if multiple symbols share the same name, returns candida
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
name: 'detect_changes',
|
|
116
|
-
description: `Analyze uncommitted git changes and find affected execution flows.
|
|
117
|
-
Maps git diff hunks to indexed symbols, then traces which processes are impacted.
|
|
118
|
-
|
|
119
|
-
WHEN TO USE: Before committing — to understand what your changes affect. Pre-commit review, PR preparation.
|
|
120
|
-
AFTER THIS: Review affected processes. Use context() on high-risk symbols. READ gitnexus://repo/{name}/process/{name} for full traces.
|
|
121
|
-
|
|
116
|
+
description: `Analyze uncommitted git changes and find affected execution flows.
|
|
117
|
+
Maps git diff hunks to indexed symbols, then traces which processes are impacted.
|
|
118
|
+
|
|
119
|
+
WHEN TO USE: Before committing — to understand what your changes affect. Pre-commit review, PR preparation.
|
|
120
|
+
AFTER THIS: Review affected processes. Use context() on high-risk symbols. READ gitnexus://repo/{name}/process/{name} for full traces.
|
|
121
|
+
|
|
122
122
|
Returns: changed symbols, affected processes, and a risk summary.`,
|
|
123
123
|
inputSchema: {
|
|
124
124
|
type: 'object',
|
|
@@ -132,14 +132,14 @@ Returns: changed symbols, affected processes, and a risk summary.`,
|
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
name: 'rename',
|
|
135
|
-
description: `Multi-file coordinated rename using the knowledge graph + text search.
|
|
136
|
-
Finds all references via graph (high confidence) and regex text search (lower confidence). Preview by default.
|
|
137
|
-
|
|
138
|
-
WHEN TO USE: Renaming a function, class, method, or variable across the codebase. Safer than find-and-replace.
|
|
139
|
-
AFTER THIS: Run detect_changes() to verify no unexpected side effects.
|
|
140
|
-
|
|
141
|
-
Each edit is tagged with confidence:
|
|
142
|
-
- "graph": found via knowledge graph relationships (high confidence, safe to accept)
|
|
135
|
+
description: `Multi-file coordinated rename using the knowledge graph + text search.
|
|
136
|
+
Finds all references via graph (high confidence) and regex text search (lower confidence). Preview by default.
|
|
137
|
+
|
|
138
|
+
WHEN TO USE: Renaming a function, class, method, or variable across the codebase. Safer than find-and-replace.
|
|
139
|
+
AFTER THIS: Run detect_changes() to verify no unexpected side effects.
|
|
140
|
+
|
|
141
|
+
Each edit is tagged with confidence:
|
|
142
|
+
- "graph": found via knowledge graph relationships (high confidence, safe to accept)
|
|
143
143
|
- "text_search": found via regex text search (lower confidence, review carefully)`,
|
|
144
144
|
inputSchema: {
|
|
145
145
|
type: 'object',
|
|
@@ -156,25 +156,25 @@ Each edit is tagged with confidence:
|
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
name: 'impact',
|
|
159
|
-
description: `Analyze the blast radius of changing a code symbol.
|
|
160
|
-
Returns affected symbols grouped by depth, plus risk assessment, affected execution flows, and affected modules.
|
|
161
|
-
|
|
162
|
-
WHEN TO USE: Before making code changes — especially refactoring, renaming, or modifying shared code. Shows what would break.
|
|
163
|
-
AFTER THIS: Review d=1 items (WILL BREAK). Use context() on high-risk symbols.
|
|
164
|
-
|
|
165
|
-
Output includes:
|
|
166
|
-
- risk: LOW / MEDIUM / HIGH / CRITICAL
|
|
167
|
-
- summary: direct callers, processes affected, modules affected
|
|
168
|
-
- affected_processes: which execution flows break and at which step
|
|
169
|
-
- affected_modules: which functional areas are hit (direct vs indirect)
|
|
170
|
-
- byDepth: all affected symbols grouped by traversal depth
|
|
171
|
-
|
|
172
|
-
Depth groups:
|
|
173
|
-
- d=1: WILL BREAK (direct callers/importers)
|
|
174
|
-
- d=2: LIKELY AFFECTED (indirect)
|
|
175
|
-
- d=3: MAY NEED TESTING (transitive)
|
|
176
|
-
|
|
177
|
-
EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS
|
|
159
|
+
description: `Analyze the blast radius of changing a code symbol.
|
|
160
|
+
Returns affected symbols grouped by depth, plus risk assessment, affected execution flows, and affected modules.
|
|
161
|
+
|
|
162
|
+
WHEN TO USE: Before making code changes — especially refactoring, renaming, or modifying shared code. Shows what would break.
|
|
163
|
+
AFTER THIS: Review d=1 items (WILL BREAK). Use context() on high-risk symbols.
|
|
164
|
+
|
|
165
|
+
Output includes:
|
|
166
|
+
- risk: LOW / MEDIUM / HIGH / CRITICAL
|
|
167
|
+
- summary: direct callers, processes affected, modules affected
|
|
168
|
+
- affected_processes: which execution flows break and at which step
|
|
169
|
+
- affected_modules: which functional areas are hit (direct vs indirect)
|
|
170
|
+
- byDepth: all affected symbols grouped by traversal depth
|
|
171
|
+
|
|
172
|
+
Depth groups:
|
|
173
|
+
- d=1: WILL BREAK (direct callers/importers)
|
|
174
|
+
- d=2: LIKELY AFFECTED (indirect)
|
|
175
|
+
- d=3: MAY NEED TESTING (transitive)
|
|
176
|
+
|
|
177
|
+
EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS
|
|
178
178
|
Confidence: 1.0 = certain, <0.8 = fuzzy match`,
|
|
179
179
|
inputSchema: {
|
|
180
180
|
type: 'object',
|
|
@@ -1,155 +1,238 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* GitNexus Claude Code Hook
|
|
4
|
-
*
|
|
5
|
-
* PreToolUse
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* GitNexus Claude Code Hook
|
|
4
|
+
*
|
|
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.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: SessionStart hooks are broken on Windows (Claude Code bug).
|
|
11
|
+
* Session context is injected via CLAUDE.md / skills instead.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { spawnSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Read JSON input from stdin synchronously.
|
|
20
|
+
*/
|
|
21
|
+
function readInput() {
|
|
22
|
+
try {
|
|
23
|
+
const data = fs.readFileSync(0, 'utf-8');
|
|
24
|
+
return JSON.parse(data);
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find the .gitnexus directory by walking up from startDir.
|
|
32
|
+
* Returns the path to .gitnexus/ or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
function findGitNexusDir(startDir) {
|
|
35
|
+
let dir = startDir || process.cwd();
|
|
36
|
+
for (let i = 0; i < 5; i++) {
|
|
37
|
+
const candidate = path.join(dir, '.gitnexus');
|
|
38
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
39
|
+
const parent = path.dirname(dir);
|
|
40
|
+
if (parent === dir) break;
|
|
41
|
+
dir = parent;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract search pattern from tool input.
|
|
48
|
+
*/
|
|
49
|
+
function extractPattern(toolName, toolInput) {
|
|
50
|
+
if (toolName === 'Grep') {
|
|
51
|
+
return toolInput.pattern || null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (toolName === 'Glob') {
|
|
55
|
+
const raw = toolInput.pattern || '';
|
|
56
|
+
const match = raw.match(/[*\/]([a-zA-Z][a-zA-Z0-9_-]{2,})/);
|
|
57
|
+
return match ? match[1] : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (toolName === 'Bash') {
|
|
61
|
+
const cmd = toolInput.command || '';
|
|
62
|
+
if (!/\brg\b|\bgrep\b/.test(cmd)) return null;
|
|
63
|
+
|
|
64
|
+
const tokens = cmd.split(/\s+/);
|
|
65
|
+
let foundCmd = false;
|
|
66
|
+
let skipNext = false;
|
|
67
|
+
const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']);
|
|
68
|
+
|
|
69
|
+
for (const token of tokens) {
|
|
70
|
+
if (skipNext) { skipNext = false; continue; }
|
|
71
|
+
if (!foundCmd) {
|
|
72
|
+
if (/\brg$|\bgrep$/.test(token)) foundCmd = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (token.startsWith('-')) {
|
|
76
|
+
if (flagsWithValues.has(token)) skipNext = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const cleaned = token.replace(/['"]/g, '');
|
|
80
|
+
return cleaned.length >= 3 ? cleaned : null;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
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
|
+
}
|
|
105
|
+
|
|
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
|
+
}
|
|
126
|
+
|
|
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;
|
|
134
|
+
|
|
135
|
+
const toolName = input.tool_name || '';
|
|
136
|
+
const toolInput = input.tool_input || {};
|
|
137
|
+
|
|
138
|
+
if (toolName !== 'Grep' && toolName !== 'Glob' && toolName !== 'Bash') return;
|
|
139
|
+
|
|
140
|
+
const pattern = extractPattern(toolName, toolInput);
|
|
141
|
+
if (!pattern || pattern.length < 3) return;
|
|
142
|
+
|
|
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) {
|
|
148
|
+
result = child.stderr || '';
|
|
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 `gitnexus 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 gitnexus 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);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
if (process.env.GITNEXUS_DEBUG) {
|
|
233
|
+
console.error('GitNexus hook error:', (err.message || '').slice(0, 200));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main();
|