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
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # GitNexus
2
+
3
+ **Graph-powered code intelligence for AI agents.** Index any codebase into a knowledge graph, then query it via MCP or CLI.
4
+
5
+ Works with **Cursor**, **Claude Code**, **Windsurf**, **Cline**, **OpenCode**, and any MCP-compatible tool.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/gitnexus.svg)](https://www.npmjs.com/package/gitnexus)
8
+ [![License: PolyForm Noncommercial](https://img.shields.io/badge/License-PolyForm%20Noncommercial-blue.svg)](https://polyformproject.org/licenses/noncommercial/1.0.0/)
9
+
10
+ ---
11
+
12
+ ## Why?
13
+
14
+ AI coding tools don't understand your codebase structure. They edit a function without knowing 47 other functions depend on it. GitNexus fixes this by **precomputing every dependency, call chain, and relationship** into a queryable graph.
15
+
16
+ **One command to index. One MCP connection to give your AI agent full codebase awareness.**
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ # Index your repository
22
+ npx gitnexus analyze
23
+
24
+ # Start MCP server (for Cursor, Claude Code, etc.)
25
+ npx gitnexus mcp
26
+ ```
27
+
28
+ Or install globally:
29
+
30
+ ```bash
31
+ npm install -g gitnexus
32
+ gitnexus analyze
33
+ gitnexus mcp
34
+ ```
35
+
36
+ ## MCP Setup
37
+
38
+ ### Cursor / Windsurf
39
+
40
+ Add to your MCP config (`.cursor/mcp.json` or equivalent):
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "gitnexus": {
46
+ "command": "npx",
47
+ "args": ["-y", "gitnexus", "mcp"],
48
+ "env": {
49
+ "GITNEXUS_CWD": "/path/to/your/repo"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### Claude Code
57
+
58
+ ```bash
59
+ claude mcp add gitnexus -- npx -y gitnexus mcp
60
+ ```
61
+
62
+ ### OpenCode
63
+
64
+ Add to your `opencode.json`:
65
+
66
+ ```json
67
+ {
68
+ "mcp": {
69
+ "gitnexus": {
70
+ "command": "npx",
71
+ "args": ["-y", "gitnexus", "mcp"]
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ## What It Does
78
+
79
+ GitNexus indexes your codebase through 7 phases:
80
+
81
+ 1. **Structure** — File/folder tree
82
+ 2. **Parse** — AST extraction via Tree-sitter (9 languages)
83
+ 3. **Imports** — Resolve import paths (including TS path aliases, Rust modules, Java wildcards, Go packages)
84
+ 4. **Calls** — Function call resolution with confidence scoring (0.3-0.9)
85
+ 5. **Heritage** — Class extends/implements chains
86
+ 6. **Communities** — Leiden algorithm clusters related code into functional groups
87
+ 7. **Processes** — Entry point detection and execution flow tracing
88
+
89
+ The result is a **KuzuDB graph database** stored locally in `.gitnexus/` with full-text search and semantic embeddings.
90
+
91
+ ## MCP Tools
92
+
93
+ Your AI agent gets these tools automatically:
94
+
95
+ | Tool | What It Does | Example |
96
+ |------|-------------|---------|
97
+ | `search` | Hybrid search (BM25 + semantic) with cluster context | "Find authentication logic" |
98
+ | `overview` | List all clusters and processes | "Show me the codebase structure" |
99
+ | `explore` | Deep dive on a symbol, cluster, or process | "Tell me about UserService" |
100
+ | `impact` | Blast radius analysis | "What breaks if I change validateUser?" |
101
+ | `cypher` | Raw Cypher graph queries | Complex relationship queries |
102
+ | `read` | Smart file reader with fuzzy path matching | Read specific files |
103
+ | `grep` | Regex pattern search | Find TODOs, error codes |
104
+
105
+ ## MCP Resources
106
+
107
+ | Resource | Purpose |
108
+ |----------|---------|
109
+ | `gitnexus://context` | Codebase stats and overview |
110
+ | `gitnexus://clusters` | All functional clusters |
111
+ | `gitnexus://cluster/{name}` | Cluster members and details |
112
+ | `gitnexus://processes` | All execution flows |
113
+ | `gitnexus://process/{name}` | Full process trace |
114
+ | `gitnexus://schema` | Graph schema for Cypher queries |
115
+
116
+ ## CLI Commands
117
+
118
+ ```bash
119
+ gitnexus analyze [path] # Index a repository (or update stale index)
120
+ gitnexus analyze --force # Force full re-index
121
+ gitnexus mcp # Start MCP server (stdio)
122
+ gitnexus serve # Start HTTP server for web UI
123
+ gitnexus list # List all indexed repositories
124
+ gitnexus status # Show index status for current repo
125
+ gitnexus clean # Delete index for current repo
126
+ ```
127
+
128
+ ## Supported Languages
129
+
130
+ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust
131
+
132
+ ## How Impact Analysis Works
133
+
134
+ ```
135
+ gitnexus_impact("UserService", "upstream")
136
+
137
+ TARGET: Class UserService (src/services/user.ts)
138
+
139
+ UPSTREAM (what depends on this):
140
+ Depth 1 (direct callers):
141
+ handleLogin [CALLS 90%] → src/api/auth.ts:45
142
+ handleRegister [CALLS 90%] → src/api/auth.ts:78
143
+ Depth 2:
144
+ authRouter [IMPORTS] → src/routes/auth.ts
145
+
146
+ 8 files affected, 3 clusters touched
147
+ ```
148
+
149
+ Options: `maxDepth`, `minConfidence`, `relationTypes`, `includeTests`
150
+
151
+ ## Agent Skills
152
+
153
+ GitNexus ships with skill files that teach AI agents how to use the tools effectively:
154
+
155
+ - **Exploring** — Navigate unfamiliar code using the knowledge graph
156
+ - **Debugging** — Trace bugs through call chains
157
+ - **Impact Analysis** — Analyze blast radius before changes
158
+ - **Refactoring** — Plan safe refactors using dependency mapping
159
+
160
+ These are installed automatically to `.claude/skills/` when you run `gitnexus analyze`.
161
+
162
+ ## Requirements
163
+
164
+ - Node.js >= 18
165
+ - Git repository (uses git for commit tracking)
166
+
167
+ ## Privacy
168
+
169
+ - All processing happens locally on your machine
170
+ - No code is sent to any server
171
+ - Index stored in `.gitnexus/` inside your repo (gitignored)
172
+
173
+ ## Web UI
174
+
175
+ GitNexus also has a browser-based UI at [gitnexus.vercel.app](https://gitnexus.vercel.app) — 100% client-side, your code never leaves the browser.
176
+
177
+ ## License
178
+
179
+ [PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/)
180
+
181
+ Free for non-commercial use. Contact for commercial licensing.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * AI Context Generator
3
+ *
4
+ * Creates AGENTS.md and CLAUDE.md with full inline GitNexus context.
5
+ * AGENTS.md is the standard read by Cursor, Windsurf, OpenCode, Cline, etc.
6
+ * CLAUDE.md is for Claude Code which only reads that file.
7
+ */
8
+ interface RepoStats {
9
+ files?: number;
10
+ nodes?: number;
11
+ edges?: number;
12
+ communities?: number;
13
+ processes?: number;
14
+ }
15
+ /**
16
+ * Generate AI context files after indexing
17
+ */
18
+ export declare function generateAIContextFiles(repoPath: string, _storagePath: string, projectName: string, stats: RepoStats): Promise<{
19
+ files: string[];
20
+ }>;
21
+ export {};
@@ -0,0 +1,219 @@
1
+ /**
2
+ * AI Context Generator
3
+ *
4
+ * Creates AGENTS.md and CLAUDE.md with full inline GitNexus context.
5
+ * AGENTS.md is the standard read by Cursor, Windsurf, OpenCode, Cline, etc.
6
+ * CLAUDE.md is for Claude Code which only reads that file.
7
+ */
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ // ESM equivalent of __dirname
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const GITNEXUS_START_MARKER = '<!-- gitnexus:start -->';
15
+ const GITNEXUS_END_MARKER = '<!-- gitnexus:end -->';
16
+ /**
17
+ * Generate the full GitNexus context content (resources-first approach)
18
+ */
19
+ function generateGitNexusContent(projectName, stats) {
20
+ return `${GITNEXUS_START_MARKER}
21
+ # GitNexus MCP
22
+
23
+ This project is indexed by GitNexus, providing AI agents with deep code intelligence.
24
+
25
+ ## Project: ${projectName}
26
+
27
+ | Metric | Count |
28
+ |--------|-------|
29
+ | Files | ${stats.files || 0} |
30
+ | Symbols | ${stats.nodes || 0} |
31
+ | Relationships | ${stats.edges || 0} |
32
+ | Communities | ${stats.communities || 0} |
33
+ | Processes | ${stats.processes || 0} |
34
+
35
+ ## Quick Start
36
+
37
+ \`\`\`
38
+ 1. READ gitnexus://context → Get codebase overview (~150 tokens)
39
+ 2. READ gitnexus://clusters → See all functional clusters
40
+ 3. READ gitnexus://cluster/{name} → Deep dive on specific cluster
41
+ 4. gitnexus_search(query) → Find code by query
42
+ \`\`\`
43
+
44
+ ## Available Resources
45
+
46
+ | Resource | Purpose |
47
+ |----------|---------|
48
+ | \`gitnexus://context\` | Codebase stats, tools, and resources overview |
49
+ | \`gitnexus://clusters\` | All clusters with symbol counts and cohesion |
50
+ | \`gitnexus://cluster/{name}\` | Cluster members and details |
51
+ | \`gitnexus://processes\` | All execution flows with types |
52
+ | \`gitnexus://process/{name}\` | Full process trace with steps |
53
+ | \`gitnexus://schema\` | Graph schema for Cypher queries |
54
+
55
+ ## Available Tools
56
+
57
+ | Tool | Purpose | When to Use |
58
+ |------|---------|-------------|
59
+ | \`search\` | Semantic + keyword search | Finding code by query |
60
+ | \`overview\` | List clusters & processes | Understanding architecture |
61
+ | \`explore\` | Deep dive on symbol/cluster/process | Detailed investigation |
62
+ | \`impact\` | Blast radius analysis | Before making changes |
63
+ | \`cypher\` | Raw graph queries | Complex analysis |
64
+
65
+ ## Workflow Examples
66
+
67
+ ### Exploring the Codebase
68
+ \`\`\`
69
+ READ gitnexus://context → Stats and overview
70
+ READ gitnexus://clusters → Find relevant cluster
71
+ READ gitnexus://cluster/Auth → Explore Auth cluster
72
+ gitnexus_explore("validateUser", "symbol") → Detailed symbol info
73
+ \`\`\`
74
+
75
+ ### Planning a Change
76
+ \`\`\`
77
+ gitnexus_impact("UserService", "upstream") → See what breaks
78
+ READ gitnexus://processes → Check affected flows
79
+ gitnexus_explore("LoginFlow", "process") → Trace execution
80
+ \`\`\`
81
+
82
+ ## Graph Schema
83
+
84
+ **Nodes:** File, Function, Class, Interface, Method, Community, Process
85
+
86
+ **Relationships:** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS
87
+
88
+ \`\`\`cypher
89
+ // Example: Find callers of a function
90
+ MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
91
+ RETURN caller.name, caller.filePath
92
+ \`\`\`
93
+
94
+ ${GITNEXUS_END_MARKER}`;
95
+ }
96
+ /**
97
+ * Check if a file exists
98
+ */
99
+ async function fileExists(filePath) {
100
+ try {
101
+ await fs.access(filePath);
102
+ return true;
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Create or update GitNexus section in a file
110
+ * - If file doesn't exist: create with GitNexus content
111
+ * - If file exists without GitNexus section: append
112
+ * - If file exists with GitNexus section: replace that section
113
+ */
114
+ async function upsertGitNexusSection(filePath, content) {
115
+ const exists = await fileExists(filePath);
116
+ if (!exists) {
117
+ await fs.writeFile(filePath, content, 'utf-8');
118
+ return 'created';
119
+ }
120
+ const existingContent = await fs.readFile(filePath, 'utf-8');
121
+ // Check if GitNexus section already exists
122
+ const startIdx = existingContent.indexOf(GITNEXUS_START_MARKER);
123
+ const endIdx = existingContent.indexOf(GITNEXUS_END_MARKER);
124
+ if (startIdx !== -1 && endIdx !== -1) {
125
+ // Replace existing section
126
+ const before = existingContent.substring(0, startIdx);
127
+ const after = existingContent.substring(endIdx + GITNEXUS_END_MARKER.length);
128
+ const newContent = before + content + after;
129
+ await fs.writeFile(filePath, newContent.trim() + '\n', 'utf-8');
130
+ return 'updated';
131
+ }
132
+ // Append new section
133
+ const newContent = existingContent.trim() + '\n\n' + content + '\n';
134
+ await fs.writeFile(filePath, newContent, 'utf-8');
135
+ return 'appended';
136
+ }
137
+ /**
138
+ * Install GitNexus skills to .claude/skills/gitnexus/
139
+ * Works natively with Claude Code, Cursor, and GitHub Copilot
140
+ */
141
+ async function installSkills(repoPath) {
142
+ const skillsDir = path.join(repoPath, '.claude', 'skills', 'gitnexus');
143
+ const installedSkills = [];
144
+ // Skill definitions bundled with the package
145
+ const skills = [
146
+ {
147
+ name: 'exploring',
148
+ description: 'Navigate unfamiliar code using GitNexus knowledge graph',
149
+ },
150
+ {
151
+ name: 'debugging',
152
+ description: 'Trace bugs through call chains using knowledge graph',
153
+ },
154
+ {
155
+ name: 'impact-analysis',
156
+ description: 'Analyze blast radius before making code changes',
157
+ },
158
+ {
159
+ name: 'refactoring',
160
+ description: 'Plan safe refactors using blast radius and dependency mapping',
161
+ },
162
+ ];
163
+ for (const skill of skills) {
164
+ const skillDir = path.join(skillsDir, skill.name);
165
+ const skillPath = path.join(skillDir, 'SKILL.md');
166
+ try {
167
+ // Create skill directory
168
+ await fs.mkdir(skillDir, { recursive: true });
169
+ // Try to read from package skills directory
170
+ const packageSkillPath = path.join(__dirname, '..', '..', 'skills', `${skill.name}.md`);
171
+ let skillContent;
172
+ try {
173
+ skillContent = await fs.readFile(packageSkillPath, 'utf-8');
174
+ }
175
+ catch {
176
+ // Fallback: generate minimal skill content
177
+ skillContent = `---
178
+ name: gitnexus-${skill.name}
179
+ description: ${skill.description}
180
+ ---
181
+
182
+ # ${skill.name.charAt(0).toUpperCase() + skill.name.slice(1)}
183
+
184
+ ${skill.description}
185
+
186
+ Use GitNexus tools to accomplish this task.
187
+ `;
188
+ }
189
+ await fs.writeFile(skillPath, skillContent, 'utf-8');
190
+ installedSkills.push(skill.name);
191
+ }
192
+ catch (err) {
193
+ // Skip on error, don't fail the whole process
194
+ console.warn(`Warning: Could not install skill ${skill.name}:`, err);
195
+ }
196
+ }
197
+ return installedSkills;
198
+ }
199
+ /**
200
+ * Generate AI context files after indexing
201
+ */
202
+ export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats) {
203
+ const content = generateGitNexusContent(projectName, stats);
204
+ const createdFiles = [];
205
+ // Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Cline, etc.)
206
+ const agentsPath = path.join(repoPath, 'AGENTS.md');
207
+ const agentsResult = await upsertGitNexusSection(agentsPath, content);
208
+ createdFiles.push(`AGENTS.md (${agentsResult})`);
209
+ // Create CLAUDE.md (for Claude Code)
210
+ const claudePath = path.join(repoPath, 'CLAUDE.md');
211
+ const claudeResult = await upsertGitNexusSection(claudePath, content);
212
+ createdFiles.push(`CLAUDE.md (${claudeResult})`);
213
+ // Install skills to .claude/skills/gitnexus/
214
+ const installedSkills = await installSkills(repoPath);
215
+ if (installedSkills.length > 0) {
216
+ createdFiles.push(`.claude/skills/gitnexus/ (${installedSkills.length} skills)`);
217
+ }
218
+ return { files: createdFiles };
219
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Analyze Command
3
+ *
4
+ * Indexes a repository and stores the knowledge graph in .gitnexus/
5
+ */
6
+ export interface AnalyzeOptions {
7
+ force?: boolean;
8
+ skipEmbeddings?: boolean;
9
+ }
10
+ export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Analyze Command
3
+ *
4
+ * Indexes a repository and stores the knowledge graph in .gitnexus/
5
+ */
6
+ import path from 'path';
7
+ import ora from 'ora';
8
+ import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
9
+ import { initKuzu, loadGraphToKuzu, getKuzuStats, executeQuery, executeWithReusedStatement, closeKuzu, createFTSIndex } from '../core/kuzu/kuzu-adapter.js';
10
+ import { runEmbeddingPipeline } from '../core/embeddings/embedding-pipeline.js';
11
+ import { getStoragePaths, saveMeta, loadMeta, addToGitignore } from '../storage/repo-manager.js';
12
+ import { getCurrentCommit, isGitRepo, getGitRoot } from '../storage/git.js';
13
+ import { generateAIContextFiles } from './ai-context.js';
14
+ export const analyzeCommand = async (inputPath, options) => {
15
+ const spinner = ora('Checking repository...').start();
16
+ // If path provided, use it directly. Otherwise, find git root from cwd.
17
+ let repoPath;
18
+ if (inputPath) {
19
+ repoPath = path.resolve(inputPath);
20
+ }
21
+ else {
22
+ const gitRoot = getGitRoot(process.cwd());
23
+ if (!gitRoot) {
24
+ spinner.fail('Not inside a git repository');
25
+ process.exitCode = 1;
26
+ return;
27
+ }
28
+ repoPath = gitRoot;
29
+ }
30
+ if (!isGitRepo(repoPath)) {
31
+ spinner.fail('Not a git repository');
32
+ process.exitCode = 1;
33
+ return;
34
+ }
35
+ const { storagePath, kuzuPath } = getStoragePaths(repoPath);
36
+ const currentCommit = getCurrentCommit(repoPath);
37
+ const existingMeta = await loadMeta(storagePath);
38
+ // Skip if already indexed at same commit
39
+ if (existingMeta && !options?.force && existingMeta.lastCommit === currentCommit) {
40
+ spinner.succeed('Repository already up to date');
41
+ return;
42
+ }
43
+ // Run ingestion pipeline
44
+ spinner.text = 'Running ingestion pipeline...';
45
+ const pipelineResult = await runPipelineFromRepo(repoPath, (progress) => {
46
+ spinner.text = `${progress.phase}: ${progress.percent}%`;
47
+ });
48
+ // Load graph into KuzuDB
49
+ // Always start fresh - remove existing kuzu DB to avoid stale/corrupt data
50
+ spinner.text = 'Loading graph into KuzuDB...';
51
+ await closeKuzu();
52
+ // Kuzu 0.11 stores databases as: <name> (main file) + <name>.wal (WAL file)
53
+ // BOTH must be deleted or kuzu will find the orphaned WAL and corrupt the database
54
+ const fsClean = await import('fs/promises');
55
+ const kuzuFiles = [kuzuPath, `${kuzuPath}.wal`, `${kuzuPath}.lock`];
56
+ for (const f of kuzuFiles) {
57
+ try {
58
+ await fsClean.rm(f, { recursive: true, force: true });
59
+ }
60
+ catch { /* may not exist */ }
61
+ }
62
+ await initKuzu(kuzuPath);
63
+ await loadGraphToKuzu(pipelineResult.graph, pipelineResult.fileContents, storagePath);
64
+ // Create FTS indexes for keyword search
65
+ // Indexes searchable content on: File, Function, Class, Method
66
+ spinner.text = 'Creating FTS indexes...';
67
+ try {
68
+ await createFTSIndex('File', 'file_fts', ['name', 'content']);
69
+ await createFTSIndex('Function', 'function_fts', ['name', 'content']);
70
+ await createFTSIndex('Class', 'class_fts', ['name', 'content']);
71
+ await createFTSIndex('Method', 'method_fts', ['name', 'content']);
72
+ }
73
+ catch (e) {
74
+ // FTS index creation may fail if tables are empty (no data for that type)
75
+ console.error('Note: Some FTS indexes may not have been created:', e.message);
76
+ }
77
+ // Generate embeddings
78
+ if (!options?.skipEmbeddings) {
79
+ spinner.text = 'Generating embeddings...';
80
+ await runEmbeddingPipeline(executeQuery, executeWithReusedStatement, (progress) => {
81
+ spinner.text = `Embeddings: ${progress.percent}%`;
82
+ });
83
+ }
84
+ // Save metadata
85
+ const stats = await getKuzuStats();
86
+ await saveMeta(storagePath, {
87
+ repoPath,
88
+ lastCommit: currentCommit,
89
+ indexedAt: new Date().toISOString(),
90
+ stats: {
91
+ files: pipelineResult.fileContents.size,
92
+ nodes: stats.nodes,
93
+ edges: stats.edges,
94
+ communities: pipelineResult.communityResult?.stats.totalCommunities,
95
+ processes: pipelineResult.processResult?.stats.totalProcesses,
96
+ },
97
+ });
98
+ // Add .gitnexus to .gitignore
99
+ await addToGitignore(repoPath);
100
+ // Generate AI context files
101
+ const projectName = path.basename(repoPath);
102
+ const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
103
+ files: pipelineResult.fileContents.size,
104
+ nodes: stats.nodes,
105
+ edges: stats.edges,
106
+ communities: pipelineResult.communityResult?.stats.totalCommunities,
107
+ processes: pipelineResult.processResult?.stats.totalProcesses,
108
+ });
109
+ // Close database
110
+ await closeKuzu();
111
+ spinner.succeed('Repository indexed successfully');
112
+ console.log(` Path: ${repoPath}`);
113
+ console.log(` Storage: ${storagePath}`);
114
+ console.log(` Stats: ${stats.nodes} nodes, ${stats.edges} edges`);
115
+ if (aiContext.files.length > 0) {
116
+ console.log(` AI Context: ${aiContext.files.join(', ')}`);
117
+ }
118
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Clean Command
3
+ *
4
+ * Removes the .gitnexus index from the current repository.
5
+ */
6
+ export declare const cleanCommand: (options?: {
7
+ force?: boolean;
8
+ }) => Promise<void>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Clean Command
3
+ *
4
+ * Removes the .gitnexus index from the current repository.
5
+ */
6
+ import fs from 'fs/promises';
7
+ import { findRepo } from '../storage/repo-manager.js';
8
+ export const cleanCommand = async (options) => {
9
+ const cwd = process.cwd();
10
+ const repo = await findRepo(cwd);
11
+ if (!repo) {
12
+ console.log('No indexed repository found in this directory.');
13
+ return;
14
+ }
15
+ const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath;
16
+ if (!options?.force) {
17
+ console.log(`⚠️ This will delete the GitNexus index for: ${repoName}`);
18
+ console.log(` Path: ${repo.storagePath}`);
19
+ console.log('\nRun with --force to confirm deletion.');
20
+ return;
21
+ }
22
+ try {
23
+ await fs.rm(repo.storagePath, { recursive: true, force: true });
24
+ console.log(`🗑️ Deleted: ${repo.storagePath}`);
25
+ }
26
+ catch (err) {
27
+ console.error('Failed to delete:', err);
28
+ }
29
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { analyzeCommand } from './analyze.js';
4
+ import { serveCommand } from './serve.js';
5
+ import { listCommand } from './list.js';
6
+ import { statusCommand } from './status.js';
7
+ import { mcpCommand } from './mcp.js';
8
+ import { cleanCommand } from './clean.js';
9
+ const program = new Command();
10
+ program
11
+ .name('gitnexus')
12
+ .description('GitNexus local CLI and MCP server')
13
+ .version('1.0.0');
14
+ program
15
+ .command('analyze [path]')
16
+ .description('Index a repository (full analysis)')
17
+ .option('-f, --force', 'Force full re-index even if up to date')
18
+ .option('--skip-embeddings', 'Skip embedding generation (faster)')
19
+ .action(analyzeCommand);
20
+ program
21
+ .command('serve')
22
+ .description('Start local HTTP server for web UI connection')
23
+ .option('-p, --port <port>', 'Port number', '4747')
24
+ .action(serveCommand);
25
+ program
26
+ .command('mcp')
27
+ .description('Start MCP server (stdio)')
28
+ .action(mcpCommand);
29
+ program
30
+ .command('list')
31
+ .description('List indexed repositories')
32
+ .action(listCommand);
33
+ program
34
+ .command('status')
35
+ .description('Show index status for current repo')
36
+ .action(statusCommand);
37
+ program
38
+ .command('clean')
39
+ .description('Delete GitNexus index for current repo')
40
+ .option('-f, --force', 'Skip confirmation prompt')
41
+ .action(cleanCommand);
42
+ program.parse(process.argv);
@@ -0,0 +1,6 @@
1
+ /**
2
+ * List Command
3
+ *
4
+ * Shows info about the indexed repo in the current directory.
5
+ */
6
+ export declare const listCommand: () => Promise<void>;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * List Command
3
+ *
4
+ * Shows info about the indexed repo in the current directory.
5
+ */
6
+ import { findRepo } from '../storage/repo-manager.js';
7
+ export const listCommand = async () => {
8
+ const cwd = process.cwd();
9
+ const repo = await findRepo(cwd);
10
+ if (!repo) {
11
+ console.log('No indexed repository found in this directory.');
12
+ console.log('Run `gitnexus analyze` to index your codebase.');
13
+ return;
14
+ }
15
+ const stats = repo.meta.stats || {};
16
+ const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath;
17
+ const indexedDate = new Date(repo.meta.indexedAt).toLocaleString();
18
+ console.log(`\n📁 ${repoName}`);
19
+ console.log(` Path: ${repo.repoPath}`);
20
+ console.log(` Indexed: ${indexedDate}`);
21
+ console.log(` Stats: ${stats.files ?? 0} files, ${stats.nodes ?? 0} nodes, ${stats.edges ?? 0} edges`);
22
+ console.log(` Commit: ${repo.meta.lastCommit?.slice(0, 7) || 'unknown'}`);
23
+ if (stats.communities)
24
+ console.log(` Communities: ${stats.communities}`);
25
+ if (stats.processes)
26
+ console.log(` Processes: ${stats.processes}`);
27
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP Command
3
+ *
4
+ * Starts the MCP server in standalone mode using local .gitnexus/ index.
5
+ * Auto-detects repository by searching for .gitnexus/ folder.
6
+ */
7
+ export declare const mcpCommand: () => Promise<void>;