@veewo/gitnexus 1.3.11 → 1.4.6-rc

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 (181) hide show
  1. package/README.md +37 -80
  2. package/dist/benchmark/agent-context/tool-runner.js +2 -2
  3. package/dist/benchmark/neonspark-candidates.js +3 -3
  4. package/dist/benchmark/tool-runner.js +2 -2
  5. package/dist/cli/ai-context.d.ts +2 -1
  6. package/dist/cli/ai-context.js +16 -12
  7. package/dist/cli/analyze.d.ts +2 -0
  8. package/dist/cli/analyze.js +68 -48
  9. package/dist/cli/augment.js +1 -1
  10. package/dist/cli/eval-server.d.ts +8 -1
  11. package/dist/cli/eval-server.js +30 -13
  12. package/dist/cli/index.js +28 -82
  13. package/dist/cli/lazy-action.d.ts +6 -0
  14. package/dist/cli/lazy-action.js +18 -0
  15. package/dist/cli/mcp.js +3 -1
  16. package/dist/cli/setup.js +87 -48
  17. package/dist/cli/setup.test.js +18 -13
  18. package/dist/cli/skill-gen.d.ts +26 -0
  19. package/dist/cli/skill-gen.js +549 -0
  20. package/dist/cli/status.js +13 -4
  21. package/dist/cli/tool.d.ts +3 -2
  22. package/dist/cli/tool.js +50 -16
  23. package/dist/cli/wiki.js +8 -4
  24. package/dist/config/ignore-service.d.ts +25 -0
  25. package/dist/config/ignore-service.js +76 -0
  26. package/dist/config/supported-languages.d.ts +4 -1
  27. package/dist/config/supported-languages.js +3 -2
  28. package/dist/core/augmentation/engine.js +94 -67
  29. package/dist/core/embeddings/embedder.d.ts +1 -1
  30. package/dist/core/embeddings/embedder.js +1 -1
  31. package/dist/core/embeddings/embedding-pipeline.d.ts +3 -3
  32. package/dist/core/embeddings/embedding-pipeline.js +52 -25
  33. package/dist/core/embeddings/types.d.ts +1 -1
  34. package/dist/core/graph/types.d.ts +7 -2
  35. package/dist/core/ingestion/ast-cache.js +3 -2
  36. package/dist/core/ingestion/call-processor.d.ts +8 -6
  37. package/dist/core/ingestion/call-processor.js +468 -206
  38. package/dist/core/ingestion/call-routing.d.ts +53 -0
  39. package/dist/core/ingestion/call-routing.js +108 -0
  40. package/dist/core/ingestion/constants.d.ts +16 -0
  41. package/dist/core/ingestion/constants.js +16 -0
  42. package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
  43. package/dist/core/ingestion/entry-point-scoring.js +116 -23
  44. package/dist/core/ingestion/export-detection.d.ts +18 -0
  45. package/dist/core/ingestion/export-detection.js +231 -0
  46. package/dist/core/ingestion/filesystem-walker.js +4 -3
  47. package/dist/core/ingestion/framework-detection.d.ts +19 -4
  48. package/dist/core/ingestion/framework-detection.js +182 -6
  49. package/dist/core/ingestion/heritage-processor.d.ts +13 -5
  50. package/dist/core/ingestion/heritage-processor.js +109 -55
  51. package/dist/core/ingestion/import-processor.d.ts +16 -20
  52. package/dist/core/ingestion/import-processor.js +199 -579
  53. package/dist/core/ingestion/language-config.d.ts +46 -0
  54. package/dist/core/ingestion/language-config.js +167 -0
  55. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  56. package/dist/core/ingestion/mro-processor.js +369 -0
  57. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  58. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  59. package/dist/core/ingestion/parsing-processor.d.ts +4 -1
  60. package/dist/core/ingestion/parsing-processor.js +107 -109
  61. package/dist/core/ingestion/pipeline.d.ts +6 -3
  62. package/dist/core/ingestion/pipeline.js +208 -114
  63. package/dist/core/ingestion/process-processor.js +8 -2
  64. package/dist/core/ingestion/resolution-context.d.ts +53 -0
  65. package/dist/core/ingestion/resolution-context.js +132 -0
  66. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  67. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  68. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  69. package/dist/core/ingestion/resolvers/go.js +42 -0
  70. package/dist/core/ingestion/resolvers/index.d.ts +18 -0
  71. package/dist/core/ingestion/resolvers/index.js +13 -0
  72. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  73. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  74. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  75. package/dist/core/ingestion/resolvers/php.js +35 -0
  76. package/dist/core/ingestion/resolvers/python.d.ts +19 -0
  77. package/dist/core/ingestion/resolvers/python.js +52 -0
  78. package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
  79. package/dist/core/ingestion/resolvers/ruby.js +15 -0
  80. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  81. package/dist/core/ingestion/resolvers/rust.js +73 -0
  82. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  83. package/dist/core/ingestion/resolvers/standard.js +123 -0
  84. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  85. package/dist/core/ingestion/resolvers/utils.js +122 -0
  86. package/dist/core/ingestion/symbol-table.d.ts +21 -1
  87. package/dist/core/ingestion/symbol-table.js +40 -12
  88. package/dist/core/ingestion/tree-sitter-queries.d.ts +13 -10
  89. package/dist/core/ingestion/tree-sitter-queries.js +297 -7
  90. package/dist/core/ingestion/type-env.d.ts +49 -0
  91. package/dist/core/ingestion/type-env.js +611 -0
  92. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  93. package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
  94. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  95. package/dist/core/ingestion/type-extractors/csharp.js +383 -0
  96. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  97. package/dist/core/ingestion/type-extractors/go.js +467 -0
  98. package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
  99. package/dist/core/ingestion/type-extractors/index.js +31 -0
  100. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  101. package/dist/core/ingestion/type-extractors/jvm.js +681 -0
  102. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  103. package/dist/core/ingestion/type-extractors/php.js +549 -0
  104. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  105. package/dist/core/ingestion/type-extractors/python.js +406 -0
  106. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  107. package/dist/core/ingestion/type-extractors/ruby.js +389 -0
  108. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  109. package/dist/core/ingestion/type-extractors/rust.js +449 -0
  110. package/dist/core/ingestion/type-extractors/shared.d.ts +133 -0
  111. package/dist/core/ingestion/type-extractors/shared.js +703 -0
  112. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  113. package/dist/core/ingestion/type-extractors/swift.js +137 -0
  114. package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
  115. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  116. package/dist/core/ingestion/type-extractors/typescript.js +494 -0
  117. package/dist/core/ingestion/utils.d.ts +103 -0
  118. package/dist/core/ingestion/utils.js +1085 -4
  119. package/dist/core/ingestion/workers/parse-worker.d.ts +51 -4
  120. package/dist/core/ingestion/workers/parse-worker.js +634 -222
  121. package/dist/core/ingestion/workers/worker-pool.js +8 -0
  122. package/dist/core/{kuzu → lbug}/csv-generator.d.ts +12 -10
  123. package/dist/core/{kuzu → lbug}/csv-generator.js +82 -101
  124. package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +20 -25
  125. package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +150 -122
  126. package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
  127. package/dist/core/{kuzu → lbug}/schema.js +23 -22
  128. package/dist/core/lbug/schema.test.d.ts +1 -0
  129. package/dist/core/search/bm25-index.d.ts +4 -4
  130. package/dist/core/search/bm25-index.js +12 -11
  131. package/dist/core/search/hybrid-search.d.ts +2 -2
  132. package/dist/core/search/hybrid-search.js +6 -6
  133. package/dist/core/tree-sitter/parser-loader.d.ts +1 -0
  134. package/dist/core/tree-sitter/parser-loader.js +19 -0
  135. package/dist/core/wiki/generator.d.ts +2 -2
  136. package/dist/core/wiki/generator.js +6 -6
  137. package/dist/core/wiki/graph-queries.d.ts +4 -4
  138. package/dist/core/wiki/graph-queries.js +7 -7
  139. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  140. package/dist/mcp/compatible-stdio-transport.js +200 -0
  141. package/dist/mcp/core/{kuzu-adapter.d.ts → lbug-adapter.d.ts} +11 -10
  142. package/dist/mcp/core/lbug-adapter.js +327 -0
  143. package/dist/mcp/local/local-backend.d.ts +21 -16
  144. package/dist/mcp/local/local-backend.js +306 -706
  145. package/dist/mcp/local/unity-parity-seed-loader.d.ts +6 -1
  146. package/dist/mcp/local/unity-parity-seed-loader.js +119 -9
  147. package/dist/mcp/local/unity-parity-seed-loader.test.js +95 -7
  148. package/dist/mcp/resources.js +2 -2
  149. package/dist/mcp/server.js +28 -13
  150. package/dist/mcp/staleness.js +2 -2
  151. package/dist/mcp/tools.js +12 -3
  152. package/dist/server/api.js +12 -12
  153. package/dist/server/mcp-http.d.ts +1 -1
  154. package/dist/server/mcp-http.js +1 -1
  155. package/dist/storage/git.js +4 -1
  156. package/dist/storage/repo-manager.d.ts +20 -2
  157. package/dist/storage/repo-manager.js +74 -4
  158. package/dist/types/pipeline.d.ts +1 -1
  159. package/hooks/claude/gitnexus-hook.cjs +149 -46
  160. package/hooks/claude/pre-tool-use.sh +2 -1
  161. package/hooks/claude/session-start.sh +0 -0
  162. package/package.json +20 -4
  163. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  164. package/skills/gitnexus-cli.md +8 -8
  165. package/skills/gitnexus-debugging.md +1 -1
  166. package/skills/gitnexus-exploring.md +1 -1
  167. package/skills/gitnexus-guide.md +1 -1
  168. package/skills/gitnexus-impact-analysis.md +1 -1
  169. package/skills/gitnexus-pr-review.md +163 -0
  170. package/skills/gitnexus-refactoring.md +1 -1
  171. package/dist/cli/claude-hooks.d.ts +0 -22
  172. package/dist/cli/claude-hooks.js +0 -97
  173. package/dist/mcp/core/kuzu-adapter.js +0 -231
  174. /package/dist/core/{kuzu/csv-generator.test.d.ts → ingestion/type-extractors/types.js} +0 -0
  175. /package/dist/core/{kuzu/relationship-pair-buckets.test.d.ts → lbug/csv-generator.test.d.ts} +0 -0
  176. /package/dist/core/{kuzu → lbug}/csv-generator.test.js +0 -0
  177. /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.d.ts +0 -0
  178. /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.js +0 -0
  179. /package/dist/core/{kuzu/schema.test.d.ts → lbug/relationship-pair-buckets.test.d.ts} +0 -0
  180. /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.test.js +0 -0
  181. /package/dist/core/{kuzu → lbug}/schema.test.js +0 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Export Detection
