gitnexus 1.4.7 → 1.4.8

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 (92) hide show
  1. package/README.md +22 -1
  2. package/dist/cli/ai-context.d.ts +1 -1
  3. package/dist/cli/ai-context.js +1 -1
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +54 -21
  6. package/dist/cli/index.js +2 -1
  7. package/dist/cli/setup.js +78 -1
  8. package/dist/config/supported-languages.d.ts +30 -0
  9. package/dist/config/supported-languages.js +30 -0
  10. package/dist/core/embeddings/embedder.d.ts +6 -1
  11. package/dist/core/embeddings/embedder.js +65 -5
  12. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  13. package/dist/core/embeddings/http-client.d.ts +31 -0
  14. package/dist/core/embeddings/http-client.js +179 -0
  15. package/dist/core/embeddings/index.d.ts +1 -0
  16. package/dist/core/embeddings/index.js +1 -0
  17. package/dist/core/embeddings/types.d.ts +1 -1
  18. package/dist/core/graph/types.d.ts +2 -1
  19. package/dist/core/ingestion/ast-helpers.d.ts +80 -0
  20. package/dist/core/ingestion/ast-helpers.js +738 -0
  21. package/dist/core/ingestion/call-analysis.d.ts +73 -0
  22. package/dist/core/ingestion/call-analysis.js +490 -0
  23. package/dist/core/ingestion/call-processor.d.ts +48 -1
  24. package/dist/core/ingestion/call-processor.js +368 -7
  25. package/dist/core/ingestion/call-routing.d.ts +6 -0
  26. package/dist/core/ingestion/entry-point-scoring.js +36 -26
  27. package/dist/core/ingestion/framework-detection.d.ts +10 -2
  28. package/dist/core/ingestion/framework-detection.js +49 -12
  29. package/dist/core/ingestion/heritage-processor.js +47 -49
  30. package/dist/core/ingestion/import-processor.d.ts +1 -1
  31. package/dist/core/ingestion/import-processor.js +103 -194
  32. package/dist/core/ingestion/import-resolution.d.ts +101 -0
  33. package/dist/core/ingestion/import-resolution.js +251 -0
  34. package/dist/core/ingestion/language-config.d.ts +3 -0
  35. package/dist/core/ingestion/language-config.js +13 -0
  36. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  37. package/dist/core/ingestion/markdown-processor.js +124 -0
  38. package/dist/core/ingestion/mro-processor.js +8 -3
  39. package/dist/core/ingestion/named-binding-extraction.d.ts +9 -43
  40. package/dist/core/ingestion/named-binding-extraction.js +89 -79
  41. package/dist/core/ingestion/parsing-processor.d.ts +2 -2
  42. package/dist/core/ingestion/parsing-processor.js +14 -73
  43. package/dist/core/ingestion/pipeline.d.ts +10 -0
  44. package/dist/core/ingestion/pipeline.js +421 -4
  45. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  46. package/dist/core/ingestion/resolution-context.js +7 -4
  47. package/dist/core/ingestion/resolvers/index.d.ts +1 -1
  48. package/dist/core/ingestion/resolvers/index.js +1 -1
  49. package/dist/core/ingestion/resolvers/jvm.d.ts +2 -1
  50. package/dist/core/ingestion/resolvers/jvm.js +25 -9
  51. package/dist/core/ingestion/resolvers/php.d.ts +14 -0
  52. package/dist/core/ingestion/resolvers/php.js +43 -3
  53. package/dist/core/ingestion/resolvers/utils.d.ts +5 -0
  54. package/dist/core/ingestion/resolvers/utils.js +16 -0
  55. package/dist/core/ingestion/symbol-table.d.ts +16 -0
  56. package/dist/core/ingestion/symbol-table.js +20 -6
  57. package/dist/core/ingestion/tree-sitter-queries.d.ts +4 -4
  58. package/dist/core/ingestion/tree-sitter-queries.js +43 -2
  59. package/dist/core/ingestion/type-env.d.ts +28 -1
  60. package/dist/core/ingestion/type-env.js +419 -96
  61. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  62. package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
  63. package/dist/core/ingestion/type-extractors/csharp.js +149 -16
  64. package/dist/core/ingestion/type-extractors/index.d.ts +1 -1
  65. package/dist/core/ingestion/type-extractors/index.js +1 -1
  66. package/dist/core/ingestion/type-extractors/jvm.js +169 -66
  67. package/dist/core/ingestion/type-extractors/rust.js +35 -1
  68. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -2
  69. package/dist/core/ingestion/type-extractors/shared.js +5 -10
  70. package/dist/core/ingestion/type-extractors/swift.js +7 -6
  71. package/dist/core/ingestion/type-extractors/types.d.ts +37 -7
  72. package/dist/core/ingestion/type-extractors/typescript.js +141 -9
  73. package/dist/core/ingestion/utils.d.ts +2 -120
  74. package/dist/core/ingestion/utils.js +3 -1051
  75. package/dist/core/ingestion/workers/parse-worker.d.ts +13 -4
  76. package/dist/core/ingestion/workers/parse-worker.js +66 -87
  77. package/dist/core/lbug/csv-generator.js +18 -1
  78. package/dist/core/lbug/lbug-adapter.d.ts +10 -0
  79. package/dist/core/lbug/lbug-adapter.js +69 -4
  80. package/dist/core/lbug/schema.d.ts +5 -3
  81. package/dist/core/lbug/schema.js +26 -2
  82. package/dist/mcp/core/embedder.js +11 -3
  83. package/dist/mcp/core/lbug-adapter.js +12 -1
  84. package/dist/mcp/local/local-backend.d.ts +22 -0
  85. package/dist/mcp/local/local-backend.js +133 -29
  86. package/dist/mcp/resources.js +2 -0
  87. package/dist/mcp/tools.js +2 -2
  88. package/dist/server/api.d.ts +19 -1
  89. package/dist/server/api.js +66 -6
  90. package/dist/storage/git.d.ts +12 -0
  91. package/dist/storage/git.js +21 -0
  92. package/package.json +10 -2
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Import Resolution Dispatch
3
+ *
4
+ * Per-language dispatch table for import resolution and named binding extraction.
5
+ * Replaces the 120-line if-chain in resolveLanguageImport() and the 7-branch
6
+ * dispatch in extractNamedBindings() with a single table lookup each.
7
+ *
8
+ * Follows the existing ExportChecker / CallRouter pattern:
9
+ * - Function aliases (not interfaces) to avoid megamorphic inline-cache issues
10
+ * - `satisfies Record<SupportedLanguages, ...>` for compile-time exhaustiveness
11
+ * - Const dispatch table — configs are accessed via ctx.configs at call time
12
+ */
13
+ import { SupportedLanguages } from '../../config/supported-languages.js';
14
+ import { KOTLIN_EXTENSIONS, appendKotlinWildcard, resolveJvmWildcard, resolveJvmMemberImport, resolveGoPackageDir, resolveGoPackage, resolveCSharpImport as resolveCSharpImportHelper, resolveCSharpNamespaceDir, resolvePhpImport as resolvePhpImportHelper, resolveRustImport as resolveRustImportHelper, resolveRubyImport as resolveRubyImportHelper, resolvePythonImport as resolvePythonImportHelper, resolveImportPath, } from './resolvers/index.js';
15
+ import { extractTsNamedBindings, extractPythonNamedBindings, extractKotlinNamedBindings, extractRustNamedBindings, extractPhpNamedBindings, extractCsharpNamedBindings, extractJavaNamedBindings, } from './named-binding-extraction.js';
16
+ // ============================================================================
17
+ // Import path preprocessing
18
+ // ============================================================================
19
+ /**
20
+ * Clean and preprocess a raw import source text into a resolved import path.
21
+ * Strips quotes/angle brackets (universal) and applies language-specific
22
+ * transformations (currently only Kotlin wildcard import detection).
23
+ */
24
+ export function preprocessImportPath(sourceText, importNode, language) {
25
+ const cleaned = sourceText.replace(/['"<>]/g, '');
26
+ // Defense-in-depth: reject null bytes and control characters (matches Ruby call-routing pattern)
27
+ if (!cleaned || cleaned.length > 2048 || /[\x00-\x1f]/.test(cleaned))
28
+ return null;
29
+ if (language === SupportedLanguages.Kotlin) {
30
+ return appendKotlinWildcard(cleaned, importNode);
31
+ }
32
+ return cleaned;
33
+ }
34
+ // ============================================================================
35
+ // Per-language resolver functions
36
+ // ============================================================================
37
+ /**
38
+ * Standard single-file resolution (TS/JS/C/C++ and fallback for other languages).
39
+ * Handles relative imports, tsconfig path aliases, and suffix matching.
40
+ */
41
+ function resolveStandard(rawImportPath, filePath, ctx, language) {
42
+ const resolvedPath = resolveImportPath(filePath, rawImportPath, ctx.allFilePaths, ctx.allFileList, ctx.normalizedFileList, ctx.resolveCache, language, ctx.configs.tsconfigPaths, ctx.index);
43
+ return resolvedPath ? { kind: 'files', files: [resolvedPath] } : null;
44
+ }
45
+ /** Java: JVM wildcard -> member import -> standard fallthrough */
46
+ function resolveJavaImport(rawImportPath, filePath, ctx) {
47
+ if (rawImportPath.endsWith('.*')) {
48
+ const matchedFiles = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
49
+ if (matchedFiles.length > 0)
50
+ return { kind: 'files', files: matchedFiles };
51
+ }
52
+ else {
53
+ const memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
54
+ if (memberResolved)
55
+ return { kind: 'files', files: [memberResolved] };
56
+ }
57
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Java);
58
+ }
59
+ /**
60
+ * Kotlin: JVM wildcard/member with Java-interop fallback -> top-level function imports -> standard.
61
+ * Kotlin can import from .kt/.kts files OR from .java files (Java interop).
62
+ */
63
+ function resolveKotlinImport(rawImportPath, filePath, ctx) {
64
+ if (rawImportPath.endsWith('.*')) {
65
+ const matchedFiles = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
66
+ if (matchedFiles.length === 0) {
67
+ const javaMatches = resolveJvmWildcard(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
68
+ if (javaMatches.length > 0)
69
+ return { kind: 'files', files: javaMatches };
70
+ }
71
+ if (matchedFiles.length > 0)
72
+ return { kind: 'files', files: matchedFiles };
73
+ }
74
+ else {
75
+ let memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
76
+ if (!memberResolved) {
77
+ memberResolved = resolveJvmMemberImport(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
78
+ }
79
+ if (memberResolved)
80
+ return { kind: 'files', files: [memberResolved] };
81
+ // Kotlin: top-level function imports (e.g. import models.getUser) have only 2 segments,
82
+ // which resolveJvmMemberImport skips (requires >=3). Fall back to package-directory scan
83
+ // for lowercase last segments (function/property imports). Uppercase last segments
84
+ // (class imports like models.User) fall through to standard suffix resolution.
85
+ const segments = rawImportPath.split('.');
86
+ const lastSeg = segments[segments.length - 1];
87
+ if (segments.length >= 2 && lastSeg[0] && lastSeg[0] === lastSeg[0].toLowerCase()) {
88
+ const pkgWildcard = segments.slice(0, -1).join('.') + '.*';
89
+ let dirFiles = resolveJvmWildcard(pkgWildcard, ctx.normalizedFileList, ctx.allFileList, KOTLIN_EXTENSIONS, ctx.index);
90
+ if (dirFiles.length === 0) {
91
+ dirFiles = resolveJvmWildcard(pkgWildcard, ctx.normalizedFileList, ctx.allFileList, ['.java'], ctx.index);
92
+ }
93
+ if (dirFiles.length > 0)
94
+ return { kind: 'files', files: dirFiles };
95
+ }
96
+ }
97
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Kotlin);
98
+ }
99
+ /** Go: package-level imports via go.mod module path. */
100
+ function resolveGoImport(rawImportPath, filePath, ctx) {
101
+ const goModule = ctx.configs.goModule;
102
+ if (goModule && rawImportPath.startsWith(goModule.modulePath)) {
103
+ const pkgSuffix = resolveGoPackageDir(rawImportPath, goModule);
104
+ if (pkgSuffix) {
105
+ const pkgFiles = resolveGoPackage(rawImportPath, goModule, ctx.normalizedFileList, ctx.allFileList);
106
+ if (pkgFiles.length > 0) {
107
+ return { kind: 'package', files: pkgFiles, dirSuffix: pkgSuffix };
108
+ }
109
+ }
110
+ // Fall through if no files found (package might be external)
111
+ }
112
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Go);
113
+ }
114
+ /** C#: namespace-based resolution via .csproj configs, with suffix-match fallback. */
115
+ function resolveCSharpImportDispatch(rawImportPath, filePath, ctx) {
116
+ const csharpConfigs = ctx.configs.csharpConfigs;
117
+ if (csharpConfigs.length > 0) {
118
+ const resolvedFiles = resolveCSharpImportHelper(rawImportPath, csharpConfigs, ctx.normalizedFileList, ctx.allFileList, ctx.index);
119
+ if (resolvedFiles.length > 1) {
120
+ const dirSuffix = resolveCSharpNamespaceDir(rawImportPath, csharpConfigs);
121
+ if (dirSuffix) {
122
+ return { kind: 'package', files: resolvedFiles, dirSuffix };
123
+ }
124
+ }
125
+ if (resolvedFiles.length > 0)
126
+ return { kind: 'files', files: resolvedFiles };
127
+ }
128
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.CSharp);
129
+ }
130
+ /** PHP: namespace-based resolution via composer.json PSR-4. */
131
+ function resolvePhpImportDispatch(rawImportPath, _filePath, ctx) {
132
+ const resolved = resolvePhpImportHelper(rawImportPath, ctx.configs.composerConfig, ctx.allFilePaths, ctx.normalizedFileList, ctx.allFileList, ctx.index);
133
+ return resolved ? { kind: 'files', files: [resolved] } : null;
134
+ }
135
+ /** Swift: module imports via Package.swift target map. */
136
+ function resolveSwiftImportDispatch(rawImportPath, _filePath, ctx) {
137
+ const swiftPackageConfig = ctx.configs.swiftPackageConfig;
138
+ if (swiftPackageConfig) {
139
+ const targetDir = swiftPackageConfig.targets.get(rawImportPath);
140
+ if (targetDir) {
141
+ const dirPrefix = targetDir + '/';
142
+ const files = [];
143
+ for (let i = 0; i < ctx.normalizedFileList.length; i++) {
144
+ if (ctx.normalizedFileList[i].startsWith(dirPrefix) && ctx.normalizedFileList[i].endsWith('.swift')) {
145
+ files.push(ctx.allFileList[i]);
146
+ }
147
+ }
148
+ if (files.length > 0)
149
+ return { kind: 'files', files };
150
+ }
151
+ }
152
+ return null; // External framework (Foundation, UIKit, etc.)
153
+ }
154
+ /**
155
+ * Python: relative imports (PEP 328) + proximity-based bare imports.
156
+ * Falls through to standard suffix resolution when proximity finds no match.
157
+ */
158
+ function resolvePythonImportDispatch(rawImportPath, filePath, ctx) {
159
+ const resolved = resolvePythonImportHelper(filePath, rawImportPath, ctx.allFilePaths);
160
+ if (resolved)
161
+ return { kind: 'files', files: [resolved] };
162
+ if (rawImportPath.startsWith('.'))
163
+ return null; // relative but unresolved -- don't suffix-match
164
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Python);
165
+ }
166
+ /** Ruby: require / require_relative. */
167
+ function resolveRubyImportDispatch(rawImportPath, _filePath, ctx) {
168
+ const resolved = resolveRubyImportHelper(rawImportPath, ctx.normalizedFileList, ctx.allFileList, ctx.index);
169
+ return resolved ? { kind: 'files', files: [resolved] } : null;
170
+ }
171
+ /** Rust: expand grouped imports: use {crate::a, crate::b} and use crate::models::{User, Repo}. */
172
+ function resolveRustImportDispatch(rawImportPath, filePath, ctx) {
173
+ // Top-level grouped: use {crate::a, crate::b}
174
+ if (rawImportPath.startsWith('{') && rawImportPath.endsWith('}')) {
175
+ const inner = rawImportPath.slice(1, -1);
176
+ const parts = inner.split(',').map(p => p.trim()).filter(Boolean);
177
+ const resolved = [];
178
+ for (const part of parts) {
179
+ const r = resolveRustImportHelper(filePath, part, ctx.allFilePaths);
180
+ if (r)
181
+ resolved.push(r);
182
+ }
183
+ return resolved.length > 0 ? { kind: 'files', files: resolved } : null;
184
+ }
185
+ // Scoped grouped: use crate::models::{User, Repo}
186
+ const braceIdx = rawImportPath.indexOf('::{');
187
+ if (braceIdx !== -1 && rawImportPath.endsWith('}')) {
188
+ const pathPrefix = rawImportPath.substring(0, braceIdx);
189
+ const braceContent = rawImportPath.substring(braceIdx + 3, rawImportPath.length - 1);
190
+ const items = braceContent.split(',').map(s => s.trim()).filter(Boolean);
191
+ const resolved = [];
192
+ for (const item of items) {
193
+ // Handle `use crate::models::{User, Repo as R}` — strip alias for resolution
194
+ const itemName = item.includes(' as ') ? item.split(' as ')[0].trim() : item;
195
+ const r = resolveRustImportHelper(filePath, `${pathPrefix}::${itemName}`, ctx.allFilePaths);
196
+ if (r)
197
+ resolved.push(r);
198
+ }
199
+ if (resolved.length > 0)
200
+ return { kind: 'files', files: resolved };
201
+ // Fallback: resolve the prefix path itself (e.g. crate::models -> models.rs)
202
+ const prefixResult = resolveRustImportHelper(filePath, pathPrefix, ctx.allFilePaths);
203
+ if (prefixResult)
204
+ return { kind: 'files', files: [prefixResult] };
205
+ }
206
+ return resolveStandard(rawImportPath, filePath, ctx, SupportedLanguages.Rust);
207
+ }
208
+ // ============================================================================
209
+ // Dispatch tables
210
+ // ============================================================================
211
+ /**
212
+ * Per-language import resolver dispatch table.
213
+ * Configs are accessed via ctx.configs at call time — no factory closure needed.
214
+ * Each resolver encapsulates the full resolution flow for its language, including
215
+ * fallthrough to standard resolution where appropriate.
216
+ */
217
+ export const importResolvers = {
218
+ [SupportedLanguages.JavaScript]: (raw, fp, ctx) => resolveStandard(raw, fp, ctx, SupportedLanguages.JavaScript),
219
+ [SupportedLanguages.TypeScript]: (raw, fp, ctx) => resolveStandard(raw, fp, ctx, SupportedLanguages.TypeScript),
220
+ [SupportedLanguages.Python]: (raw, fp, ctx) => resolvePythonImportDispatch(raw, fp, ctx),
221
+ [SupportedLanguages.Java]: (raw, fp, ctx) => resolveJavaImport(raw, fp, ctx),
222
+ [SupportedLanguages.C]: (raw, fp, ctx) => resolveStandard(raw, fp, ctx, SupportedLanguages.C),
223
+ [SupportedLanguages.CPlusPlus]: (raw, fp, ctx) => resolveStandard(raw, fp, ctx, SupportedLanguages.CPlusPlus),
224
+ [SupportedLanguages.CSharp]: (raw, fp, ctx) => resolveCSharpImportDispatch(raw, fp, ctx),
225
+ [SupportedLanguages.Go]: (raw, fp, ctx) => resolveGoImport(raw, fp, ctx),
226
+ [SupportedLanguages.Ruby]: (raw, fp, ctx) => resolveRubyImportDispatch(raw, fp, ctx),
227
+ [SupportedLanguages.Rust]: (raw, fp, ctx) => resolveRustImportDispatch(raw, fp, ctx),
228
+ [SupportedLanguages.PHP]: (raw, fp, ctx) => resolvePhpImportDispatch(raw, fp, ctx),
229
+ [SupportedLanguages.Kotlin]: (raw, fp, ctx) => resolveKotlinImport(raw, fp, ctx),
230
+ [SupportedLanguages.Swift]: (raw, fp, ctx) => resolveSwiftImportDispatch(raw, fp, ctx),
231
+ };
232
+ /**
233
+ * Per-language named binding extractor dispatch table.
234
+ * Languages with whole-module import semantics (Go, Ruby, C/C++, Swift) return undefined --
235
+ * their bindings are synthesized post-parse by synthesizeWildcardImportBindings() in pipeline.ts.
236
+ */
237
+ export const namedBindingExtractors = {
238
+ [SupportedLanguages.JavaScript]: extractTsNamedBindings,
239
+ [SupportedLanguages.TypeScript]: extractTsNamedBindings,
240
+ [SupportedLanguages.Python]: extractPythonNamedBindings,
241
+ [SupportedLanguages.Java]: extractJavaNamedBindings,
242
+ [SupportedLanguages.C]: undefined,
243
+ [SupportedLanguages.CPlusPlus]: undefined,
244
+ [SupportedLanguages.CSharp]: extractCsharpNamedBindings,
245
+ [SupportedLanguages.Go]: undefined,
246
+ [SupportedLanguages.Ruby]: undefined,
247
+ [SupportedLanguages.Rust]: extractRustNamedBindings,
248
+ [SupportedLanguages.PHP]: extractPhpNamedBindings,
249
+ [SupportedLanguages.Kotlin]: extractKotlinNamedBindings,
250
+ [SupportedLanguages.Swift]: undefined,
251
+ };
@@ -1,3 +1,4 @@
1
+ import type { ImportConfigs } from './import-resolution.js';
1
2
  /** TypeScript path alias config parsed from tsconfig.json */
