gitnexus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +181 -0
  2. package/dist/cli/ai-context.d.ts +21 -0
  3. package/dist/cli/ai-context.js +219 -0
  4. package/dist/cli/analyze.d.ts +10 -0
  5. package/dist/cli/analyze.js +118 -0
  6. package/dist/cli/clean.d.ts +8 -0
  7. package/dist/cli/clean.js +29 -0
  8. package/dist/cli/index.d.ts +2 -0
  9. package/dist/cli/index.js +42 -0
  10. package/dist/cli/list.d.ts +6 -0
  11. package/dist/cli/list.js +27 -0
  12. package/dist/cli/mcp.d.ts +7 -0
  13. package/dist/cli/mcp.js +85 -0
  14. package/dist/cli/serve.d.ts +3 -0
  15. package/dist/cli/serve.js +5 -0
  16. package/dist/cli/status.d.ts +6 -0
  17. package/dist/cli/status.js +27 -0
  18. package/dist/config/ignore-service.d.ts +1 -0
  19. package/dist/config/ignore-service.js +208 -0
  20. package/dist/config/supported-languages.d.ts +11 -0
  21. package/dist/config/supported-languages.js +15 -0
  22. package/dist/core/embeddings/embedder.d.ts +60 -0
  23. package/dist/core/embeddings/embedder.js +205 -0
  24. package/dist/core/embeddings/embedding-pipeline.d.ts +50 -0
  25. package/dist/core/embeddings/embedding-pipeline.js +321 -0
  26. package/dist/core/embeddings/index.d.ts +9 -0
  27. package/dist/core/embeddings/index.js +9 -0
  28. package/dist/core/embeddings/text-generator.d.ts +24 -0
  29. package/dist/core/embeddings/text-generator.js +182 -0
  30. package/dist/core/embeddings/types.d.ts +87 -0
  31. package/dist/core/embeddings/types.js +32 -0
  32. package/dist/core/graph/graph.d.ts +2 -0
  33. package/dist/core/graph/graph.js +61 -0
  34. package/dist/core/graph/types.d.ts +50 -0
  35. package/dist/core/graph/types.js +1 -0
  36. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  37. package/dist/core/ingestion/ast-cache.js +34 -0
  38. package/dist/core/ingestion/call-processor.d.ts +8 -0
  39. package/dist/core/ingestion/call-processor.js +269 -0
  40. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  41. package/dist/core/ingestion/cluster-enricher.js +170 -0
  42. package/dist/core/ingestion/community-processor.d.ts +39 -0
  43. package/dist/core/ingestion/community-processor.js +269 -0
  44. package/dist/core/ingestion/entry-point-scoring.d.ts +39 -0
  45. package/dist/core/ingestion/entry-point-scoring.js +235 -0
  46. package/dist/core/ingestion/filesystem-walker.d.ts +5 -0
  47. package/dist/core/ingestion/filesystem-walker.js +26 -0
  48. package/dist/core/ingestion/framework-detection.d.ts +38 -0
  49. package/dist/core/ingestion/framework-detection.js +183 -0
  50. package/dist/core/ingestion/heritage-processor.d.ts +14 -0
  51. package/dist/core/ingestion/heritage-processor.js +134 -0
  52. package/dist/core/ingestion/import-processor.d.ts +8 -0
  53. package/dist/core/ingestion/import-processor.js +490 -0
  54. package/dist/core/ingestion/parsing-processor.d.ts +8 -0
  55. package/dist/core/ingestion/parsing-processor.js +249 -0
  56. package/dist/core/ingestion/pipeline.d.ts +2 -0
  57. package/dist/core/ingestion/pipeline.js +228 -0
  58. package/dist/core/ingestion/process-processor.d.ts +51 -0
  59. package/dist/core/ingestion/process-processor.js +278 -0
  60. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  61. package/dist/core/ingestion/structure-processor.js +36 -0
  62. package/dist/core/ingestion/symbol-table.d.ts +33 -0
  63. package/dist/core/ingestion/symbol-table.js +38 -0
  64. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -0
  65. package/dist/core/ingestion/tree-sitter-queries.js +319 -0
  66. package/dist/core/ingestion/utils.d.ts +10 -0
  67. package/dist/core/ingestion/utils.js +44 -0
  68. package/dist/core/kuzu/csv-generator.d.ts +22 -0
  69. package/dist/core/kuzu/csv-generator.js +272 -0
  70. package/dist/core/kuzu/kuzu-adapter.d.ts +81 -0
  71. package/dist/core/kuzu/kuzu-adapter.js +568 -0
  72. package/dist/core/kuzu/schema.d.ts +53 -0
  73. package/dist/core/kuzu/schema.js +380 -0
  74. package/dist/core/search/bm25-index.d.ts +22 -0
  75. package/dist/core/search/bm25-index.js +52 -0
  76. package/dist/core/search/hybrid-search.d.ts +49 -0
  77. package/dist/core/search/hybrid-search.js +118 -0
  78. package/dist/core/tree-sitter/parser-loader.d.ts +4 -0
  79. package/dist/core/tree-sitter/parser-loader.js +42 -0
  80. package/dist/lib/utils.d.ts +1 -0
  81. package/dist/lib/utils.js +3 -0
  82. package/dist/mcp/core/embedder.d.ts +27 -0
  83. package/dist/mcp/core/embedder.js +93 -0
  84. package/dist/mcp/core/kuzu-adapter.d.ts +23 -0
  85. package/dist/mcp/core/kuzu-adapter.js +62 -0
  86. package/dist/mcp/local/local-backend.d.ts +73 -0
  87. package/dist/mcp/local/local-backend.js +752 -0
  88. package/dist/mcp/resources.d.ts +31 -0
  89. package/dist/mcp/resources.js +279 -0
  90. package/dist/mcp/server.d.ts +12 -0
  91. package/dist/mcp/server.js +130 -0
  92. package/dist/mcp/staleness.d.ts +15 -0
  93. package/dist/mcp/staleness.js +29 -0
  94. package/dist/mcp/tools.d.ts +24 -0
  95. package/dist/mcp/tools.js +160 -0
  96. package/dist/server/api.d.ts +6 -0
  97. package/dist/server/api.js +156 -0
  98. package/dist/storage/git.d.ts +7 -0
  99. package/dist/storage/git.js +39 -0
  100. package/dist/storage/repo-manager.d.ts +61 -0
  101. package/dist/storage/repo-manager.js +106 -0
  102. package/dist/types/pipeline.d.ts +28 -0
  103. package/dist/types/pipeline.js +16 -0
  104. package/package.json +80 -0
  105. package/skills/debugging.md +104 -0
  106. package/skills/exploring.md +112 -0
  107. package/skills/impact-analysis.md +114 -0
  108. package/skills/refactoring.md +119 -0
  109. package/vendor/leiden/index.cjs +355 -0
  110. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCP Resources