3
+ *
4
+ * Determines whether a symbol (function, class, etc.) is exported/public
5
+ * in its language. This is a pure function — safe for use in worker threads.
6
+ *
7
+ * Shared between parse-worker.ts (worker pool) and parsing-processor.ts (sequential fallback).
8
+ */
9
+ import { findSiblingChild } from './utils.js';
10
+ import { SupportedLanguages } from '../../config/supported-languages.js';
11
+ // ============================================================================
12
+ // Per-language export checkers
13
+ // ============================================================================
14
+ /** JS/TS: walk ancestors looking for export_statement or export_specifier. */
15
+ const tsExportChecker = (node, _name) => {
16
+ let current = node;
17
+ while (current) {
18
+ const type = current.type;
19
+ if (type === 'export_statement' ||
20
+ type === 'export_specifier' ||
21
+ (type === 'lexical_declaration' && current.parent?.type === 'export_statement')) {
22
+ return true;
23
+ }
24
+ // Fallback: check if node text starts with 'export ' for edge cases
25
+ if (current.text?.startsWith('export ')) {
26
+ return true;
27
+ }
28
+ current = current.parent;
29
+ }
30
+ return false;
31
+ };
32
+ /** Python: public if no leading underscore (convention). */
33
+ const pythonExportChecker = (_node, name) => !name.startsWith('_');
34
+ /** Java: check for 'public' modifier — modifiers are siblings of the name node, not parents. */
35
+ const javaExportChecker = (node, _name) => {
36
+ let current = node;
37
+ while (current) {
38
+ if (current.parent) {
39
+ const parent = current.parent;
40
+ for (let i = 0; i < parent.childCount; i++) {
41
+ const child = parent.child(i);
42
+ if (child?.type === 'modifiers' && child.text?.includes('public')) {
43
+ return true;
44
+ }
45
+ }
46
+ if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
47
+ if (parent.text?.trimStart().startsWith('public')) {
48
+ return true;
49
+ }
50
+ }
51
+ }
52
+ current = current.parent;
53
+ }
54
+ return false;
55
+ };
56
+ /** C# declaration node types for sibling modifier scanning. */
57
+ const CSHARP_DECL_TYPES = new Set([
58
+ 'method_declaration', 'local_function_statement', 'constructor_declaration',
59
+ 'class_declaration', 'interface_declaration', 'struct_declaration',
60
+ 'enum_declaration', 'record_declaration', 'record_struct_declaration',
61
+ 'record_class_declaration', 'delegate_declaration',
62
+ 'property_declaration', 'field_declaration', 'event_declaration',
63
+ 'namespace_declaration', 'file_scoped_namespace_declaration',
64
+ ]);
65
+ /**
66
+ * C#: modifier nodes are SIBLINGS of the name node inside the declaration.
67
+ * Walk up to the declaration node, then scan its direct children.
68
+ */
69
+ const csharpExportChecker = (node, _name) => {
70
+ let current = node;
71
+ while (current) {
72
+ if (CSHARP_DECL_TYPES.has(current.type)) {
73
+ for (let i = 0; i < current.childCount; i++) {
74
+ const child = current.child(i);
75
+ if (child?.type === 'modifier' && child.text === 'public')
76
+ return true;
77
+ }
78
+ return false;
79
+ }
80
+ current = current.parent;
81
+ }
82
+ return false;
83
+ };
84
+ /** Go: uppercase first letter = exported. */
85
+ const goExportChecker = (_node, name) => {
86
+ if (name.length === 0)
87
+ return false;
88
+ const first = name[0];
89
+ return first === first.toUpperCase() && first !== first.toLowerCase();
90
+ };
91
+ /** Rust declaration node types for sibling visibility_modifier scanning. */
92
+ const RUST_DECL_TYPES = new Set([
93
+ 'function_item', 'struct_item', 'enum_item', 'trait_item', 'impl_item',
94
+ 'union_item', 'type_item', 'const_item', 'static_item', 'mod_item',
95
+ 'use_declaration', 'associated_type', 'function_signature_item',
96
+ ]);
97
+ /**
98
+ * Rust: visibility_modifier is a SIBLING of the name node within the declaration node
99
+ * (function_item, struct_item, etc.), not a parent. Walk up to the declaration node,
100
+ * then scan its direct children.
101
+ */
102
+ const rustExportChecker = (node, _name) => {
103
+ let current = node;
104
+ while (current) {
105
+ if (RUST_DECL_TYPES.has(current.type)) {
106
+ for (let i = 0; i < current.childCount; i++) {
107
+ const child = current.child(i);
108
+ if (child?.type === 'visibility_modifier' && child.text?.startsWith('pub'))
109
+ return true;
110
+ }
111
+ return false;
112
+ }
113
+ current = current.parent;
114
+ }
115
+ return false;
116
+ };
117
+ /**
118
+ * Kotlin: default visibility is public (unlike Java).
119
+ * visibility_modifier is inside modifiers, a sibling of the name node within the declaration.
120
+ */
121
+ const kotlinExportChecker = (node, _name) => {
122
+ let current = node;
123
+ while (current) {
124
+ if (current.parent) {
125
+ const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
126
+ if (visMod) {
127
+ const text = visMod.text;
128
+ if (text === 'private' || text === 'internal' || text === 'protected')
129
+ return false;
130
+ if (text === 'public')
131
+ return true;
132
+ }
133
+ }
134
+ current = current.parent;
135
+ }
136
+ // No visibility modifier = public (Kotlin default)
137
+ return true;
138
+ };
139
+ /**
140
+ * C/C++: functions without 'static' storage class have external linkage by default,
141
+ * making them globally accessible (equivalent to exported). Only functions explicitly
142
+ * marked 'static' are file-scoped (not exported). C++ anonymous namespaces
143
+ * (namespace { ... }) also give internal linkage.
144
+ */
145
+ const cCppExportChecker = (node, _name) => {
146
+ let cur = node;
147
+ while (cur) {
148
+ if (cur.type === 'function_definition' || cur.type === 'declaration') {
149
+ // Check for 'static' storage class specifier as a direct child node.
150
+ // This avoids reading the full function text (which can be very large).
151
+ for (let i = 0; i < cur.childCount; i++) {
152
+ const child = cur.child(i);
153
+ if (child?.type === 'storage_class_specifier' && child.text === 'static')
154
+ return false;
155
+ }
156
+ }
157
+ // C++ anonymous namespace: namespace_definition with no name child = internal linkage
158
+ if (cur.type === 'namespace_definition') {
159
+ const hasName = cur.childForFieldName?.('name');
160
+ if (!hasName)
161
+ return false;
162
+ }
163
+ cur = cur.parent;
164
+ }
165
+ return true; // Top-level C/C++ functions default to external linkage
166
+ };
167
+ /** PHP: check for visibility modifier or top-level scope. */
168
+ const phpExportChecker = (node, _name) => {
169
+ let current = node;
170
+ while (current) {
171
+ if (current.type === 'class_declaration' ||
172
+ current.type === 'interface_declaration' ||
173
+ current.type === 'trait_declaration' ||
174
+ current.type === 'enum_declaration') {
175
+ return true;
176
+ }
177
+ if (current.type === 'visibility_modifier') {
178
+ return current.text === 'public';
179
+ }
180
+ current = current.parent;
181
+ }
182
+ // Top-level functions are globally accessible
183
+ return true;
184
+ };
185
+ /** Swift: check for 'public' or 'open' access modifiers. */
186
+ const swiftExportChecker = (node, _name) => {
187
+ let current = node;
188
+ while (current) {
189
+ if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
190
+ const text = current.text || '';
191
+ if (text.includes('public') || text.includes('open'))
192
+ return true;
193
+ }
194
+ current = current.parent;
195
+ }
196
+ return false;
197
+ };
198
+ // ============================================================================
199
+ // Exhaustive dispatch table — satisfies enforces all SupportedLanguages are covered
200
+ // ============================================================================
201
+ const exportCheckers = {
202
+ [SupportedLanguages.JavaScript]: tsExportChecker,
203
+ [SupportedLanguages.TypeScript]: tsExportChecker,
204
+ [SupportedLanguages.Python]: pythonExportChecker,
205
+ [SupportedLanguages.Java]: javaExportChecker,
206
+ [SupportedLanguages.CSharp]: csharpExportChecker,
207
+ [SupportedLanguages.Go]: goExportChecker,
208
+ [SupportedLanguages.Rust]: rustExportChecker,
209
+ [SupportedLanguages.Kotlin]: kotlinExportChecker,
210
+ [SupportedLanguages.C]: cCppExportChecker,
211
+ [SupportedLanguages.CPlusPlus]: cCppExportChecker,
212
+ [SupportedLanguages.PHP]: phpExportChecker,
213
+ [SupportedLanguages.Swift]: swiftExportChecker,
214
+ [SupportedLanguages.Ruby]: (_node, _name) => true,
215
+ };
216
+ // ============================================================================
217
+ // Public API
218
+ // ============================================================================
219
+ /**
220
+ * Check if a tree-sitter node is exported/public in its language.
221
+ * @param node - The tree-sitter AST node
222
+ * @param name - The symbol name
223
+ * @param language - The programming language
224
+ * @returns true if the symbol is exported/public
225
+ */
226
+ export const isNodeExported = (node, name, language) => {
227
+ const checker = exportCheckers[language];
228
+ if (!checker)
229
+ return false;
230
+ return checker(node, name);
231
+ };
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { glob } from 'glob';
4
- import { shouldIgnorePath } from '../../config/ignore-service.js';
4
+ import { createIgnoreFilter, shouldIgnorePath } from '../../config/ignore-service.js';
5
5
  const READ_CONCURRENCY = 32;