2
3
  export interface TsconfigPaths {
3
4
  /** Map of alias prefix -> target prefix (e.g., "@/" -> "src/") */
@@ -44,3 +45,5 @@ export declare function loadComposerConfig(repoRoot: string): Promise<ComposerCo
44
45
  */
45
46
  export declare function loadCSharpProjectConfig(repoRoot: string): Promise<CSharpProjectConfig[]>;
46
47
  export declare function loadSwiftPackageConfig(repoRoot: string): Promise<SwiftPackageConfig | null>;
48
+ /** Load all language-specific configs once for an ingestion run. */
49
+ export declare function loadImportConfigs(repoRoot: string): Promise<ImportConfigs>;
@@ -165,3 +165,16 @@ export async function loadSwiftPackageConfig(repoRoot) {
165
165
  }
166
166
  return null;
167
167
  }
168
+ // ============================================================================
169
+ // BUNDLED CONFIG LOADER
170
+ // ============================================================================
171
+ /** Load all language-specific configs once for an ingestion run. */
172
+ export async function loadImportConfigs(repoRoot) {
173
+ return {
174
+ tsconfigPaths: await loadTsconfigPaths(repoRoot),
175
+ goModule: await loadGoModulePath(repoRoot),
176
+ composerConfig: await loadComposerConfig(repoRoot),
177
+ swiftPackageConfig: await loadSwiftPackageConfig(repoRoot),
178
+ csharpConfigs: await loadCSharpProjectConfig(repoRoot),
179
+ };
180
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Markdown Processor
3
+ *
4
+ * Extracts structure from .md files using regex (no tree-sitter dependency).
5
+ * Creates Section nodes for headings with hierarchy, and IMPORTS edges for
6
+ * cross-file links.
7
+ */
8
+ import { KnowledgeGraph } from '../graph/types.js';
9
+ interface MdFile {
10
+ path: string;
11
+ content: string;
12
+ }
13
+ export declare const processMarkdown: (graph: KnowledgeGraph, files: MdFile[], allPathSet: Set<string>) => {
14
+ sections: number;
15
+ links: number;
16
+ };
17
+ export {};
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Markdown Processor
3
+ *
4
+ * Extracts structure from .md files using regex (no tree-sitter dependency).
5
+ * Creates Section nodes for headings with hierarchy, and IMPORTS edges for
6
+ * cross-file links.
7
+ */
8
+ import path from 'node:path';
9
+ import { generateId } from '../../lib/utils.js';
10
+ const HEADING_RE = /^(#{1,6})\s+(.+)$/;
11
+ const LINK_RE = /\[([^\]]*)\]\(([^)]+)\)/g;
12
+ const MD_EXTENSIONS = new Set(['.md', '.mdx']);
13
+ export const processMarkdown = (graph, files, allPathSet) => {
14
+ let totalSections = 0;
15
+ let totalLinks = 0;
16
+ for (const file of files) {
17
+ const ext = path.extname(file.path).toLowerCase();
18
+ if (!MD_EXTENSIONS.has(ext))
19
+ continue;
20
+ const fileNodeId = generateId('File', file.path);
21
+ // Skip if file node doesn't exist (shouldn't happen, structure-processor creates it)
22
+ if (!graph.getNode(fileNodeId))
23
+ continue;
24
+ const lines = file.content.split('\n');
25
+ // --- Extract headings and build hierarchy ---
26
+ // First pass: collect all heading positions so we can compute endLine spans
27
+ const headings = [];
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const match = lines[i].match(HEADING_RE);
30
+ if (!match)
31
+ continue;
32
+ headings.push({
33
+ level: match[1].length,
34
+ heading: match[2].trim(),
35
+ lineNum: i + 1, // 1-indexed
36
+ });
37
+ }
38
+ // Second pass: create nodes with proper endLine spans
39
+ const sectionStack = [];
40
+ for (let h = 0; h < headings.length; h++) {
41
+ const { level, heading, lineNum } = headings[h];
42
+ // endLine = line before next heading at same or higher level, or EOF
43
+ let endLine = lines.length;
44
+ for (let j = h + 1; j < headings.length; j++) {
45
+ if (headings[j].level <= level) {
46
+ endLine = headings[j].lineNum - 1;
47
+ break;
48
+ }
49
+ }
50
+ const sectionId = generateId('Section', `${file.path}:L${lineNum}:${heading}`);
51
+ const node = {
52
+ id: sectionId,
53
+ label: 'Section',
54
+ properties: {
55
+ name: heading,
56
+ filePath: file.path,
57
+ startLine: lineNum,
58
+ endLine,
59
+ level,
60
+ description: `h${level}`,
61
+ },
62
+ };
63
+ graph.addNode(node);
64
+ totalSections++;
65
+ // Find parent: pop stack until we find a level strictly less than current
66
+ while (sectionStack.length > 0 && sectionStack[sectionStack.length - 1].level >= level) {
67
+ sectionStack.pop();
68
+ }
69
+ const parentId = sectionStack.length > 0
70
+ ? sectionStack[sectionStack.length - 1].id
71
+ : fileNodeId;
72
+ graph.addRelationship({
73
+ id: generateId('CONTAINS', `${parentId}->${sectionId}`),
74
+ type: 'CONTAINS',
75
+ sourceId: parentId,
76
+ targetId: sectionId,
77
+ confidence: 1.0,
78
+ reason: 'markdown-heading',
79
+ });
80
+ sectionStack.push({ level, id: sectionId });
81
+ }
82
+ // --- Extract links to other files in the repo ---
83
+ const fileDir = path.dirname(file.path);
84
+ const seenLinks = new Set();
85
+ let linkMatch;
86
+ LINK_RE.lastIndex = 0;
87
+ while ((linkMatch = LINK_RE.exec(file.content)) !== null) {
88
+ const href = linkMatch[2];
89
+ // Skip external URLs, anchors, and mailto
90
+ if (href.startsWith('http://') || href.startsWith('https://') ||
91
+ href.startsWith('#') || href.startsWith('mailto:')) {
92
+ continue;
93
+ }
94
+ // Strip anchor fragments from local links
95
+ const cleanHref = href.split('#')[0];
96
+ if (!cleanHref)
97
+ continue;
98
+ // Resolve relative to the file's directory, then normalize
99
+ const resolved = path.posix.normalize(path.posix.join(fileDir, cleanHref));
100
+ if (allPathSet.has(resolved)) {
101
+ const targetFileId = generateId('File', resolved);
102
+ // Skip if target file node doesn't exist
103
+ if (!graph.getNode(targetFileId))
104
+ continue;
105
+ // Dedup: skip if we've already linked this file pair
106
+ const linkKey = `${fileNodeId}->${targetFileId}`;
107
+ if (seenLinks.has(linkKey))
108
+ continue;
109
+ seenLinks.add(linkKey);
110
+ const relId = generateId('IMPORTS', linkKey);
111
+ graph.addRelationship({
112
+ id: relId,
113
+ type: 'IMPORTS',
114
+ sourceId: fileNodeId,
115
+ targetId: targetFileId,
116
+ confidence: 0.8,
117
+ reason: 'markdown-link',
118
+ });
119
+ totalLinks++;
120
+ }
121
+ }
122
+ }
123
+ return { sections: totalSections, links: totalLinks };
124
+ };
@@ -158,10 +158,11 @@ function resolveByMroOrder(methodName, defs, mroOrder, reasonPrefix) {
158
158
  return {
159
159
  resolvedTo: match.methodId,
160
160
  reason: `${reasonPrefix}: ${match.className}::${methodName}`,
161
+ confidence: 0.9, // MRO-ordered resolution
161
162
  };
162
163
  }
