gitnexus 1.4.10 → 1.5.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 (186) hide show
  1. package/README.md +6 -5
  2. package/dist/cli/ai-context.d.ts +4 -1
  3. package/dist/cli/ai-context.js +19 -11
  4. package/dist/cli/analyze.d.ts +6 -0
  5. package/dist/cli/analyze.js +105 -251
  6. package/dist/cli/eval-server.js +20 -11
  7. package/dist/cli/index-repo.js +20 -22
  8. package/dist/cli/index.js +8 -7
  9. package/dist/cli/mcp.js +1 -1
  10. package/dist/cli/serve.js +29 -1
  11. package/dist/cli/setup.js +9 -9
  12. package/dist/cli/skill-gen.js +15 -9
  13. package/dist/cli/wiki.d.ts +2 -0
  14. package/dist/cli/wiki.js +141 -26
  15. package/dist/config/ignore-service.js +102 -22
  16. package/dist/config/supported-languages.d.ts +8 -42
  17. package/dist/config/supported-languages.js +8 -43
  18. package/dist/core/augmentation/engine.js +19 -7
  19. package/dist/core/embeddings/embedder.js +19 -15
  20. package/dist/core/embeddings/embedding-pipeline.js +6 -6
  21. package/dist/core/embeddings/http-client.js +3 -3
  22. package/dist/core/embeddings/text-generator.js +9 -24
  23. package/dist/core/embeddings/types.d.ts +1 -1
  24. package/dist/core/embeddings/types.js +1 -7
  25. package/dist/core/graph/graph.js +6 -2
  26. package/dist/core/graph/types.d.ts +9 -59
  27. package/dist/core/ingestion/ast-cache.js +3 -3
  28. package/dist/core/ingestion/call-processor.d.ts +20 -2
  29. package/dist/core/ingestion/call-processor.js +347 -144
  30. package/dist/core/ingestion/call-routing.js +10 -4
  31. package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
  32. package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
  33. package/dist/core/ingestion/call-sites/java.d.ts +9 -0
  34. package/dist/core/ingestion/call-sites/java.js +30 -0
  35. package/dist/core/ingestion/cluster-enricher.js +6 -8
  36. package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
  37. package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
  38. package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
  39. package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
  40. package/dist/core/ingestion/cobol-processor.js +102 -56
  41. package/dist/core/ingestion/community-processor.js +21 -15
  42. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
  43. package/dist/core/ingestion/entry-point-scoring.js +5 -6
  44. package/dist/core/ingestion/export-detection.js +32 -9
  45. package/dist/core/ingestion/field-extractor.d.ts +1 -1
  46. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
  48. package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
  49. package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
  50. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
  51. package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
  52. package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
  53. package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
  54. package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
  55. package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
  56. package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
  57. package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
  58. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
  59. package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
  60. package/dist/core/ingestion/field-extractors/generic.js +6 -0
  61. package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
  62. package/dist/core/ingestion/field-extractors/typescript.js +1 -1
  63. package/dist/core/ingestion/field-types.d.ts +4 -2
  64. package/dist/core/ingestion/filesystem-walker.js +3 -3
  65. package/dist/core/ingestion/framework-detection.d.ts +1 -1
  66. package/dist/core/ingestion/framework-detection.js +355 -85
  67. package/dist/core/ingestion/heritage-processor.d.ts +24 -0
  68. package/dist/core/ingestion/heritage-processor.js +99 -8
  69. package/dist/core/ingestion/import-processor.js +44 -15
  70. package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
  71. package/dist/core/ingestion/import-resolvers/dart.js +1 -1
  72. package/dist/core/ingestion/import-resolvers/go.js +4 -2
  73. package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
  74. package/dist/core/ingestion/import-resolvers/php.js +4 -4
  75. package/dist/core/ingestion/import-resolvers/python.js +1 -1
  76. package/dist/core/ingestion/import-resolvers/rust.js +9 -3
  77. package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
  78. package/dist/core/ingestion/import-resolvers/standard.js +6 -5
  79. package/dist/core/ingestion/import-resolvers/swift.js +2 -1
  80. package/dist/core/ingestion/import-resolvers/utils.js +26 -7
  81. package/dist/core/ingestion/language-config.js +5 -4
  82. package/dist/core/ingestion/language-provider.d.ts +7 -2
  83. package/dist/core/ingestion/languages/c-cpp.js +106 -21
  84. package/dist/core/ingestion/languages/cobol.js +1 -1
  85. package/dist/core/ingestion/languages/csharp.js +96 -19
  86. package/dist/core/ingestion/languages/dart.js +23 -7
  87. package/dist/core/ingestion/languages/go.js +1 -1
  88. package/dist/core/ingestion/languages/index.d.ts +1 -1
  89. package/dist/core/ingestion/languages/index.js +2 -3
  90. package/dist/core/ingestion/languages/java.js +4 -1
  91. package/dist/core/ingestion/languages/kotlin.js +60 -13
  92. package/dist/core/ingestion/languages/php.js +102 -25
  93. package/dist/core/ingestion/languages/python.js +28 -5
  94. package/dist/core/ingestion/languages/ruby.js +56 -14
  95. package/dist/core/ingestion/languages/rust.js +55 -11
  96. package/dist/core/ingestion/languages/swift.js +112 -27
  97. package/dist/core/ingestion/languages/typescript.js +95 -19
  98. package/dist/core/ingestion/markdown-processor.js +5 -5
  99. package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
  100. package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
  101. package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
  102. package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
  103. package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
  104. package/dist/core/ingestion/method-extractors/generic.js +137 -0
  105. package/dist/core/ingestion/method-types.d.ts +61 -0
  106. package/dist/core/ingestion/method-types.js +2 -0
  107. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  108. package/dist/core/ingestion/mro-processor.js +12 -8
  109. package/dist/core/ingestion/named-binding-processor.js +2 -2
  110. package/dist/core/ingestion/named-bindings/rust.js +3 -1
  111. package/dist/core/ingestion/parsing-processor.js +74 -24
  112. package/dist/core/ingestion/pipeline.d.ts +2 -1
  113. package/dist/core/ingestion/pipeline.js +208 -102
  114. package/dist/core/ingestion/process-processor.js +12 -10
  115. package/dist/core/ingestion/resolution-context.js +3 -3
  116. package/dist/core/ingestion/route-extractors/middleware.js +31 -7
  117. package/dist/core/ingestion/route-extractors/php.js +2 -1
  118. package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
  119. package/dist/core/ingestion/structure-processor.d.ts +1 -1
  120. package/dist/core/ingestion/structure-processor.js +4 -4
  121. package/dist/core/ingestion/symbol-table.d.ts +1 -1
  122. package/dist/core/ingestion/symbol-table.js +22 -6
  123. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  124. package/dist/core/ingestion/tree-sitter-queries.js +1 -1
  125. package/dist/core/ingestion/type-env.d.ts +2 -2
  126. package/dist/core/ingestion/type-env.js +75 -50
  127. package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
  128. package/dist/core/ingestion/type-extractors/csharp.js +24 -14
  129. package/dist/core/ingestion/type-extractors/dart.js +6 -8
  130. package/dist/core/ingestion/type-extractors/go.js +7 -6
  131. package/dist/core/ingestion/type-extractors/jvm.js +10 -21
  132. package/dist/core/ingestion/type-extractors/php.js +26 -13
  133. package/dist/core/ingestion/type-extractors/python.js +11 -15
  134. package/dist/core/ingestion/type-extractors/ruby.js +8 -3
  135. package/dist/core/ingestion/type-extractors/rust.js +6 -8
  136. package/dist/core/ingestion/type-extractors/shared.js +134 -50
  137. package/dist/core/ingestion/type-extractors/swift.js +16 -13
  138. package/dist/core/ingestion/type-extractors/typescript.js +23 -15
  139. package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
  140. package/dist/core/ingestion/utils/ast-helpers.js +72 -35
  141. package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
  142. package/dist/core/ingestion/utils/call-analysis.js +96 -49
  143. package/dist/core/ingestion/utils/event-loop.js +1 -1
  144. package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
  145. package/dist/core/ingestion/workers/parse-worker.js +364 -84
  146. package/dist/core/ingestion/workers/worker-pool.js +5 -10
  147. package/dist/core/lbug/csv-generator.js +54 -15
  148. package/dist/core/lbug/lbug-adapter.d.ts +5 -0
  149. package/dist/core/lbug/lbug-adapter.js +86 -23
  150. package/dist/core/lbug/schema.d.ts +3 -6
  151. package/dist/core/lbug/schema.js +6 -30
  152. package/dist/core/run-analyze.d.ts +49 -0
  153. package/dist/core/run-analyze.js +257 -0
  154. package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
  155. package/dist/core/tree-sitter/parser-loader.js +1 -1
  156. package/dist/core/wiki/cursor-client.js +2 -7
  157. package/dist/core/wiki/generator.js +38 -23
  158. package/dist/core/wiki/graph-queries.js +10 -10
  159. package/dist/core/wiki/html-viewer.js +7 -3
  160. package/dist/core/wiki/llm-client.d.ts +23 -2
  161. package/dist/core/wiki/llm-client.js +96 -26
  162. package/dist/core/wiki/prompts.js +7 -6
  163. package/dist/mcp/core/embedder.js +1 -1
  164. package/dist/mcp/core/lbug-adapter.d.ts +4 -1
  165. package/dist/mcp/core/lbug-adapter.js +17 -7
  166. package/dist/mcp/local/local-backend.js +247 -95
  167. package/dist/mcp/resources.js +14 -6
  168. package/dist/mcp/server.js +13 -5
  169. package/dist/mcp/staleness.js +5 -1
  170. package/dist/mcp/tools.js +100 -23
  171. package/dist/server/analyze-job.d.ts +53 -0
  172. package/dist/server/analyze-job.js +146 -0
  173. package/dist/server/analyze-worker.d.ts +13 -0
  174. package/dist/server/analyze-worker.js +59 -0
  175. package/dist/server/api.js +795 -44
  176. package/dist/server/git-clone.d.ts +25 -0
  177. package/dist/server/git-clone.js +91 -0
  178. package/dist/storage/git.js +1 -3
  179. package/dist/storage/repo-manager.d.ts +5 -2
  180. package/dist/storage/repo-manager.js +4 -4
  181. package/dist/types/pipeline.d.ts +1 -21
  182. package/dist/types/pipeline.js +1 -18
  183. package/hooks/claude/gitnexus-hook.cjs +52 -22
  184. package/package.json +3 -2
  185. package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
  186. package/dist/core/ingestion/utils/language-detection.js +0 -70
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Git Clone Utility
3
+ *
4
+ * Shallow-clones repositories into ~/.gitnexus/repos/{name}/.
5
+ * If already cloned, does git pull instead.
6
+ */
7
+ /** Extract the repository name from a git URL (HTTPS or SSH). */
8
+ export declare function extractRepoName(url: string): string;
9
+ /** Get the clone target directory for a repo name. */
10
+ export declare function getCloneDir(repoName: string): string;
11
+ /**
12
+ * Validate a git URL to prevent SSRF attacks.
13
+ * Only allows https:// and http:// schemes. Blocks private/internal addresses.
14
+ */
15
+ export declare function validateGitUrl(url: string): void;
16
+ export interface CloneProgress {
17
+ phase: 'cloning' | 'pulling';
18
+ message: string;
19
+ }
20
+ /**
21
+ * Clone or pull a git repository.
22
+ * If targetDir doesn't exist: git clone --depth 1
23
+ * If targetDir exists with .git: git pull --ff-only
24
+ */
25
+ export declare function cloneOrPull(url: string, targetDir: string, onProgress?: (progress: CloneProgress) => void): Promise<string>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Git Clone Utility
3
+ *
4
+ * Shallow-clones repositories into ~/.gitnexus/repos/{name}/.
5
+ * If already cloned, does git pull instead.
6
+ */
7
+ import { spawn } from 'child_process';
8
+ import path from 'path';
9
+ import os from 'os';
10
+ import fs from 'fs/promises';
11
+ /** Extract the repository name from a git URL (HTTPS or SSH). */
12
+ export function extractRepoName(url) {
13
+ const cleaned = url.replace(/\/+$/, '');
14
+ const lastSegment = cleaned.split(/[/:]/).pop() || 'unknown';
15
+ return lastSegment.replace(/\.git$/, '');
16
+ }
17
+ /** Get the clone target directory for a repo name. */
18
+ export function getCloneDir(repoName) {
19
+ return path.join(os.homedir(), '.gitnexus', 'repos', repoName);
20
+ }
21
+ /**
22
+ * Validate a git URL to prevent SSRF attacks.
23
+ * Only allows https:// and http:// schemes. Blocks private/internal addresses.
24
+ */
25
+ export function validateGitUrl(url) {
26
+ let parsed;
27
+ try {
28
+ parsed = new URL(url);
29
+ }
30
+ catch {
31
+ throw new Error('Invalid URL');
32
+ }
33
+ if (!['https:', 'http:'].includes(parsed.protocol)) {
34
+ throw new Error('Only https:// and http:// git URLs are allowed');
35
+ }
36
+ const host = parsed.hostname.toLowerCase();
37
+ if (host === 'localhost' ||
38
+ host === '[::1]' ||
39
+ /^127\./.test(host) ||
40
+ /^10\./.test(host) ||
41
+ /^172\.(1[6-9]|2\d|3[01])\./.test(host) ||
42
+ /^192\.168\./.test(host) ||
43
+ /^169\.254\./.test(host) ||
44
+ /^0\./.test(host)) {
45
+ throw new Error('Cloning from private/internal addresses is not allowed');
46
+ }
47
+ }
48
+ /**
49
+ * Clone or pull a git repository.
50
+ * If targetDir doesn't exist: git clone --depth 1
51
+ * If targetDir exists with .git: git pull --ff-only
52
+ */
53
+ export async function cloneOrPull(url, targetDir, onProgress) {
54
+ const exists = await fs.access(path.join(targetDir, '.git')).then(() => true, () => false);
55
+ if (exists) {
56
+ onProgress?.({ phase: 'pulling', message: 'Pulling latest changes...' });
57
+ await runGit(['pull', '--ff-only'], targetDir);
58
+ }
59
+ else {
60
+ validateGitUrl(url);
61
+ await fs.mkdir(path.dirname(targetDir), { recursive: true });
62
+ onProgress?.({ phase: 'cloning', message: `Cloning ${url}...` });
63
+ await runGit(['clone', '--depth', '1', url, targetDir]);
64
+ }
65
+ return targetDir;
66
+ }
67
+ function runGit(args, cwd) {
68
+ return new Promise((resolve, reject) => {
69
+ const proc = spawn('git', args, {
70
+ cwd,
71
+ stdio: ['ignore', 'pipe', 'pipe'],
72
+ });
73
+ let stderr = '';
74
+ proc.stderr.on('data', (chunk) => {
75
+ stderr += chunk;
76
+ });
77
+ proc.on('close', (code) => {
78
+ if (code === 0)
79
+ resolve();
80
+ else {
81
+ // Log full stderr internally but don't expose it to API callers (SSRF mitigation)
82
+ if (stderr.trim())
83
+ console.error(`git ${args[0]} stderr: ${stderr.trim()}`);
84
+ reject(new Error(`git ${args[0]} failed (exit code ${code})`));
85
+ }
86
+ });
87
+ proc.on('error', (err) => {
88
+ reject(new Error(`Failed to spawn git: ${err.message}`));
89
+ });
90
+ });
91
+ }
@@ -24,9 +24,7 @@ export const getCurrentCommit = (repoPath) => {
24
24
  */
25
25
  export const getGitRoot = (fromPath) => {
26
26
  try {
27
- const raw = execSync('git rev-parse --show-toplevel', { cwd: fromPath })
28
- .toString()
29
- .trim();
27
+ const raw = execSync('git rev-parse --show-toplevel', { cwd: fromPath }).toString().trim();
30
28
  // On Windows, git returns /d/Projects/Foo — path.resolve normalizes to D:\Projects\Foo
31
29
  return path.resolve(raw);
32
30
  }
@@ -119,13 +119,16 @@ export declare const unregisterRepo: (repoPath: string) => Promise<void>;
119
119
  export declare const listRegisteredRepos: (opts?: {
120
120
  validate?: boolean;
121
121
  }) => Promise<RegistryEntry[]>;
122
- export type LLMProvider = 'openai' | 'cursor';
123
122
  export interface CLIConfig {
124
123
  apiKey?: string;
125
124
  model?: string;
126
125
  baseUrl?: string;
127
- provider?: LLMProvider;
126
+ provider?: 'openai' | 'openrouter' | 'azure' | 'custom' | 'cursor';
128
127
  cursorModel?: string;
128
+ /** Azure api-version query param (e.g. '2024-10-21'). Only used when provider is 'azure'. */
129
+ apiVersion?: string;
130
+ /** Set true when the deployment is a reasoning model (o1, o3, o4-mini). Auto-detected for OpenAI; must be set for Azure deployments. */
131
+ isReasoningModel?: boolean;
129
132
  }
130
133
  /**
131
134
  * Get the path to the global CLI config file
@@ -208,9 +208,7 @@ export const registerRepo = async (repoPath, meta) => {
208
208
  const existing = entries.findIndex((e) => {
209
209
  const a = path.resolve(e.path);
210
210
  const b = resolved;
211
- return process.platform === 'win32'
212
- ? a.toLowerCase() === b.toLowerCase()
213
- : a === b;
211
+ return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
214
212
  });
215
213
  const entry = {
216
214
  name,
@@ -294,6 +292,8 @@ export const saveCLIConfig = async (config) => {
294
292
  try {
295
293
  await fs.chmod(configPath, 0o600);
296
294
  }
297
- catch { /* best-effort */ }
295
+ catch {
296
+ /* best-effort */
297
+ }
298
298
  }