6
6
  /** Skip files larger than 512KB — they're usually generated/vendored and crash tree-sitter */
7
7
  const MAX_FILE_SIZE = 512 * 1024;
@@ -11,12 +11,13 @@ const UNITY_RESOURCE_GLOBS = ['**/*.prefab', '**/*.unity', '**/*.asset'];
11
11
  * Memory: ~10MB for 100K files vs ~1GB+ with content.
12
12
  */
13
13
  export const walkRepositoryPaths = async (repoPath, onProgress) => {
14
- const files = await glob('**/*', {
14
+ const ignoreFilter = await createIgnoreFilter(repoPath);
15
+ const filtered = await glob('**/*', {
15
16
  cwd: repoPath,
16
17
  nodir: true,
17
18
  dot: false,
19
+ ignore: ignoreFilter,
18
20
  });
19
- const filtered = files.filter(file => !shouldIgnorePath(file));
20
21
  const entries = [];
21
22
  let processed = 0;
22
23
  let skippedLarge = 0;
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Framework Detection
3
3
  *
4
- * Detects frameworks from file path patterns and provides entry point multipliers.
5
- * This enables framework-aware entry point scoring.
4
+ * Detects frameworks from:
5
+ * 1) file path patterns
6
+ * 2) AST definition text (decorators/annotations/attributes)
7
+ * and provides entry point multipliers for process scoring.
6
8
  *
7
9
  * DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
8
10
  * (no bonus, no penalty) - same behavior as before this feature.
@@ -20,8 +22,8 @@ export interface FrameworkHint {
20
22
  */
21
23
  export declare function detectFrameworkFromPath(filePath: string): FrameworkHint | null;
22
24
  /**
23
- * Patterns that indicate entry points within code (for future AST-based detection)
24
- * These would require parsing decorators/annotations in the code itself.
25
+ * Patterns that indicate framework entry points within code definitions.
26
+ * These are matched against AST node text (class/method/function declaration text).
25
27
  */
26
28
  export declare const FRAMEWORK_AST_PATTERNS: {
27
29
  nestjs: string[];
@@ -31,9 +33,22 @@ export declare const FRAMEWORK_AST_PATTERNS: {
31
33
  spring: string[];
32
34
  jaxrs: string[];
33
35
  aspnet: string[];
36
+ signalr: string[];
37
+ blazor: string[];
38
+ efcore: string[];
34
39
  'go-http': string[];
35
40
  laravel: string[];
36
41
  actix: string[];
37
42
  axum: string[];
38
43
  rocket: string[];
44
+ uikit: string[];
45
+ swiftui: string[];
46
+ combine: string[];
39
47
  };
48
+ import { SupportedLanguages } from '../../config/supported-languages.js';
49
+ /**
50
+ * Detect framework entry points from AST definition text (decorators/annotations/attributes).
51
+ * Returns null if no known pattern is found.
52
+ * Note: callers should slice definitionText to ~300 chars since annotations appear at the start.
53
+ */
54
+ export declare function detectFrameworkFromAST(language: SupportedLanguages, definitionText: string): FrameworkHint | null;
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Framework Detection
3
3
  *
4
- * Detects frameworks from file path patterns and provides entry point multipliers.
5
- * This enables framework-aware entry point scoring.
4
+ * Detects frameworks from:
5
+ * 1) file path patterns
6
+ * 2) AST definition text (decorators/annotations/attributes)
7
+ * and provides entry point multipliers for process scoring.
6
8
  *
7
9
  * DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
8
10
  * (no bonus, no penalty) - same behavior as before this feature.
@@ -94,6 +96,41 @@ export function detectFrameworkFromPath(filePath) {
94
96
  if ((p.includes('/service/') || p.includes('/services/')) && p.endsWith('.java')) {
95
97
  return { framework: 'java-service', entryPointMultiplier: 1.8, reason: 'java-service' };
96
98
  }
99
+ // ========== KOTLIN FRAMEWORKS ==========
100
+ // Spring Boot Kotlin controllers
101
+ if ((p.includes('/controller/') || p.includes('/controllers/')) && p.endsWith('.kt')) {
102
+ return { framework: 'spring-kotlin', entryPointMultiplier: 3.0, reason: 'spring-kotlin-controller' };
103
+ }
104
+ // Spring Boot - files ending in Controller.kt
105
+ if (p.endsWith('controller.kt')) {
106
+ return { framework: 'spring-kotlin', entryPointMultiplier: 3.0, reason: 'spring-kotlin-controller-file' };
107
+ }
108
+ // Ktor routes
109
+ if (p.includes('/routes/') && p.endsWith('.kt')) {
110
+ return { framework: 'ktor', entryPointMultiplier: 2.5, reason: 'ktor-routes' };
111
+ }
112
+ // Ktor plugins folder or Routing.kt files
113
+ if (p.includes('/plugins/') && p.endsWith('.kt')) {
114
+ return { framework: 'ktor', entryPointMultiplier: 2.0, reason: 'ktor-plugin' };
115
+ }
116
+ if (p.endsWith('routing.kt') || p.endsWith('routes.kt')) {
117
+ return { framework: 'ktor', entryPointMultiplier: 2.5, reason: 'ktor-routing-file' };
118
+ }
119
+ // Android Activities, Fragments
120
+ if ((p.includes('/activity/') || p.includes('/ui/')) && p.endsWith('.kt')) {
121
+ return { framework: 'android-kotlin', entryPointMultiplier: 2.5, reason: 'android-ui' };
122
+ }
123
+ if (p.endsWith('activity.kt') || p.endsWith('fragment.kt')) {
124
+ return { framework: 'android-kotlin', entryPointMultiplier: 2.5, reason: 'android-component' };
125
+ }
126
+ // Kotlin main entry point
127
+ if (p.endsWith('/main.kt')) {
128
+ return { framework: 'kotlin', entryPointMultiplier: 3.0, reason: 'kotlin-main' };
129
+ }
130
+ // Kotlin Application entry point (common naming)
131
+ if (p.endsWith('/application.kt')) {
132
+ return { framework: 'kotlin', entryPointMultiplier: 2.5, reason: 'kotlin-application' };
133
+ }
97
134
  // ========== C# / .NET FRAMEWORKS ==========
98
135
  // ASP.NET Controllers
99
136
  if (p.includes('/controllers/') && p.endsWith('.cs')) {
@@ -103,6 +140,29 @@ export function detectFrameworkFromPath(filePath) {
103
140
  if (p.endsWith('controller.cs')) {
104
141
  return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-controller-file' };
105
142
  }
143
+ // ASP.NET Services
144
+ if ((p.includes('/services/') || p.includes('/service/')) && p.endsWith('.cs')) {
145
+ return { framework: 'aspnet', entryPointMultiplier: 1.8, reason: 'aspnet-service' };
146
+ }
147
+ // ASP.NET Middleware
148
+ if (p.includes('/middleware/') && p.endsWith('.cs')) {
149
+ return { framework: 'aspnet', entryPointMultiplier: 2.5, reason: 'aspnet-middleware' };
150
+ }
151
+ // SignalR Hubs
152
+ if (p.includes('/hubs/') && p.endsWith('.cs')) {
153
+ return { framework: 'signalr', entryPointMultiplier: 2.5, reason: 'signalr-hub' };
154
+ }
155
+ if (p.endsWith('hub.cs')) {
156
+ return { framework: 'signalr', entryPointMultiplier: 2.5, reason: 'signalr-hub-file' };
157
+ }
158
+ // Minimal API / Program.cs / Startup.cs
159
+ if (p.endsWith('/program.cs') || p.endsWith('/startup.cs')) {
160
+ return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-entry' };
161
+ }
162
+ // Background services / Hosted services
163
+ if ((p.includes('/backgroundservices/') || p.includes('/hostedservices/')) && p.endsWith('.cs')) {
164
+ return { framework: 'aspnet', entryPointMultiplier: 2.0, reason: 'aspnet-background-service' };
165
+ }
106
166
  // Blazor pages
107
167
  if (p.includes('/pages/') && p.endsWith('.razor')) {
108
168
  return { framework: 'blazor', entryPointMultiplier: 2.5, reason: 'blazor-page' };
@@ -195,6 +255,52 @@ export function detectFrameworkFromPath(filePath) {
195
255
  if (p.includes('/repositories/') && p.endsWith('.php')) {
196
256
  return { framework: 'laravel', entryPointMultiplier: 1.5, reason: 'laravel-repository' };
197
257
  }
258
+ // ========== RUBY ==========
259
+ // Ruby: bin/ or exe/ (CLI entry points)
260
+ if ((p.includes('/bin/') || p.includes('/exe/')) && p.endsWith('.rb')) {
261
+ return { framework: 'ruby', entryPointMultiplier: 2.5, reason: 'ruby-executable' };
262
+ }
263
+ // Ruby: Rakefile or *.rake (task definitions)
264
+ if (p.endsWith('/rakefile') || p.endsWith('.rake')) {
265
+ return { framework: 'ruby', entryPointMultiplier: 1.5, reason: 'ruby-rake' };
266
+ }
267
+ // ========== SWIFT / iOS ==========
268
+ // iOS App entry points (highest priority)
269
+ if (p.endsWith('/appdelegate.swift') || p.endsWith('/scenedelegate.swift') || p.endsWith('/app.swift')) {
270
+ return { framework: 'ios', entryPointMultiplier: 3.0, reason: 'ios-app-entry' };
271
+ }
272
+ // SwiftUI App entry (@main)
273
+ if (p.endsWith('app.swift') && p.includes('/sources/')) {
274
+ return { framework: 'swiftui', entryPointMultiplier: 3.0, reason: 'swiftui-app' };
275
+ }
276
+ // UIKit ViewControllers (high priority - screen entry points)
277
+ if ((p.includes('/viewcontrollers/') || p.includes('/controllers/') || p.includes('/screens/')) && p.endsWith('.swift')) {
278
+ return { framework: 'uikit', entryPointMultiplier: 2.5, reason: 'uikit-viewcontroller' };
279
+ }
280
+ // ViewController by filename convention
281
+ if (p.endsWith('viewcontroller.swift') || p.endsWith('vc.swift')) {
282
+ return { framework: 'uikit', entryPointMultiplier: 2.5, reason: 'uikit-viewcontroller-file' };
283
+ }
284
+ // Coordinator pattern (navigation entry points)
285
+ if (p.includes('/coordinators/') && p.endsWith('.swift')) {
286
+ return { framework: 'ios-coordinator', entryPointMultiplier: 2.5, reason: 'ios-coordinator' };
287
+ }
288
+ // Coordinator by filename
289
+ if (p.endsWith('coordinator.swift')) {
290
+ return { framework: 'ios-coordinator', entryPointMultiplier: 2.5, reason: 'ios-coordinator-file' };
291
+ }
292
+ // SwiftUI Views (moderate - reusable components)
293
+ if ((p.includes('/views/') || p.includes('/scenes/')) && p.endsWith('.swift')) {
294
+ return { framework: 'swiftui', entryPointMultiplier: 1.8, reason: 'swiftui-view' };
295
+ }
296
+ // Service layer
297
+ if (p.includes('/services/') && p.endsWith('.swift')) {
298
+ return { framework: 'ios-service', entryPointMultiplier: 1.8, reason: 'ios-service' };
299
+ }
300
+ // Router / navigation
301
+ if (p.includes('/router/') && p.endsWith('.swift')) {
302
+ return { framework: 'ios-router', entryPointMultiplier: 2.0, reason: 'ios-router' };
303
+ }
198
304
  // ========== GENERIC PATTERNS ==========
199
305
  // Any language: index files in API folders
200
306
  if (p.includes('/api/') && (p.endsWith('/index.ts') || p.endsWith('/index.js') ||
@@ -205,11 +311,11 @@ export function detectFrameworkFromPath(filePath) {
205
311
  return null;
206
312
  }
207
313
  // ============================================================================
208
- // FUTURE: AST-BASED PATTERNS (for Phase 3)
314
+ // AST-BASED FRAMEWORK DETECTION
209
315
  // ============================================================================
210
316
  /**
211
- * Patterns that indicate entry points within code (for future AST-based detection)
212
- * These would require parsing decorators/annotations in the code itself.
317
+ * Patterns that indicate framework entry points within code definitions.
318
+ * These are matched against AST node text (class/method/function declaration text).
213
319
  */
214
320
  export const FRAMEWORK_AST_PATTERNS = {
215
321
  // JavaScript/TypeScript decorators
@@ -222,7 +328,11 @@ export const FRAMEWORK_AST_PATTERNS = {
222
328
  'spring': ['@RestController', '@Controller', '@GetMapping', '@PostMapping', '@RequestMapping'],
223
329
  'jaxrs': ['@Path', '@GET', '@POST', '@PUT', '@DELETE'],
224
330
  // C# attributes
225
- 'aspnet': ['[ApiController]', '[HttpGet]', '[HttpPost]', '[Route]'],
331
+ 'aspnet': ['[ApiController]', '[HttpGet]', '[HttpPost]', '[HttpPut]', '[HttpDelete]',
332
+ '[Route]', '[Authorize]', '[AllowAnonymous]'],
333
+ 'signalr': ['[HubMethodName]', ': Hub', ': Hub<'],
334
+ 'blazor': ['@page', '[Parameter]', '@inject'],
335
+ 'efcore': ['DbContext', 'DbSet<', 'OnModelCreating'],
226
336
  // Go patterns (function signatures)
227
337
  'go-http': ['http.Handler', 'http.HandlerFunc', 'ServeHTTP'],
228
338
  // PHP/Laravel
@@ -232,4 +342,70 @@ export const FRAMEWORK_AST_PATTERNS = {
232
342
  'actix': ['#[get', '#[post', '#[put', '#[delete'],
233
343
  'axum': ['Router::new'],
234
344
  'rocket': ['#[get', '#[post'],
345
+ // Swift/iOS
346
+ 'uikit': ['viewDidLoad', 'viewWillAppear', 'viewDidAppear', 'UIViewController'],
347
+ 'swiftui': ['@main', 'WindowGroup', 'ContentView', '@StateObject', '@ObservedObject'],
348
+ 'combine': ['sink', 'assign', 'Publisher', 'Subscriber'],
349
+ };
350
+ import { SupportedLanguages } from '../../config/supported-languages.js';
351
+ const AST_FRAMEWORK_PATTERNS_BY_LANGUAGE = {
352
+ [SupportedLanguages.JavaScript]: [
353
+ { framework: 'nestjs', entryPointMultiplier: 3.2, reason: 'nestjs-decorator', patterns: FRAMEWORK_AST_PATTERNS.nestjs },
354
+ ],
355
+ [SupportedLanguages.TypeScript]: [
356
+ { framework: 'nestjs', entryPointMultiplier: 3.2, reason: 'nestjs-decorator', patterns: FRAMEWORK_AST_PATTERNS.nestjs },
357
+ ],
358
+ [SupportedLanguages.Python]: [
359
+ { framework: 'fastapi', entryPointMultiplier: 3.0, reason: 'fastapi-decorator', patterns: FRAMEWORK_AST_PATTERNS.fastapi },
360
+ { framework: 'flask', entryPointMultiplier: 2.8, reason: 'flask-decorator', patterns: FRAMEWORK_AST_PATTERNS.flask },
361
+ ],
362
+ [SupportedLanguages.Java]: [
363
+ { framework: 'spring', entryPointMultiplier: 3.2, reason: 'spring-annotation', patterns: FRAMEWORK_AST_PATTERNS.spring },
364
+ { framework: 'jaxrs', entryPointMultiplier: 3.0, reason: 'jaxrs-annotation', patterns: FRAMEWORK_AST_PATTERNS.jaxrs },
365
+ ],
366
+ [SupportedLanguages.Kotlin]: [
367
+ { framework: 'spring-kotlin', entryPointMultiplier: 3.2, reason: 'spring-kotlin-annotation', patterns: FRAMEWORK_AST_PATTERNS.spring },
368
+ { framework: 'jaxrs', entryPointMultiplier: 3.0, reason: 'jaxrs-annotation', patterns: FRAMEWORK_AST_PATTERNS.jaxrs },
369
+ { framework: 'ktor', entryPointMultiplier: 2.8, reason: 'ktor-routing', patterns: ['routing', 'embeddedServer', 'Application.module'] },
370
+ { framework: 'android-kotlin', entryPointMultiplier: 2.5, reason: 'android-annotation', patterns: ['@AndroidEntryPoint', 'AppCompatActivity', 'Fragment('] },
371
+ ],
372
+ [SupportedLanguages.CSharp]: [
373
+ { framework: 'aspnet', entryPointMultiplier: 3.2, reason: 'aspnet-attribute', patterns: FRAMEWORK_AST_PATTERNS.aspnet },
374
+ { framework: 'signalr', entryPointMultiplier: 2.8, reason: 'signalr-attribute', patterns: FRAMEWORK_AST_PATTERNS.signalr },
375
+ { framework: 'blazor', entryPointMultiplier: 2.5, reason: 'blazor-attribute', patterns: FRAMEWORK_AST_PATTERNS.blazor },
376
+ { framework: 'efcore', entryPointMultiplier: 2.0, reason: 'efcore-pattern', patterns: FRAMEWORK_AST_PATTERNS.efcore },
377
+ ],
378
+ [SupportedLanguages.PHP]: [
379
+ { framework: 'laravel', entryPointMultiplier: 3.0, reason: 'php-route-attribute', patterns: FRAMEWORK_AST_PATTERNS.laravel },
380
+ ],
235
381
  };
382
+ /** Pre-lowercased patterns for O(1) pattern matching at runtime */
383
+ const AST_PATTERNS_LOWERED = Object.fromEntries(Object.entries(AST_FRAMEWORK_PATTERNS_BY_LANGUAGE).map(([lang, cfgs]) => [
384
+ lang,
385
+ cfgs.map(cfg => ({ ...cfg, patterns: cfg.patterns.map(p => p.toLowerCase()) })),
386
+ ]));
387
+ /**
388
+ * Detect framework entry points from AST definition text (decorators/annotations/attributes).
389
+ * Returns null if no known pattern is found.
390
+ * Note: callers should slice definitionText to ~300 chars since annotations appear at the start.
391
+ */
392
+ export function detectFrameworkFromAST(language, definitionText) {
393
+ if (!language || !definitionText)
394
+ return null;
395
+ const configs = AST_PATTERNS_LOWERED[language.toLowerCase()];
396
+ if (!configs || configs.length === 0)
397
+ return null;
398
+ const normalized = definitionText.toLowerCase();
399
+ for (const cfg of configs) {
400
+ for (const pattern of cfg.patterns) {
401
+ if (normalized.includes(pattern)) {
402
+ return {
403
+ framework: cfg.framework,
404
+ entryPointMultiplier: cfg.entryPointMultiplier,
405
+ reason: cfg.reason,
406
+ };
407
+ }
408
+ }
409
+ }
410
+ return null;
411
+ }
@@ -2,19 +2,27 @@
2
2
  * Heritage Processor
3
3
  *
4
4
  * Extracts class inheritance relationships:
5
- * - EXTENDS: Class extends another Class (TS, JS, Python)
6
- * - IMPLEMENTS: Class implements an Interface (TS only)
5
+ * - EXTENDS: Class extends another Class (TS, JS, Python, C#, C++)
6
+ * - IMPLEMENTS: Class implements an Interface (TS, C#, Java, Kotlin, PHP)
7
+ *
8
+ * Languages like C# use a single `base_list` for both class and interface parents.
9
+ * We resolve the correct edge type by checking the symbol table: if the parent is
10
+ * registered as an Interface, we emit IMPLEMENTS; otherwise EXTENDS. For unresolved
11
+ * external symbols, the fallback heuristic is language-gated:
12
+ * - C# / Java: apply the `I[A-Z]` naming convention (e.g. IDisposable → IMPLEMENTS)
13
+ * - Swift: default to IMPLEMENTS (protocol conformance is more common than class inheritance)
14
+ * - All other languages: default to EXTENDS
7
15
  */
8
16
  import { KnowledgeGraph } from '../graph/types.js';
9
17
  import { ASTCache } from './ast-cache.js';
10
- import { SymbolTable } from './symbol-table.js';
11
18
  import type { ExtractedHeritage } from './workers/parse-worker.js';
19
+ import type { ResolutionContext } from './resolution-context.js';
12
20
  export declare const processHeritage: (graph: KnowledgeGraph, files: {
13
21
  path: string;
14
22
  content: string;
15
- }[], astCache: ASTCache, symbolTable: SymbolTable, onProgress?: (current: number, total: number) => void) => Promise<void>;
23
+ }[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;
16
24
  /**
17
25
  * Fast path: resolve pre-extracted heritage from workers.
18
26
  * No AST parsing — workers already extracted className + parentName + kind.
19
27
  */
20
- export declare const processHeritageFromExtracted: (graph: KnowledgeGraph, extractedHeritage: ExtractedHeritage[], symbolTable: SymbolTable, onProgress?: (current: number, total: number) => void) => Promise<void>;
28
+ export declare const processHeritageFromExtracted: (graph: KnowledgeGraph, extractedHeritage: ExtractedHeritage[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void) => Promise<void>;