163
164
  }
164
- return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition` };
165
+ return { resolvedTo: defs[0].methodId, reason: `${reasonPrefix} fallback: first definition`, confidence: 0.7 };
165
166
  }
166
167
  function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
167
168
  const classDefs = [];
@@ -179,21 +180,24 @@ function resolveCsharpJava(methodName, defs, parentEdgeTypes) {
179
180
  return {
180
181
  resolvedTo: classDefs[0].methodId,
181
182
  reason: `class method wins: ${classDefs[0].className}::${methodName}`,
183
+ confidence: 0.95, // Class method is authoritative
182
184
  };
183
185
  }
184
186
  if (interfaceDefs.length > 1) {
185
187
  return {
186
188
  resolvedTo: null,
187
189
  reason: `ambiguous: ${methodName} defined in multiple interfaces: ${interfaceDefs.map(d => d.className).join(', ')}`,
190
+ confidence: 0.5,
188
191
  };
189
192
  }
190
193
  if (interfaceDefs.length === 1) {
191
194
  return {
192
195
  resolvedTo: interfaceDefs[0].methodId,
193
196
  reason: `single interface default: ${interfaceDefs[0].className}::${methodName}`,
197
+ confidence: 0.85, // Single interface, unambiguous
194
198
  };
195
199
  }
196
- return { resolvedTo: null, reason: 'no resolution found' };
200
+ return { resolvedTo: null, reason: 'no resolution found', confidence: 0.5 };
197
201
  }
198
202
  // ---------------------------------------------------------------------------
199
203
  // Main entry point
@@ -293,6 +297,7 @@ export function computeMRO(graph) {
293
297
  resolution = {
294
298
  resolvedTo: null,
295
299
  reason: `Rust requires qualified syntax: <Type as Trait>::${methodName}()`,
300
+ confidence: 0.5,
296
301
  };
297
302
  break;
298
303
  default:
@@ -316,7 +321,7 @@ export function computeMRO(graph) {
316
321
  sourceId: classId,
317
322
  targetId: resolution.resolvedTo,
318
323
  type: 'OVERRIDES',
319
- confidence: 1.0,
324
+ confidence: resolution.confidence,
320
325
  reason: resolution.reason,
321
326
  });
322
327
  overrideEdges++;
@@ -1,6 +1,7 @@
1
- import { SupportedLanguages } from '../../config/supported-languages.js';
2
1
  import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
3
2
  import type { NamedImportMap } from './import-processor.js';
3
+ import type { NamedBinding } from './import-resolution.js';
4
+ import type { SyntaxNode } from './utils.js';
4
5
  /**
5
6
  * Walk a named-binding re-export chain through NamedImportMap.
6
7
  *
@@ -17,45 +18,10 @@ import type { NamedImportMap } from './import-processor.js';
17
18
  * silent misses at depth=0 for non-aliased bindings.
18
19
  */
19
20
  export declare function walkBindingChain(name: string, currentFilePath: string, symbolTable: SymbolTable, namedImportMap: NamedImportMap, allDefs: SymbolDefinition[]): SymbolDefinition[] | null;
20
- /**
21
- * Extract named bindings from an import AST node.
22
- * Returns undefined if the import is not a named import (e.g., import * or default).
23
- *
24
- * TS: import { User, Repo as R } from './models'
25
- * → [{local:'User', exported:'User'}, {local:'R', exported:'Repo'}]
26
- *
27
- * Python: from models import User, Repo as R
28
- * → [{local:'User', exported:'User'}, {local:'R', exported:'Repo'}]
29
- */
30
- export declare function extractNamedBindings(importNode: any, language: SupportedLanguages): {
31
- local: string;
32
- exported: string;
33
- }[] | undefined;
34
- export declare function extractTsNamedBindings(importNode: any): {
35
- local: string;
36
- exported: string;
37
- }[] | undefined;
38
- export declare function extractPythonNamedBindings(importNode: any): {
39
- local: string;
40
- exported: string;
41
- }[] | undefined;
42
- export declare function extractKotlinNamedBindings(importNode: any): {
43
- local: string;
44
- exported: string;
45
- }[] | undefined;
46
- export declare function extractRustNamedBindings(importNode: any): {
47
- local: string;
48
- exported: string;
49
- }[] | undefined;
50
- export declare function extractPhpNamedBindings(importNode: any): {
51
- local: string;
52
- exported: string;
53
- }[] | undefined;
54
- export declare function extractCsharpNamedBindings(importNode: any): {
55
- local: string;
56
- exported: string;
57
- }[] | undefined;
58
- export declare function extractJavaNamedBindings(importNode: any): {
59
- local: string;
60
- exported: string;
61
- }[] | undefined;
21
+ export declare function extractTsNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
22
+ export declare function extractPythonNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
23
+ export declare function extractKotlinNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
24
+ export declare function extractRustNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
25
+ export declare function extractPhpNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
26
+ export declare function extractCsharpNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;
27
+ export declare function extractJavaNamedBindings(importNode: SyntaxNode): NamedBinding[] | undefined;