299
299
  };
@@ -1,18 +1,6 @@
1
- import { GraphNode, GraphRelationship, KnowledgeGraph } from '../core/graph/types.js';
1
+ import type { KnowledgeGraph } from '../core/graph/types.js';
2
2
  import { CommunityDetectionResult } from '../core/ingestion/community-processor.js';
3
3
  import { ProcessDetectionResult } from '../core/ingestion/process-processor.js';
4
- export type PipelinePhase = 'idle' | 'extracting' | 'structure' | 'parsing' | 'imports' | 'calls' | 'heritage' | 'communities' | 'processes' | 'enriching' | 'complete' | 'error';
5
- export interface PipelineProgress {
6
- phase: PipelinePhase;
7
- percent: number;
8
- message: string;
9
- detail?: string;
10
- stats?: {
11
- filesProcessed: number;
12
- totalFiles: number;
13
- nodesCreated: number;
14
- };
15
- }
16
4
  export interface PipelineResult {
17
5
  graph: KnowledgeGraph;
18
6
  /** Absolute path to the repo root — used for lazy file reads during LadybugDB loading */
@@ -22,11 +10,3 @@ export interface PipelineResult {
22
10
  communityResult?: CommunityDetectionResult;
23
11
  processResult?: ProcessDetectionResult;
24
12
  }
25
- export interface SerializablePipelineResult {
26
- nodes: GraphNode[];
27
- relationships: GraphRelationship[];
28
- repoPath: string;
29
- totalFileCount: number;
30
- }
31
- export declare const serializePipelineResult: (result: PipelineResult) => SerializablePipelineResult;
32
- export declare const deserializePipelineResult: (serialized: SerializablePipelineResult, createGraph: () => KnowledgeGraph) => PipelineResult;
@@ -1,18 +1 @@
1
- // Helper to convert PipelineResult to serializable format
2
- export const serializePipelineResult = (result) => ({
3
- nodes: [...result.graph.iterNodes()],
4
- relationships: [...result.graph.iterRelationships()],
5
- repoPath: result.repoPath,
6
- totalFileCount: result.totalFileCount,
7
- });
8
- // Helper to reconstruct from serializable format (used in main thread)
9
- export const deserializePipelineResult = (serialized, createGraph) => {
10
- const graph = createGraph();
11
- serialized.nodes.forEach(node => graph.addNode(node));
12
- serialized.relationships.forEach(rel => graph.addRelationship(rel));
13
- return {
14
- graph,
15
- repoPath: serialized.repoPath,
16
- totalFileCount: serialized.totalFileCount,
17
- };
18
- };
1
+ export {};
@@ -64,10 +64,26 @@ function extractPattern(toolName, toolInput) {
64
64
  const tokens = cmd.split(/\s+/);
65
65
  let foundCmd = false;
66
66
  let skipNext = false;
67
- const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']);
67
+ const flagsWithValues = new Set([
68
+ '-e',
69
+ '-f',
70
+ '-m',
71
+ '-A',
72
+ '-B',
73
+ '-C',
74
+ '-g',
75
+ '--glob',
76
+ '-t',
77
+ '--type',
78
+ '--include',
79
+ '--exclude',
80
+ ]);
68
81
 
69
82
  for (const token of tokens) {
70
- if (skipNext) { skipNext = false; continue; }
83
+ if (skipNext) {
84
+ skipNext = false;
85
+ continue;
86
+ }
71
87
  if (!foundCmd) {
72
88
  if (/\brg$|\bgrep$/.test(token)) foundCmd = true;
73
89
  continue;
@@ -110,18 +126,20 @@ function resolveCliPath() {
110
126
  function runGitNexusCli(cliPath, args, cwd, timeout) {
111
127
  const isWin = process.platform === 'win32';
112
128
  if (cliPath) {
113
- return spawnSync(
114
- process.execPath,
115
- [cliPath, ...args],
116
- { encoding: 'utf-8', timeout, cwd, stdio: ['pipe', 'pipe', 'pipe'] }
117
- );
129
+ return spawnSync(process.execPath, [cliPath, ...args], {
130
+ encoding: 'utf-8',
131
+ timeout,
132
+ cwd,
133
+ stdio: ['pipe', 'pipe', 'pipe'],
134
+ });
118
135
  }
119
136
  // 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
- );
137
+ return spawnSync(isWin ? 'npx.cmd' : 'npx', ['-y', 'gitnexus', ...args], {
138
+ encoding: 'utf-8',
139
+ timeout: timeout + 5000,
140
+ cwd,
141
+ stdio: ['pipe', 'pipe', 'pipe'],
142
+ });
125
143
  }
126
144
 
127
145
  /**
@@ -147,7 +165,9 @@ function handlePreToolUse(input) {
147
165
  if (!child.error && child.status === 0) {
148
166
  result = child.stderr || '';
149
167
  }
150
- } catch { /* graceful failure */ }
168
+ } catch {
169
+ /* graceful failure */
170
+ }
151
171
 
152
172
  if (result && result.trim()) {
153
173
  sendHookResponse('PreToolUse', result.trim());
@@ -158,9 +178,11 @@ function handlePreToolUse(input) {
158
178
  * Emit a PostToolUse hook response with additional context for the agent.
159
179
  */
160
180
  function sendHookResponse(hookEventName, message) {
161
- console.log(JSON.stringify({
162
- hookSpecificOutput: { hookEventName, additionalContext: message }
163
- }));
181
+ console.log(
182
+ JSON.stringify({
183
+ hookSpecificOutput: { hookEventName, additionalContext: message },
184
+ }),
185
+ );
164
186
  }
165
187
 
166
188
  /**
@@ -192,10 +214,15 @@ function handlePostToolUse(input) {
192
214
  let currentHead = '';
193
215
  try {
194
216
  const headResult = spawnSync('git', ['rev-parse', 'HEAD'], {
195
- encoding: 'utf-8', timeout: 3000, cwd, stdio: ['pipe', 'pipe', 'pipe'],
217
+ encoding: 'utf-8',
218
+ timeout: 3000,
219
+ cwd,
220
+ stdio: ['pipe', 'pipe', 'pipe'],
196
221
  });
197
222
  currentHead = (headResult.stdout || '').trim();
198
- } catch { return; }
223
+ } catch {
224
+ return;
225
+ }
199
226
 
200
227
  if (!currentHead) return;
201
228
 
@@ -204,16 +231,19 @@ function handlePostToolUse(input) {
204
231
  try {
205
232
  const meta = JSON.parse(fs.readFileSync(path.join(gitNexusDir, 'meta.json'), 'utf-8'));
206
233
  lastCommit = meta.lastCommit || '';
207
- hadEmbeddings = (meta.stats && meta.stats.embeddings > 0);
208
- } catch { /* no meta — treat as stale */ }
234
+ hadEmbeddings = meta.stats && meta.stats.embeddings > 0;
235
+ } catch {
236
+ /* no meta — treat as stale */
237
+ }
209
238
 
210
239
  // If HEAD matches last indexed commit, no reindex needed
211
240
  if (currentHead && currentHead === lastCommit) return;
212
241
 
213
242
  const analyzeCmd = `npx gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`;
214
- sendHookResponse('PostToolUse',
243
+ sendHookResponse(
244
+ 'PostToolUse',
215
245
  `GitNexus index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` +
216
- `Run \`${analyzeCmd}\` to update the knowledge graph.`
246
+ `Run \`${analyzeCmd}\` to update the knowledge graph.`,
217
247
  );
218
248
  }
219
249
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.4.10",
3
+ "version": "1.5.0",
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",
@@ -39,6 +39,7 @@
39
39
  ],
40
40
  "scripts": {
41
41
  "build": "tsc",
42
+ "serve": "tsx src/cli/index.ts serve",
42
43
  "dev": "tsx watch src/cli/index.ts",
43
44
  "test": "vitest run",
44
45
  "test:unit": "vitest run test/unit",
@@ -49,6 +50,7 @@
49
50
  "prepack": "npm run build && chmod +x dist/cli/index.js"
50
51
  },
51
52
  "dependencies": {
53
+ "gitnexus-shared": "file:../gitnexus-shared",
52
54
  "@huggingface/transformers": "^3.0.0",
53
55
  "@ladybugdb/core": "^0.15.2",
54
56
  "@modelcontextprotocol/sdk": "^1.0.0",
@@ -91,7 +93,6 @@
91
93
  "@types/node": "^20.0.0",
92
94
  "@types/uuid": "^10.0.0",
93
95
  "@vitest/coverage-v8": "^4.0.18",
94
- "husky": "^9.1.7",
95
96
  "tsx": "^4.0.0",
96
97
  "typescript": "^5.4.5",
97
98
  "vitest": "^4.0.18"
@@ -1,9 +0,0 @@
1
- /**
2
- * Language Detection — maps file paths to SupportedLanguages enum values.
3
- */
4
- import { SupportedLanguages } from '../../../config/supported-languages.js';
5
- /**
6
- * Map file extension to SupportedLanguage enum.
7
- * Returns null if the file extension is not recognized.
8
- */
9
- export declare const getLanguageFromFilename: (filename: string) => SupportedLanguages | null;
@@ -1,70 +0,0 @@
1
- /**
2
- * Language Detection — maps file paths to SupportedLanguages enum values.
3
- */
4
- import { SupportedLanguages } from '../../../config/supported-languages.js';
5
- /** Ruby extensionless filenames recognised as Ruby source */
6
- const RUBY_EXTENSIONLESS_FILES = new Set(['Rakefile', 'Gemfile', 'Guardfile', 'Vagrantfile', 'Brewfile']);
7
- /**
8
- * Map file extension to SupportedLanguage enum.
9
- * Returns null if the file extension is not recognized.
10
- */
11
- export const getLanguageFromFilename = (filename) => {
12
- // TypeScript (including TSX)
13
- if (filename.endsWith('.tsx'))
14
- return SupportedLanguages.TypeScript;
15
- if (filename.endsWith('.ts'))
16
- return SupportedLanguages.TypeScript;
17
- // JavaScript (including JSX)
18
- if (filename.endsWith('.jsx'))
19
- return SupportedLanguages.JavaScript;
20
- if (filename.endsWith('.js'))
21
- return SupportedLanguages.JavaScript;
22
- // Python
23
- if (filename.endsWith('.py'))
24
- return SupportedLanguages.Python;
25
- // Java
26
- if (filename.endsWith('.java'))
27
- return SupportedLanguages.Java;
28
- // C source files
29
- if (filename.endsWith('.c'))
30
- return SupportedLanguages.C;
31
- // C++ (all common extensions, including .h)
32
- // .h is parsed as C++ because tree-sitter-cpp is a strict superset of C, so pure-C
33
- // headers parse correctly, and C++ headers (classes, templates) are handled properly.
34
- if (filename.endsWith('.cpp') || filename.endsWith('.cc') || filename.endsWith('.cxx') ||
35
- filename.endsWith('.h') || filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh'))
36
- return SupportedLanguages.CPlusPlus;
37
- // C#
38
- if (filename.endsWith('.cs'))
39
- return SupportedLanguages.CSharp;
40
- // Go
41
- if (filename.endsWith('.go'))
42
- return SupportedLanguages.Go;
43
- // Rust
44
- if (filename.endsWith('.rs'))
45
- return SupportedLanguages.Rust;
46
- // Kotlin
47
- if (filename.endsWith('.kt') || filename.endsWith('.kts'))
48
- return SupportedLanguages.Kotlin;
49
- // PHP (all common extensions)
50
- if (filename.endsWith('.php') || filename.endsWith('.phtml') ||
51
- filename.endsWith('.php3') || filename.endsWith('.php4') ||
52
- filename.endsWith('.php5') || filename.endsWith('.php8')) {
53
- return SupportedLanguages.PHP;
54
- }
55
- // Ruby (extensions)
56
- if (filename.endsWith('.rb') || filename.endsWith('.rake') || filename.endsWith('.gemspec')) {
57
- return SupportedLanguages.Ruby;
58
- }
59
- // Ruby (extensionless files)
60
- const basename = filename.split('/').pop() || filename;
61
- if (RUBY_EXTENSIONLESS_FILES.has(basename)) {
62
- return SupportedLanguages.Ruby;
63
- }
64
- // Swift (extensions)
65
- if (filename.endsWith('.swift'))
66
- return SupportedLanguages.Swift;
67
- if (filename.endsWith('.dart'))
68
- return SupportedLanguages.Dart;
69
- return null;
70
- };