3
+ *
4
+ * Provides structured on-demand data to AI agents.
5
+ * Resources complement tools by offering lightweight, cacheable data.
6
+ */
7
+ import type { LocalBackend } from './local/local-backend.js';
8
+ export interface ResourceDefinition {
9
+ uri: string;
10
+ name: string;
11
+ description: string;
12
+ mimeType: string;
13
+ }
14
+ export interface ResourceTemplate {
15
+ uriTemplate: string;
16
+ name: string;
17
+ description: string;
18
+ mimeType: string;
19
+ }
20
+ /**
21
+ * Static resources available when codebase is indexed
22
+ */
23
+ export declare function getResourceDefinitions(projectName: string): ResourceDefinition[];
24
+ /**
25
+ * Dynamic resource templates
26
+ */
27
+ export declare function getResourceTemplates(): ResourceTemplate[];
28
+ /**
29
+ * Read a resource and return its content
30
+ */
31
+ export declare function readResource(uri: string, backend: LocalBackend): Promise<string>;
@@ -0,0 +1,279 @@
1
+ /**
2
+ * MCP Resources
3
+ *
4
+ * Provides structured on-demand data to AI agents.
5
+ * Resources complement tools by offering lightweight, cacheable data.
6
+ */
7
+ import { checkStaleness } from './staleness.js';
8
+ /**
9
+ * Static resources available when codebase is indexed
10
+ */
11
+ export function getResourceDefinitions(projectName) {
12
+ return [
13
+ {
14
+ uri: 'gitnexus://context',
15
+ name: `${projectName} Overview`,
16
+ description: 'Codebase stats, hotspots, and available tools',
17
+ mimeType: 'text/yaml',
18
+ },
19
+ {
20
+ uri: 'gitnexus://clusters',
21
+ name: 'All Clusters',
22
+ description: 'List of all functional clusters with stats',
23
+ mimeType: 'text/yaml',
24
+ },
25
+ {
26
+ uri: 'gitnexus://processes',
27
+ name: 'All Processes',
28
+ description: 'List of all execution flows with types',
29
+ mimeType: 'text/yaml',
30
+ },
31
+ {
32
+ uri: 'gitnexus://schema',
33
+ name: 'Graph Schema',
34
+ description: 'Node types and relationships for Cypher queries',
35
+ mimeType: 'text/yaml',
36
+ },
37
+ ];
38
+ }
39
+ /**
40
+ * Dynamic resource templates
41
+ */
42
+ export function getResourceTemplates() {
43
+ return [
44
+ {
45
+ uriTemplate: 'gitnexus://cluster/{name}',
46
+ name: 'Cluster Detail',
47
+ description: 'Deep dive into a specific cluster',
48
+ mimeType: 'text/yaml',
49
+ },
50
+ {
51
+ uriTemplate: 'gitnexus://process/{name}',
52
+ name: 'Process Trace',
53
+ description: 'Step-by-step execution trace',
54
+ mimeType: 'text/yaml',
55
+ },
56
+ ];
57
+ }
58
+ /**
59
+ * Read a resource and return its content
60
+ */
61
+ export async function readResource(uri, backend) {
62
+ // Static resources
63
+ if (uri === 'gitnexus://context') {
64
+ return getContextResource(backend);
65
+ }
66
+ if (uri === 'gitnexus://clusters') {
67
+ return getClustersResource(backend);
68
+ }
69
+ if (uri === 'gitnexus://processes') {
70
+ return getProcessesResource(backend);
71
+ }
72
+ if (uri === 'gitnexus://schema') {
73
+ return getSchemaResource();
74
+ }
75
+ // Dynamic resources
76
+ if (uri.startsWith('gitnexus://cluster/')) {
77
+ const name = uri.replace('gitnexus://cluster/', '');
78
+ return getClusterDetailResource(name, backend);
79
+ }
80
+ if (uri.startsWith('gitnexus://process/')) {
81
+ const name = uri.replace('gitnexus://process/', '');
82
+ return getProcessDetailResource(name, backend);
83
+ }
84
+ throw new Error(`Unknown resource: ${uri}`);
85
+ }
86
+ /**
87
+ * Context resource - codebase overview
88
+ */
89
+ async function getContextResource(backend) {
90
+ const context = backend.context;
91
+ if (!context) {
92
+ return 'error: No codebase loaded. Run: gitnexus analyze';
93
+ }
94
+ // Check staleness
95
+ const repoPath = backend.repoPath;
96
+ const lastCommit = backend.meta?.lastCommit || 'HEAD';
97
+ const staleness = repoPath ? checkStaleness(repoPath, lastCommit) : { isStale: false, commitsBehind: 0 };
98
+ const lines = [
99
+ `project: ${context.projectName}`,
100
+ ];
101
+ // Add staleness warning if index is behind
102
+ if (staleness.isStale && staleness.hint) {
103
+ lines.push('');
104
+ lines.push(`staleness: "${staleness.hint}"`);
105
+ }
106
+ lines.push('');
107
+ lines.push('stats:');
108
+ lines.push(` files: ${context.stats.fileCount}`);
109
+ lines.push(` symbols: ${context.stats.functionCount}`);
110
+ lines.push(` clusters: ${context.stats.communityCount}`);
111
+ lines.push(` processes: ${context.stats.processCount}`);
112
+ lines.push('');
113
+ lines.push('tools_available:');
114
+ lines.push(' - search: Hybrid semantic + keyword search');
115
+ lines.push(' - explore: Deep dive on symbol/cluster/process');
116
+ lines.push(' - impact: Blast radius analysis');
117
+ lines.push(' - overview: List all clusters and processes');
118
+ lines.push(' - cypher: Raw graph queries');
119
+ lines.push(' - analyze: Re-index to update stale data');
120
+ lines.push('');
121
+ lines.push('resources_available:');
122
+ lines.push(' - gitnexus://clusters: All clusters');
123
+ lines.push(' - gitnexus://processes: All processes');
124
+ lines.push(' - gitnexus://cluster/{name}: Cluster details');
125
+ lines.push(' - gitnexus://process/{name}: Process trace');
126
+ return lines.join('\n');
127
+ }
128
+ /**
129
+ * Clusters resource - list all clusters
130
+ */
131
+ async function getClustersResource(backend) {
132
+ try {
133
+ const result = await backend.callTool('overview', { showClusters: true, showProcesses: false, limit: 50 });
134
+ if (!result.clusters || result.clusters.length === 0) {
135
+ return 'clusters: []\n# No clusters detected. Run: gitnexus analyze';
136
+ }
137
+ const lines = ['clusters:'];
138
+ for (const cluster of result.clusters) {
139
+ const label = cluster.heuristicLabel || cluster.label || cluster.id;
140
+ lines.push(` - name: "${label}"`);
141
+ lines.push(` symbols: ${cluster.symbolCount || 0}`);
142
+ if (cluster.cohesion) {
143
+ lines.push(` cohesion: ${(cluster.cohesion * 100).toFixed(0)}%`);
144
+ }
145
+ }
146
+ return lines.join('\n');
147
+ }
148
+ catch (err) {
149
+ return `error: ${err.message}`;
150
+ }
151
+ }
152
+ /**
153
+ * Processes resource - list all processes
154
+ */
155
+ async function getProcessesResource(backend) {
156
+ try {
157
+ const result = await backend.callTool('overview', { showClusters: false, showProcesses: true, limit: 50 });
158
+ if (!result.processes || result.processes.length === 0) {
159
+ return 'processes: []\n# No processes detected. Run: gitnexus analyze';
160
+ }
161
+ const lines = ['processes:'];
162
+ for (const proc of result.processes) {
163
+ const label = proc.heuristicLabel || proc.label || proc.id;
164
+ lines.push(` - name: "${label}"`);
165
+ lines.push(` type: ${proc.processType || 'unknown'}`);
166
+ lines.push(` steps: ${proc.stepCount || 0}`);
167
+ }
168
+ return lines.join('\n');
169
+ }
170
+ catch (err) {
171
+ return `error: ${err.message}`;
172
+ }
173
+ }
174
+ /**
175
+ * Schema resource - graph structure for Cypher queries
176
+ */
177
+ function getSchemaResource() {
178
+ return `# GitNexus Graph Schema
179
+
180
+ nodes:
181
+ - File: Source code files
182
+ - Function: Functions and arrow functions
183
+ - Class: Class definitions
184
+ - Interface: Interface/type definitions
185
+ - Method: Class methods
186
+ - Community: Functional cluster (Leiden algorithm)
187
+ - Process: Execution flow trace
188
+
189
+ relationships:
190
+ - CALLS: Function/method invocation
191
+ - IMPORTS: Module imports
192
+ - EXTENDS: Class inheritance
193
+ - IMPLEMENTS: Interface implementation
194
+ - DEFINES: File defines symbol
195
+ - MEMBER_OF: Symbol belongs to community
196
+ - STEP_IN_PROCESS: Symbol is step N in process
197
+
198
+ example_queries:
199
+ find_callers: |
200
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
201
+ RETURN caller.name, caller.filePath
202
+
203
+ find_community_members: |
204
+ MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community)
205
+ WHERE c.heuristicLabel = "Auth"
206
+ RETURN s.name, labels(s)[0] AS type
207
+
208
+ trace_process: |
209
+ MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
210
+ WHERE p.heuristicLabel = "LoginFlow"
211
+ RETURN s.name, r.step
212
+ ORDER BY r.step
213
+ `;
214
+ }
215
+ /**
216
+ * Cluster detail resource
217
+ */
218
+ async function getClusterDetailResource(name, backend) {
219
+ try {
220
+ const result = await backend.callTool('explore', { name, type: 'cluster' });
221
+ if (result.error) {
222
+ return `error: ${result.error}`;
223
+ }
224
+ const cluster = result.cluster;
225
+ const members = result.members || [];
226
+ const lines = [
227
+ `name: "${cluster.heuristicLabel || cluster.label || cluster.id}"`,
228
+ `symbols: ${cluster.symbolCount || members.length}`,
229
+ ];
230
+ if (cluster.cohesion) {
231
+ lines.push(`cohesion: ${(cluster.cohesion * 100).toFixed(0)}%`);
232
+ }
233
+ if (members.length > 0) {
234
+ lines.push('');
235
+ lines.push('members:');
236
+ for (const member of members.slice(0, 20)) {
237
+ lines.push(` - name: ${member.name}`);
238
+ lines.push(` type: ${member.type}`);
239
+ lines.push(` file: ${member.filePath}`);
240
+ }
241
+ if (members.length > 20) {
242
+ lines.push(` # ... and ${members.length - 20} more`);
243
+ }
244
+ }
245
+ return lines.join('\n');
246
+ }
247
+ catch (err) {
248
+ return `error: ${err.message}`;
249
+ }
250
+ }
251
+ /**
252
+ * Process detail resource
253
+ */
254
+ async function getProcessDetailResource(name, backend) {
255
+ try {
256
+ const result = await backend.callTool('explore', { name, type: 'process' });
257
+ if (result.error) {
258
+ return `error: ${result.error}`;
259
+ }
260
+ const proc = result.process;
261
+ const steps = result.steps || [];
262
+ const lines = [
263
+ `name: "${proc.heuristicLabel || proc.label || proc.id}"`,
264
+ `type: ${proc.processType || 'unknown'}`,
265
+ `step_count: ${proc.stepCount || steps.length}`,
266
+ ];
267
+ if (steps.length > 0) {
268
+ lines.push('');
269
+ lines.push('trace:');
270
+ for (const step of steps) {
271
+ lines.push(` ${step.step}: ${step.name} (${step.filePath})`);
272
+ }
273
+ }
274
+ return lines.join('\n');
275
+ }
276
+ catch (err) {
277
+ return `error: ${err.message}`;
278
+ }
279
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * MCP Server
3
+ *
4
+ * Model Context Protocol server that runs on stdio.
5
+ * External AI tools (Cursor, Claude) spawn this process and
6
+ * communicate via stdin/stdout using the MCP protocol.
7
+ *
8
+ * Tools: context, search, cypher, overview, explore, impact, analyze
9
+ * Resources: context, clusters, processes, schema, cluster/{name}, process/{name}
10
+ */
11
+ import type { LocalBackend } from './local/local-backend.js';
12
+ export declare function startMCPServer(backend: LocalBackend): Promise<void>;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * MCP Server
3
+ *
4
+ * Model Context Protocol server that runs on stdio.
5
+ * External AI tools (Cursor, Claude) spawn this process and
6
+ * communicate via stdin/stdout using the MCP protocol.
7
+ *
8
+ * Tools: context, search, cypher, overview, explore, impact, analyze
9
+ * Resources: context, clusters, processes, schema, cluster/{name}, process/{name}
10
+ */
11
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
14
+ import { GITNEXUS_TOOLS } from './tools.js';
15
+ import { getResourceDefinitions, getResourceTemplates, readResource } from './resources.js';
16
+ export async function startMCPServer(backend) {
17
+ const server = new Server({
18
+ name: 'gitnexus',
19
+ version: '0.2.0',
20
+ }, {
21
+ capabilities: {
22
+ tools: {},
23
+ resources: {},
24
+ },
25
+ });
26
+ // Handle list resources request
27
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
28
+ const context = backend.context;
29
+ if (!context) {
30
+ return { resources: [] };
31
+ }
32
+ const resources = getResourceDefinitions(context.projectName);
33
+ return {
34
+ resources: resources.map(r => ({
35
+ uri: r.uri,
36
+ name: r.name,
37
+ description: r.description,
38
+ mimeType: r.mimeType,
39
+ })),
40
+ };
41
+ });
42
+ // Handle list resource templates request (for dynamic resources)
43
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
44
+ const templates = getResourceTemplates();
45
+ return {
46
+ resourceTemplates: templates.map(t => ({
47
+ uriTemplate: t.uriTemplate,
48
+ name: t.name,
49
+ description: t.description,
50
+ mimeType: t.mimeType,
51
+ })),
52
+ };
53
+ });
54
+ // Handle read resource request
55
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
56
+ const { uri } = request.params;
57
+ try {
58
+ const content = await readResource(uri, backend);
59
+ return {
60
+ contents: [
61
+ {
62
+ uri,
63
+ mimeType: 'text/yaml',
64
+ text: content,
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ catch (err) {
70
+ return {
71
+ contents: [
72
+ {
73
+ uri,
74
+ mimeType: 'text/plain',
75
+ text: `Error: ${err.message}`,
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ });
81
+ // Handle list tools request
82
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
83
+ tools: GITNEXUS_TOOLS.map((tool) => ({
84
+ name: tool.name,
85
+ description: tool.description,
86
+ inputSchema: tool.inputSchema,
87
+ })),
88
+ }));
89
+ // Handle tool calls
90
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
91
+ const { name, arguments: args } = request.params;
92
+ try {
93
+ const result = await backend.callTool(name, args);
94
+ return {
95
+ content: [
96
+ {
97
+ type: 'text',
98
+ text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ catch (error) {
104
+ const message = error instanceof Error ? error.message : 'Unknown error';
105
+ return {
106
+ content: [
107
+ {
108
+ type: 'text',
109
+ text: `Error: ${message}`,
110
+ },
111
+ ],
112
+ isError: true,
113
+ };
114
+ }
115
+ });
116
+ // Connect to stdio transport
117
+ const transport = new StdioServerTransport();
118
+ await server.connect(transport);
119
+ // Handle graceful shutdown
120
+ process.on('SIGINT', async () => {
121
+ await backend.disconnect();
122
+ await server.close();
123
+ process.exit(0);
124
+ });
125
+ process.on('SIGTERM', async () => {
126
+ await backend.disconnect();
127
+ await server.close();
128
+ process.exit(0);
129
+ });
130
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Staleness Check
3
+ *
4
+ * Checks if the GitNexus index is behind the current git HEAD.
5
+ * Returns a hint for the LLM to call analyze if stale.
6
+ */
7
+ export interface StalenessInfo {
8
+ isStale: boolean;
9
+ commitsBehind: number;
10
+ hint?: string;
11
+ }
12
+ /**
13
+ * Check how many commits the index is behind HEAD
14
+ */
15
+ export declare function checkStaleness(repoPath: string, lastCommit: string): StalenessInfo;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Staleness Check
3
+ *
4
+ * Checks if the GitNexus index is behind the current git HEAD.
5
+ * Returns a hint for the LLM to call analyze if stale.
6
+ */
7
+ import { execSync } from 'child_process';
8
+ /**
9
+ * Check how many commits the index is behind HEAD
10
+ */
11
+ export function checkStaleness(repoPath, lastCommit) {
12
+ try {
13
+ // Get count of commits between lastCommit and HEAD
14
+ const result = execSync(`git rev-list --count ${lastCommit}..HEAD`, { cwd: repoPath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
15
+ const commitsBehind = parseInt(result, 10) || 0;
16
+ if (commitsBehind > 0) {
17
+ return {
18
+ isStale: true,
19
+ commitsBehind,
20
+ hint: `⚠️ Index is ${commitsBehind} commit${commitsBehind > 1 ? 's' : ''} behind HEAD. Run analyze tool to update.`,
21
+ };
22
+ }
23
+ return { isStale: false, commitsBehind: 0 };
24
+ }
25
+ catch {
26
+ // If git command fails, assume not stale (fail open)
27
+ return { isStale: false, commitsBehind: 0 };
28
+ }
29
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * MCP Tool Definitions
3
+ *
4
+ * Defines the tools that GitNexus exposes to external AI agents.
5
+ * Only includes tools that provide unique value over native IDE capabilities.
6
+ */
7
+ export interface ToolDefinition {
8
+ name: string;
9
+ description: string;
10
+ inputSchema: {
11
+ type: 'object';
12
+ properties: Record<string, {
13
+ type: string;
14
+ description?: string;
15
+ default?: any;
16
+ items?: {
17
+ type: string;
18
+ };
19
+ enum?: string[];
20
+ }>;
21
+ required: string[];
22
+ };
23
+ }
24
+ export declare const GITNEXUS_TOOLS: ToolDefinition[];
@@ -0,0 +1,160 @@
1
+ /**
2
+ * MCP Tool Definitions
3
+ *
4
+ * Defines the tools that GitNexus exposes to external AI agents.
5
+ * Only includes tools that provide unique value over native IDE capabilities.
6
+ */
7
+ export const GITNEXUS_TOOLS = [
8
+ {
9
+ name: 'analyze',
10
+ description: `Index or re-index the current repository. Runs the full pipeline in-process.
11
+
12
+ Creates .gitnexus/ in repo root with:
13
+ - Knowledge graph (functions, classes, calls, imports)
14
+ - Full-text search indexes
15
+ - Community detection (Leiden)
16
+ - Process tracing
17
+ - Embeddings for semantic search
18
+
19
+ Run this when:
20
+ - First time using GitNexus on a repo
21
+ - After major code changes
22
+ - When staleness warning appears in gitnexus://context
23
+ - When 'not indexed' error appears
24
+
25
+ Note: This may take 30-120 seconds for large repos.`,
26
+ inputSchema: {
27
+ type: 'object',
28
+ properties: {
29
+ path: { type: 'string', description: 'Repo path (default: current directory)' },
30
+ force: { type: 'boolean', description: 'Re-index even if exists', default: false },
31
+ skipEmbeddings: { type: 'boolean', description: 'Skip embedding generation (faster)', default: false },
32
+ },
33
+ required: [],
34
+ },
35
+ },
36
+ {
37
+ name: 'search',
38
+ description: `Hybrid search (keyword + semantic) across the codebase.
39
+ Returns code nodes with cluster context and optional graph connections.
40
+
41
+ BETTER THAN IDE search because:
42
+ - Cluster context (which functional area each result belongs to)
43
+ - Relationship data (callers/callees with depth=full)
44
+ - Hybrid ranking (BM25 + semantic via Reciprocal Rank Fusion)
45
+
46
+ RETURNS: Array of {name, type, filePath, cluster?, connections[]?, fusedScore, searchSource}`,
47
+ inputSchema: {
48
+ type: 'object',
49
+ properties: {
50
+ query: { type: 'string', description: 'Natural language or keyword search query' },
51
+ limit: { type: 'number', description: 'Max results to return', default: 10 },
52
+ depth: { type: 'string', description: 'Result detail: "definitions" (symbols only) or "full" (with relationships)', enum: ['definitions', 'full'], default: 'definitions' },
53
+ },
54
+ required: ['query'],
55
+ },
56
+ },
57
+ {
58
+ name: 'cypher',
59
+ description: `Execute Cypher query against the code knowledge graph.
60
+
61
+ SCHEMA:
62
+ - Nodes: File, Folder, Function, Class, Interface, Method, Community, Process
63
+ - Edges via CodeRelation.type: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, CONTAINS, DEFINES, MEMBER_OF, STEP_IN_PROCESS
64
+
65
+ EXAMPLES:
66
+ • Find callers of a function:
67
+ MATCH (a)-[:CodeRelation {type: 'CALLS'}]->(b:Function {name: "validateUser"}) RETURN a.name, a.filePath
68
+
69
+ • Find all functions in a community:
70
+ MATCH (f:Function)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community {label: "Auth"}) RETURN f.name
71
+
72
+ • Find steps in a process:
73
+ MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process {label: "UserLogin"}) RETURN s.name, r.step ORDER BY r.step
74
+
75
+ TIPS:
76
+ - All relationships use CodeRelation table with 'type' property
77
+ - Community = functional cluster detected by Leiden algorithm
78
+ - Process = execution flow trace from entry point to terminal`,
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ query: { type: 'string', description: 'Cypher query to execute' },
83
+ },
84
+ required: ['query'],
85
+ },
86
+ },
87
+ {
88
+ name: 'explore',
89
+ description: `Deep dive on a symbol, cluster, or process.
90
+
91
+ TYPE: symbol | cluster | process
92
+
93
+ For SYMBOL: Shows cluster membership, process participation, callers/callees
94
+ For CLUSTER: Shows members, cohesion score, processes touching it
95
+ For PROCESS: Shows step-by-step trace, clusters traversed, entry/terminal points
96
+
97
+ Use after search to understand context of a specific node.`,
98
+ inputSchema: {
99
+ type: 'object',
100
+ properties: {
101
+ name: { type: 'string', description: 'Name of symbol, cluster, or process to explore' },
102
+ type: { type: 'string', description: 'Type: symbol, cluster, or process' },
103
+ },
104
+ required: ['name', 'type'],
105
+ },
106
+ },
107
+ {
108
+ name: 'overview',
109
+ description: `Get codebase map showing all clusters and processes.
110
+
111
+ Returns:
112
+ - All communities (clusters) with member counts and cohesion scores
113
+ - All processes with step counts and types (intra/cross-community)
114
+ - High-level architectural view
115
+
116
+ Use to understand overall codebase structure before diving deep.`,
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ showProcesses: { type: 'boolean', description: 'Include process list', default: true },
121
+ showClusters: { type: 'boolean', description: 'Include cluster list', default: true },
122
+ limit: { type: 'number', description: 'Max items per category', default: 20 },
123
+ },
124
+ required: [],
125
+ },
126
+ },
127
+ {
128
+ name: 'impact',
129
+ description: `Analyze the impact of changing a code element.
130
+ Returns all nodes affected by modifying the target, with distance, edge type, and confidence.
131
+
132
+ USE BEFORE making changes to understand ripple effects.
133
+
134
+ Output includes:
135
+ - Affected processes (with step positions)
136
+ - Affected clusters (direct/indirect)
137
+ - Risk assessment (critical/high/medium/low)
138
+ - Callers/dependents grouped by depth
139
+
140
+ EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS
141
+ Confidence: 100% = certain, <80% = fuzzy match
142
+
143
+ Depth groups:
144
+ - d=1: WILL BREAK (direct callers/importers)
145
+ - d=2: LIKELY AFFECTED (indirect)
146
+ - d=3: MAY NEED TESTING (transitive)`,
147
+ inputSchema: {
148
+ type: 'object',
149
+ properties: {
150
+ target: { type: 'string', description: 'Name of function, class, or file to analyze' },
151
+ direction: { type: 'string', description: 'upstream (what depends on this) or downstream (what this depends on)' },
152
+ maxDepth: { type: 'number', description: 'Max relationship depth (default: 3)', default: 3 },
153
+ relationTypes: { type: 'array', items: { type: 'string' }, description: 'Filter: CALLS, IMPORTS, EXTENDS, IMPLEMENTS (default: usage-based)' },
154
+ includeTests: { type: 'boolean', description: 'Include test files (default: false)' },
155
+ minConfidence: { type: 'number', description: 'Minimum confidence 0-1 (default: 0.7)' },
156
+ },
157
+ required: ['target', 'direction'],
158
+ },
159
+ },
160
+ ];
@@ -0,0 +1,6 @@
1
+ /**
2
+ * HTTP API Server
3
+ *
4
+ * REST API for browser-based clients to query the local .gitnexus/ index.
5
+ */
6
+ export declare const createServer: (port: number) => Promise<void>;