@duytransipher/gitnexus 1.4.6-sipher.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 (224) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +261 -0
  3. package/dist/cli/ai-context.d.ts +23 -0
  4. package/dist/cli/ai-context.js +265 -0
  5. package/dist/cli/analyze.d.ts +12 -0
  6. package/dist/cli/analyze.js +345 -0
  7. package/dist/cli/augment.d.ts +13 -0
  8. package/dist/cli/augment.js +33 -0
  9. package/dist/cli/clean.d.ts +10 -0
  10. package/dist/cli/clean.js +60 -0
  11. package/dist/cli/eval-server.d.ts +37 -0
  12. package/dist/cli/eval-server.js +389 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +137 -0
  15. package/dist/cli/lazy-action.d.ts +6 -0
  16. package/dist/cli/lazy-action.js +18 -0
  17. package/dist/cli/list.d.ts +6 -0
  18. package/dist/cli/list.js +30 -0
  19. package/dist/cli/mcp.d.ts +8 -0
  20. package/dist/cli/mcp.js +36 -0
  21. package/dist/cli/serve.d.ts +4 -0
  22. package/dist/cli/serve.js +6 -0
  23. package/dist/cli/setup.d.ts +8 -0
  24. package/dist/cli/setup.js +367 -0
  25. package/dist/cli/sipher-patched.d.ts +2 -0
  26. package/dist/cli/sipher-patched.js +77 -0
  27. package/dist/cli/skill-gen.d.ts +26 -0
  28. package/dist/cli/skill-gen.js +549 -0
  29. package/dist/cli/status.d.ts +6 -0
  30. package/dist/cli/status.js +36 -0
  31. package/dist/cli/tool.d.ts +60 -0
  32. package/dist/cli/tool.js +180 -0
  33. package/dist/cli/wiki.d.ts +15 -0
  34. package/dist/cli/wiki.js +365 -0
  35. package/dist/config/ignore-service.d.ts +26 -0
  36. package/dist/config/ignore-service.js +284 -0
  37. package/dist/config/supported-languages.d.ts +15 -0
  38. package/dist/config/supported-languages.js +16 -0
  39. package/dist/core/augmentation/engine.d.ts +26 -0
  40. package/dist/core/augmentation/engine.js +240 -0
  41. package/dist/core/embeddings/embedder.d.ts +60 -0
  42. package/dist/core/embeddings/embedder.js +251 -0
  43. package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
  44. package/dist/core/embeddings/embedding-pipeline.js +356 -0
  45. package/dist/core/embeddings/index.d.ts +9 -0
  46. package/dist/core/embeddings/index.js +9 -0
  47. package/dist/core/embeddings/text-generator.d.ts +24 -0
  48. package/dist/core/embeddings/text-generator.js +182 -0
  49. package/dist/core/embeddings/types.d.ts +87 -0
  50. package/dist/core/embeddings/types.js +32 -0
  51. package/dist/core/graph/graph.d.ts +2 -0
  52. package/dist/core/graph/graph.js +66 -0
  53. package/dist/core/graph/types.d.ts +66 -0
  54. package/dist/core/graph/types.js +1 -0
  55. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  56. package/dist/core/ingestion/ast-cache.js +35 -0
  57. package/dist/core/ingestion/call-processor.d.ts +23 -0
  58. package/dist/core/ingestion/call-processor.js +793 -0
  59. package/dist/core/ingestion/call-routing.d.ts +68 -0
  60. package/dist/core/ingestion/call-routing.js +129 -0
  61. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  62. package/dist/core/ingestion/cluster-enricher.js +170 -0
  63. package/dist/core/ingestion/community-processor.d.ts +39 -0
  64. package/dist/core/ingestion/community-processor.js +312 -0
  65. package/dist/core/ingestion/constants.d.ts +16 -0
  66. package/dist/core/ingestion/constants.js +16 -0
  67. package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
  68. package/dist/core/ingestion/entry-point-scoring.js +353 -0
  69. package/dist/core/ingestion/export-detection.d.ts +18 -0
  70. package/dist/core/ingestion/export-detection.js +231 -0
  71. package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
  72. package/dist/core/ingestion/filesystem-walker.js +81 -0
  73. package/dist/core/ingestion/framework-detection.d.ts +54 -0
  74. package/dist/core/ingestion/framework-detection.js +411 -0
  75. package/dist/core/ingestion/heritage-processor.d.ts +28 -0
  76. package/dist/core/ingestion/heritage-processor.js +251 -0
  77. package/dist/core/ingestion/import-processor.d.ts +34 -0
  78. package/dist/core/ingestion/import-processor.js +398 -0
  79. package/dist/core/ingestion/language-config.d.ts +46 -0
  80. package/dist/core/ingestion/language-config.js +167 -0
  81. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  82. package/dist/core/ingestion/mro-processor.js +369 -0
  83. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  84. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  85. package/dist/core/ingestion/parsing-processor.d.ts +19 -0
  86. package/dist/core/ingestion/parsing-processor.js +315 -0
  87. package/dist/core/ingestion/pipeline.d.ts +6 -0
  88. package/dist/core/ingestion/pipeline.js +401 -0
  89. package/dist/core/ingestion/process-processor.d.ts +51 -0
  90. package/dist/core/ingestion/process-processor.js +315 -0
  91. package/dist/core/ingestion/resolution-context.d.ts +53 -0
  92. package/dist/core/ingestion/resolution-context.js +132 -0
  93. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  94. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  95. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  96. package/dist/core/ingestion/resolvers/go.js +42 -0
  97. package/dist/core/ingestion/resolvers/index.d.ts +18 -0
  98. package/dist/core/ingestion/resolvers/index.js +13 -0
  99. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  100. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  101. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  102. package/dist/core/ingestion/resolvers/php.js +35 -0
  103. package/dist/core/ingestion/resolvers/python.d.ts +19 -0
  104. package/dist/core/ingestion/resolvers/python.js +52 -0
  105. package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
  106. package/dist/core/ingestion/resolvers/ruby.js +15 -0
  107. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  108. package/dist/core/ingestion/resolvers/rust.js +73 -0
  109. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  110. package/dist/core/ingestion/resolvers/standard.js +123 -0
  111. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  112. package/dist/core/ingestion/resolvers/utils.js +122 -0
  113. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  114. package/dist/core/ingestion/structure-processor.js +36 -0
  115. package/dist/core/ingestion/symbol-table.d.ts +63 -0
  116. package/dist/core/ingestion/symbol-table.js +85 -0
  117. package/dist/core/ingestion/tree-sitter-queries.d.ts +15 -0
  118. package/dist/core/ingestion/tree-sitter-queries.js +888 -0
  119. package/dist/core/ingestion/type-env.d.ts +49 -0
  120. package/dist/core/ingestion/type-env.js +613 -0
  121. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
  123. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  124. package/dist/core/ingestion/type-extractors/csharp.js +383 -0
  125. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  126. package/dist/core/ingestion/type-extractors/go.js +467 -0
  127. package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
  128. package/dist/core/ingestion/type-extractors/index.js +31 -0
  129. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  130. package/dist/core/ingestion/type-extractors/jvm.js +681 -0
  131. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  132. package/dist/core/ingestion/type-extractors/php.js +549 -0
  133. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  134. package/dist/core/ingestion/type-extractors/python.js +455 -0
  135. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  136. package/dist/core/ingestion/type-extractors/ruby.js +389 -0
  137. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  138. package/dist/core/ingestion/type-extractors/rust.js +456 -0
  139. package/dist/core/ingestion/type-extractors/shared.d.ts +145 -0
  140. package/dist/core/ingestion/type-extractors/shared.js +810 -0
  141. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  142. package/dist/core/ingestion/type-extractors/swift.js +137 -0
  143. package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
  144. package/dist/core/ingestion/type-extractors/types.js +1 -0
  145. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  146. package/dist/core/ingestion/type-extractors/typescript.js +494 -0
  147. package/dist/core/ingestion/utils.d.ts +138 -0
  148. package/dist/core/ingestion/utils.js +1290 -0
  149. package/dist/core/ingestion/workers/parse-worker.d.ts +122 -0
  150. package/dist/core/ingestion/workers/parse-worker.js +1126 -0
  151. package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
  152. package/dist/core/ingestion/workers/worker-pool.js +128 -0
  153. package/dist/core/lbug/csv-generator.d.ts +33 -0
  154. package/dist/core/lbug/csv-generator.js +366 -0
  155. package/dist/core/lbug/lbug-adapter.d.ts +103 -0
  156. package/dist/core/lbug/lbug-adapter.js +769 -0
  157. package/dist/core/lbug/schema.d.ts +53 -0
  158. package/dist/core/lbug/schema.js +430 -0
  159. package/dist/core/search/bm25-index.d.ts +23 -0
  160. package/dist/core/search/bm25-index.js +96 -0
  161. package/dist/core/search/hybrid-search.d.ts +49 -0
  162. package/dist/core/search/hybrid-search.js +118 -0
  163. package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
  164. package/dist/core/tree-sitter/parser-loader.js +63 -0
  165. package/dist/core/wiki/generator.d.ts +120 -0
  166. package/dist/core/wiki/generator.js +939 -0
  167. package/dist/core/wiki/graph-queries.d.ts +80 -0
  168. package/dist/core/wiki/graph-queries.js +238 -0
  169. package/dist/core/wiki/html-viewer.d.ts +10 -0
  170. package/dist/core/wiki/html-viewer.js +297 -0
  171. package/dist/core/wiki/llm-client.d.ts +43 -0
  172. package/dist/core/wiki/llm-client.js +186 -0
  173. package/dist/core/wiki/prompts.d.ts +53 -0
  174. package/dist/core/wiki/prompts.js +174 -0
  175. package/dist/lib/utils.d.ts +1 -0
  176. package/dist/lib/utils.js +3 -0
  177. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  178. package/dist/mcp/compatible-stdio-transport.js +200 -0
  179. package/dist/mcp/core/embedder.d.ts +27 -0
  180. package/dist/mcp/core/embedder.js +108 -0
  181. package/dist/mcp/core/lbug-adapter.d.ts +57 -0
  182. package/dist/mcp/core/lbug-adapter.js +455 -0
  183. package/dist/mcp/local/local-backend.d.ts +181 -0
  184. package/dist/mcp/local/local-backend.js +1722 -0
  185. package/dist/mcp/resources.d.ts +31 -0
  186. package/dist/mcp/resources.js +411 -0
  187. package/dist/mcp/server.d.ts +23 -0
  188. package/dist/mcp/server.js +296 -0
  189. package/dist/mcp/staleness.d.ts +15 -0
  190. package/dist/mcp/staleness.js +29 -0
  191. package/dist/mcp/tools.d.ts +24 -0
  192. package/dist/mcp/tools.js +292 -0
  193. package/dist/server/api.d.ts +10 -0
  194. package/dist/server/api.js +344 -0
  195. package/dist/server/mcp-http.d.ts +13 -0
  196. package/dist/server/mcp-http.js +100 -0
  197. package/dist/storage/git.d.ts +6 -0
  198. package/dist/storage/git.js +35 -0
  199. package/dist/storage/repo-manager.d.ts +138 -0
  200. package/dist/storage/repo-manager.js +299 -0
  201. package/dist/types/pipeline.d.ts +32 -0
  202. package/dist/types/pipeline.js +18 -0
  203. package/dist/unreal/bridge.d.ts +4 -0
  204. package/dist/unreal/bridge.js +113 -0
  205. package/dist/unreal/config.d.ts +6 -0
  206. package/dist/unreal/config.js +55 -0
  207. package/dist/unreal/types.d.ts +105 -0
  208. package/dist/unreal/types.js +1 -0
  209. package/hooks/claude/gitnexus-hook.cjs +238 -0
  210. package/hooks/claude/pre-tool-use.sh +79 -0
  211. package/hooks/claude/session-start.sh +42 -0
  212. package/package.json +100 -0
  213. package/scripts/ensure-cli-executable.cjs +21 -0
  214. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  215. package/scripts/setup-unreal-gitnexus.ps1 +191 -0
  216. package/skills/gitnexus-cli.md +82 -0
  217. package/skills/gitnexus-debugging.md +89 -0
  218. package/skills/gitnexus-exploring.md +78 -0
  219. package/skills/gitnexus-guide.md +64 -0
  220. package/skills/gitnexus-impact-analysis.md +97 -0
  221. package/skills/gitnexus-pr-review.md +163 -0
  222. package/skills/gitnexus-refactoring.md +121 -0
  223. package/vendor/leiden/index.cjs +355 -0
  224. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,494 @@
1
+ import { extractSimpleTypeName, extractVarName, hasTypeAnnotation, unwrapAwait, extractCalleeName, extractElementTypeFromString, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition } from './shared.js';
2
+ const DECLARATION_NODE_TYPES = new Set([
3
+ 'lexical_declaration',
4
+ 'variable_declaration',
5
+ 'function_declaration', // JSDoc @param on function declarations
6
+ 'method_definition', // JSDoc @param on class methods
7
+ 'public_field_definition', // class field: private users: User[]
8
+ ]);
9
+ const normalizeJsDocType = (raw) => {
10
+ let type = raw.trim();
11
+ // Strip JSDoc nullable/non-nullable prefixes: ?User → User, !User → User
12
+ if (type.startsWith('?') || type.startsWith('!'))
13
+ type = type.slice(1);
14
+ // Strip union with null/undefined/void: User|null → User
15
+ const parts = type.split('|').map(p => p.trim()).filter(p => p !== 'null' && p !== 'undefined' && p !== 'void');
16
+ if (parts.length !== 1)
17
+ return undefined; // ambiguous union
18
+ type = parts[0];
19
+ // Strip module: prefix — module:models.User → models.User
20
+ if (type.startsWith('module:'))
21
+ type = type.slice(7);
22
+ // Take last segment of dotted path: models.User → User
23
+ const segments = type.split('.');
24
+ type = segments[segments.length - 1];
25
+ // Strip generic wrapper: Promise<User> → Promise (base type, not inner)
26
+ const genericMatch = type.match(/^(\w+)\s*</);
27
+ if (genericMatch)
28
+ type = genericMatch[1];
29
+ // Simple identifier check
30
+ if (/^\w+$/.test(type))
31
+ return type;
32
+ return undefined;
33
+ };
34
+ /** Regex to extract JSDoc @param annotations: `@param {Type} name` */
35
+ const JSDOC_PARAM_RE = /@param\s*\{([^}]+)\}\s+\[?(\w+)[\]=]?[^\s]*/g;
36
+ /**
37
+ * Collect JSDoc @param type bindings from comment nodes preceding a function/method.
38
+ * Returns a map of paramName → typeName.
39
+ */
40
+ const collectJsDocParams = (funcNode) => {
41
+ const commentTexts = [];
42
+ let sibling = funcNode.previousSibling;
43
+ while (sibling) {
44
+ if (sibling.type === 'comment') {
45
+ commentTexts.unshift(sibling.text);
46
+ }
47
+ else if (sibling.isNamed && sibling.type !== 'decorator') {
48
+ break;
49
+ }
50
+ sibling = sibling.previousSibling;
51
+ }
52
+ if (commentTexts.length === 0)
53
+ return new Map();
54
+ const params = new Map();
55
+ const commentBlock = commentTexts.join('\n');
56
+ JSDOC_PARAM_RE.lastIndex = 0;
57
+ let match;
58
+ while ((match = JSDOC_PARAM_RE.exec(commentBlock)) !== null) {
59
+ const typeName = normalizeJsDocType(match[1]);
60
+ const paramName = match[2];
61
+ if (typeName) {
62
+ params.set(paramName, typeName);
63
+ }
64
+ }
65
+ return params;
66
+ };
67
+ /**
68
+ * TypeScript: const x: Foo = ..., let x: Foo
69
+ * Also: JSDoc @param annotations on function/method definitions (for .js files).
70
+ */
71
+ const extractDeclaration = (node, env) => {
72
+ // JSDoc @param on functions/methods — pre-populate env with param types
73
+ if (node.type === 'function_declaration' || node.type === 'method_definition') {
74
+ const jsDocParams = collectJsDocParams(node);
75
+ for (const [paramName, typeName] of jsDocParams) {
76
+ if (!env.has(paramName))
77
+ env.set(paramName, typeName);
78
+ }
79
+ return;
80
+ }
81
+ // Class field: `private users: User[]` — public_field_definition has name + type fields directly.
82
+ if (node.type === 'public_field_definition') {
83
+ const nameNode = node.childForFieldName('name');
84
+ const typeAnnotation = node.childForFieldName('type');
85
+ if (!nameNode || !typeAnnotation)
86
+ return;
87
+ const varName = nameNode.text;
88
+ if (!varName)
89
+ return;
90
+ const typeName = extractSimpleTypeName(typeAnnotation);
91
+ if (typeName)
92
+ env.set(varName, typeName);
93
+ return;
94
+ }
95
+ for (let i = 0; i < node.namedChildCount; i++) {
96
+ const declarator = node.namedChild(i);
97
+ if (declarator?.type !== 'variable_declarator')
98
+ continue;
99
+ const nameNode = declarator.childForFieldName('name');
100
+ const typeAnnotation = declarator.childForFieldName('type');
101
+ if (!nameNode || !typeAnnotation)
102
+ continue;
103
+ const varName = extractVarName(nameNode);
104
+ const typeName = extractSimpleTypeName(typeAnnotation);
105
+ if (varName && typeName)
106
+ env.set(varName, typeName);
107
+ }
108
+ };
109
+ /** TypeScript: required_parameter / optional_parameter → name: type */
110
+ const extractParameter = (node, env) => {
111
+ let nameNode = null;
112
+ let typeNode = null;
113
+ if (node.type === 'required_parameter' || node.type === 'optional_parameter') {
114
+ nameNode = node.childForFieldName('pattern') ?? node.childForFieldName('name');
115
+ typeNode = node.childForFieldName('type');
116
+ }
117
+ else {
118
+ // Generic fallback
119
+ nameNode = node.childForFieldName('name') ?? node.childForFieldName('pattern');
120
+ typeNode = node.childForFieldName('type');
121
+ }
122
+ if (!nameNode || !typeNode)
123
+ return;
124
+ const varName = extractVarName(nameNode);
125
+ const typeName = extractSimpleTypeName(typeNode);
126
+ if (varName && typeName)
127
+ env.set(varName, typeName);
128
+ };
129
+ /** TypeScript: const x = new User() — infer type from new_expression */
130
+ const extractInitializer = (node, env, _classNames) => {
131
+ for (let i = 0; i < node.namedChildCount; i++) {
132
+ const declarator = node.namedChild(i);
133
+ if (declarator?.type !== 'variable_declarator')
134
+ continue;
135
+ // Only activate when there is no explicit type annotation — extractDeclaration already
136
+ // handles the annotated case and this function is called as a fallback.
137
+ if (declarator.childForFieldName('type') !== null)
138
+ continue;
139
+ let valueNode = declarator.childForFieldName('value');
140
+ // Unwrap `new User() as T`, `new User()!`, and double-cast `new User() as unknown as T`
141
+ while (valueNode?.type === 'as_expression' || valueNode?.type === 'non_null_expression') {
142
+ valueNode = valueNode.firstNamedChild;
143
+ }
144
+ if (valueNode?.type !== 'new_expression')
145
+ continue;
146
+ const constructorNode = valueNode.childForFieldName('constructor');
147
+ if (!constructorNode)
148
+ continue;
149
+ const nameNode = declarator.childForFieldName('name');
150
+ if (!nameNode)
151
+ continue;
152
+ const varName = extractVarName(nameNode);
153
+ const typeName = extractSimpleTypeName(constructorNode);
154
+ if (varName && typeName)
155
+ env.set(varName, typeName);
156
+ }
157
+ };
158
+ /**
159
+ * TypeScript/JavaScript: const user = getUser() — variable_declarator with call_expression value.
160
+ * Only matches unannotated declarators; annotated ones are handled by extractDeclaration.
161
+ * await is unwrapped: const user = await fetchUser() → callee = 'fetchUser'.
162
+ */
163
+ const scanConstructorBinding = (node) => {
164
+ if (node.type !== 'variable_declarator')
165
+ return undefined;
166
+ if (hasTypeAnnotation(node))
167
+ return undefined;
168
+ const nameNode = node.childForFieldName('name');
169
+ if (!nameNode || nameNode.type !== 'identifier')
170
+ return undefined;
171
+ const value = unwrapAwait(node.childForFieldName('value'));
172
+ if (!value || value.type !== 'call_expression')
173
+ return undefined;
174
+ const calleeName = extractCalleeName(value);
175
+ if (!calleeName)
176
+ return undefined;
177
+ return { varName: nameNode.text, calleeName };
178
+ };
179
+ /** Regex to extract @returns or @return from JSDoc comments: `@returns {Type}` */
180
+ const JSDOC_RETURN_RE = /@returns?\s*\{([^}]+)\}/;
181
+ /**
182
+ * Minimal sanitization for JSDoc return types — preserves generic wrappers
183
+ * (e.g. `Promise<User>`) so that extractReturnTypeName in call-processor
184
+ * can apply WRAPPER_GENERICS unwrapping. Unlike normalizeJsDocType (which
185
+ * strips generics), this only strips JSDoc-specific syntax markers.
186
+ */
187
+ const sanitizeReturnType = (raw) => {
188
+ let type = raw.trim();
189
+ // Strip JSDoc nullable/non-nullable prefixes: ?User → User, !User → User
190
+ if (type.startsWith('?') || type.startsWith('!'))
191
+ type = type.slice(1);
192
+ // Strip module: prefix — module:models.User → models.User
193
+ if (type.startsWith('module:'))
194
+ type = type.slice(7);
195
+ // Reject unions (ambiguous)
196
+ if (type.includes('|'))
197
+ return undefined;
198
+ if (!type)
199
+ return undefined;
200
+ return type;
201
+ };
202
+ /**
203
+ * Extract return type from JSDoc `@returns {Type}` or `@return {Type}` annotation
204
+ * preceding a function/method definition. Walks backwards through preceding siblings
205
+ * looking for comment nodes containing the annotation.
206
+ */
207
+ const extractReturnType = (node) => {
208
+ let sibling = node.previousSibling;
209
+ while (sibling) {
210
+ if (sibling.type === 'comment') {
211
+ const match = JSDOC_RETURN_RE.exec(sibling.text);
212
+ if (match)
213
+ return sanitizeReturnType(match[1]);
214
+ }
215
+ else if (sibling.isNamed && sibling.type !== 'decorator')
216
+ break;
217
+ sibling = sibling.previousSibling;
218
+ }
219
+ return undefined;
220
+ };
221
+ const FOR_LOOP_NODE_TYPES = new Set([
222
+ 'for_in_statement',
223
+ ]);
224
+ /** TS function/method node types that carry a parameters list. */
225
+ const TS_FUNCTION_NODE_TYPES = new Set([
226
+ 'function_declaration', 'function_expression', 'arrow_function',
227
+ 'method_definition', 'generator_function', 'generator_function_declaration',
228
+ ]);
229
+ /**
230
+ * Extract element type from a TypeScript type annotation AST node.
231
+ * Handles:
232
+ * type_annotation ": User[]" → array_type → type_identifier "User"
233
+ * type_annotation ": Array<User>" → generic_type → extractGenericTypeArgs → "User"
234
+ * Falls back to text-based extraction via extractElementTypeFromString.
235
+ */
236
+ const extractTsElementTypeFromAnnotation = (typeAnnotation, pos = 'last', depth = 0) => {
237
+ if (depth > 50)
238
+ return undefined;
239
+ // Unwrap type_annotation (the node text includes ': ' prefix)
240
+ const inner = typeAnnotation.type === 'type_annotation'
241
+ ? (typeAnnotation.firstNamedChild ?? typeAnnotation)
242
+ : typeAnnotation;
243
+ // readonly User[] — readonly_type wraps array_type: unwrap and recurse
244
+ if (inner.type === 'readonly_type') {
245
+ const wrapped = inner.firstNamedChild;
246
+ if (wrapped)
247
+ return extractTsElementTypeFromAnnotation(wrapped, pos, depth + 1);
248
+ }
249
+ // User[] — array_type: first named child is the element type
250
+ if (inner.type === 'array_type') {
251
+ const elem = inner.firstNamedChild;
252
+ if (elem)
253
+ return extractSimpleTypeName(elem);
254
+ }
255
+ // Array<User>, Map<string, User> — generic_type
256
+ // pos determines which type arg: 'first' for keys, 'last' for values
257
+ if (inner.type === 'generic_type') {
258
+ const args = extractGenericTypeArgs(inner);
259
+ if (args.length >= 1)
260
+ return pos === 'first' ? args[0] : args[args.length - 1];
261
+ }
262
+ // Fallback: strip ': ' prefix from type_annotation text and use string extraction
263
+ const rawText = inner.text;
264
+ return extractElementTypeFromString(rawText, pos);
265
+ };
266
+ /**
267
+ * Search a statement_block (function body) for a variable_declarator named `iterableName`
268
+ * that has a type annotation, preceding the given `beforeNode`.
269
+ * Returns the element type from the type annotation, or undefined.
270
+ */
271
+ const findTsLocalDeclElementType = (iterableName, blockNode, beforeNode, pos = 'last') => {
272
+ for (let i = 0; i < blockNode.namedChildCount; i++) {
273
+ const stmt = blockNode.namedChild(i);
274
+ if (!stmt)
275
+ continue;
276
+ // Stop when we reach the for-loop itself
277
+ if (stmt === beforeNode || stmt.startIndex >= beforeNode.startIndex)
278
+ break;
279
+ // Look for lexical_declaration or variable_declaration
280
+ if (stmt.type !== 'lexical_declaration' && stmt.type !== 'variable_declaration')
281
+ continue;
282
+ for (let j = 0; j < stmt.namedChildCount; j++) {
283
+ const decl = stmt.namedChild(j);
284
+ if (decl?.type !== 'variable_declarator')
285
+ continue;
286
+ const nameNode = decl.childForFieldName('name');
287
+ if (nameNode?.text !== iterableName)
288
+ continue;
289
+ const typeAnnotation = decl.childForFieldName('type');
290
+ if (typeAnnotation)
291
+ return extractTsElementTypeFromAnnotation(typeAnnotation, pos);
292
+ }
293
+ }
294
+ return undefined;
295
+ };
296
+ /**
297
+ * Walk up the AST from a for-loop node to find the enclosing function scope,
298
+ * then search (1) its parameter list and (2) local declarations in the body
299
+ * for a variable named `iterableName` with a container type annotation.
300
+ * Returns the element type extracted from the annotation, or undefined.
301
+ */
302
+ const findTsIterableElementType = (iterableName, startNode, pos = 'last') => {
303
+ let current = startNode.parent;
304
+ // Capture the immediate statement_block parent to search local declarations
305
+ const blockNode = current?.type === 'statement_block' ? current : null;
306
+ while (current) {
307
+ if (TS_FUNCTION_NODE_TYPES.has(current.type)) {
308
+ // Search function parameters
309
+ const paramsNode = current.childForFieldName('parameters')
310
+ ?? current.childForFieldName('formal_parameters');
311
+ if (paramsNode) {
312
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
313
+ const param = paramsNode.namedChild(i);
314
+ if (!param)
315
+ continue;
316
+ const patternNode = param.childForFieldName('pattern') ?? param.childForFieldName('name');
317
+ if (patternNode?.text === iterableName) {
318
+ const typeAnnotation = param.childForFieldName('type');
319
+ if (typeAnnotation)
320
+ return extractTsElementTypeFromAnnotation(typeAnnotation, pos);
321
+ }
322
+ }
323
+ }
324
+ // Search local declarations in the function body (statement_block)
325
+ if (blockNode) {
326
+ const result = findTsLocalDeclElementType(iterableName, blockNode, startNode, pos);
327
+ if (result)
328
+ return result;
329
+ }
330
+ break; // stop at the nearest function boundary
331
+ }
332
+ current = current.parent;
333
+ }
334
+ return undefined;
335
+ };
336
+ /**
337
+ * TypeScript/JavaScript: for (const user of users) where users has a known array type.
338
+ *
339
+ * Both `for...of` and `for...in` use the same `for_in_statement` AST node in tree-sitter.
340
+ * We differentiate by checking for the `of` keyword among the unnamed children.
341
+ *
342
+ * Tier 1c: resolves the element type via three strategies in priority order:
343
+ * 1. declarationTypeNodes — raw type annotation AST node (covers Array<User> from declarations)
344
+ * 2. scopeEnv string — extractElementTypeFromString on the stored type (covers locally annotated vars)
345
+ * 3. AST walk — walks up to the enclosing function's parameters to read User[] annotations directly
346
+ * Only handles `for...of`; `for...in` produces string keys, not element types.
347
+ */
348
+ const extractForLoopBinding = (node, { scopeEnv, declarationTypeNodes, scope, returnTypeLookup }) => {
349
+ if (node.type !== 'for_in_statement')
350
+ return;
351
+ // Confirm this is `for...of`, not `for...in`, by scanning unnamed children for the keyword text.
352
+ let isForOf = false;
353
+ for (let i = 0; i < node.childCount; i++) {
354
+ const child = node.child(i);
355
+ if (child && !child.isNamed && child.text === 'of') {
356
+ isForOf = true;
357
+ break;
358
+ }
359
+ }
360
+ if (!isForOf)
361
+ return;
362
+ // The iterable is the `right` field — may be identifier, member_expression, or call_expression.
363
+ const rightNode = node.childForFieldName('right');
364
+ let iterableName;
365
+ let methodName;
366
+ let callExprElementType;
367
+ if (rightNode?.type === 'identifier') {
368
+ iterableName = rightNode.text;
369
+ }
370
+ else if (rightNode?.type === 'member_expression') {
371
+ const prop = rightNode.childForFieldName('property');
372
+ if (prop)
373
+ iterableName = prop.text;
374
+ }
375
+ else if (rightNode?.type === 'call_expression') {
376
+ // entries.values() → call_expression > function: member_expression > object + property
377
+ // this.repos.values() → nested member_expression: extract property from inner member
378
+ // getUsers() → call_expression > function: identifier (Phase 7.3 — return-type path)
379
+ const fn = rightNode.childForFieldName('function');
380
+ if (fn?.type === 'member_expression') {
381
+ const obj = fn.childForFieldName('object');
382
+ const prop = fn.childForFieldName('property');
383
+ if (obj?.type === 'identifier') {
384
+ iterableName = obj.text;
385
+ }
386
+ else if (obj?.type === 'member_expression') {
387
+ // this.repos.values() → obj = this.repos → extract 'repos'
388
+ const innerProp = obj.childForFieldName('property');
389
+ if (innerProp)
390
+ iterableName = innerProp.text;
391
+ }
392
+ if (prop?.type === 'property_identifier')
393
+ methodName = prop.text;
394
+ }
395
+ else if (fn?.type === 'identifier') {
396
+ // Direct function call: for (const user of getUsers())
397
+ const rawReturn = returnTypeLookup.lookupRawReturnType(fn.text);
398
+ if (rawReturn)
399
+ callExprElementType = extractElementTypeFromString(rawReturn);
400
+ }
401
+ }
402
+ if (!iterableName && !callExprElementType)
403
+ return;
404
+ let elementType;
405
+ if (callExprElementType) {
406
+ elementType = callExprElementType;
407
+ }
408
+ else {
409
+ // Look up the container's base type name for descriptor-aware resolution
410
+ const containerTypeName = scopeEnv.get(iterableName);
411
+ const typeArgPos = methodToTypeArgPosition(methodName, containerTypeName);
412
+ elementType = resolveIterableElementType(iterableName, node, scopeEnv, declarationTypeNodes, scope, extractTsElementTypeFromAnnotation, findTsIterableElementType, typeArgPos);
413
+ }
414
+ if (!elementType)
415
+ return;
416
+ // The loop variable is the `left` field.
417
+ const leftNode = node.childForFieldName('left');
418
+ if (!leftNode)
419
+ return;
420
+ // Handle destructured for-of: for (const [k, v] of entries)
421
+ // AST: left = array_pattern directly (no variable_declarator wrapper)
422
+ // Bind the LAST identifier to the element type (value in [key, value] patterns)
423
+ if (leftNode.type === 'array_pattern') {
424
+ const lastChild = leftNode.lastNamedChild;
425
+ if (lastChild?.type === 'identifier') {
426
+ scopeEnv.set(lastChild.text, elementType);
427
+ }
428
+ return;
429
+ }
430
+ if (leftNode.type === 'object_pattern') {
431
+ // Object destructuring (e.g., `for (const { id } of users)`) destructures
432
+ // into fields of the element type. Without field-level resolution, we cannot
433
+ // bind individual properties to their correct types. Skip to avoid false bindings.
434
+ return;
435
+ }
436
+ let loopVarNode = leftNode;
437
+ // `const user` parses as: left → variable_declarator containing an identifier named `user`
438
+ if (loopVarNode.type === 'variable_declarator') {
439
+ loopVarNode = loopVarNode.childForFieldName('name') ?? loopVarNode.firstNamedChild;
440
+ }
441
+ if (!loopVarNode)
442
+ return;
443
+ const loopVarName = extractVarName(loopVarNode);
444
+ if (loopVarName)
445
+ scopeEnv.set(loopVarName, elementType);
446
+ };
447
+ /** TS/JS: const alias = u → variable_declarator with name/value fields */
448
+ const extractPendingAssignment = (node, scopeEnv) => {
449
+ for (let i = 0; i < node.namedChildCount; i++) {
450
+ const child = node.namedChild(i);
451
+ if (!child || child.type !== 'variable_declarator')
452
+ continue;
453
+ const nameNode = child.childForFieldName('name');
454
+ const valueNode = child.childForFieldName('value');
455
+ if (!nameNode || !valueNode)
456
+ continue;
457
+ const lhs = nameNode.text;
458
+ if (scopeEnv.has(lhs))
459
+ continue;
460
+ if (valueNode.type === 'identifier')
461
+ return { kind: 'copy', lhs, rhs: valueNode.text };
462
+ }
463
+ return undefined;
464
+ };
465
+ /** TS instanceof narrowing: `x instanceof User` → bind x to User.
466
+ * Only works when x has no prior type binding (e.g. x: unknown, untyped params).
467
+ * Typed params (x: Animal) are blocked by the !scopeEnv.has() guard in buildTypeEnv.
468
+ * Uses first-writer-wins, same as Rust match arm bindings. */
469
+ const extractPatternBinding = (node) => {
470
+ if (node.type !== 'binary_expression')
471
+ return undefined;
472
+ const op = node.children.find(c => !c.isNamed && c.text === 'instanceof');
473
+ if (!op)
474
+ return undefined;
475
+ // binary_expression children are positional — no left/right fields
476
+ const left = node.namedChild(0);
477
+ const right = node.namedChild(1);
478
+ if (left?.type !== 'identifier' || right?.type !== 'identifier')
479
+ return undefined;
480
+ return { varName: left.text, typeName: right.text };
481
+ };
482
+ export const typeConfig = {
483
+ declarationNodeTypes: DECLARATION_NODE_TYPES,
484
+ forLoopNodeTypes: FOR_LOOP_NODE_TYPES,
485
+ patternBindingNodeTypes: new Set(['binary_expression']),
486
+ extractDeclaration,
487
+ extractParameter,
488
+ extractInitializer,
489
+ scanConstructorBinding,
490
+ extractReturnType,
491
+ extractForLoopBinding,
492
+ extractPendingAssignment,
493
+ extractPatternBinding,
494
+ };
@@ -0,0 +1,138 @@
1
+ import type Parser from 'tree-sitter';
2
+ import { SupportedLanguages } from '../../config/supported-languages.js';
3
+ /** Tree-sitter AST node. Re-exported for use across ingestion modules. */
4
+ export type SyntaxNode = Parser.SyntaxNode;
5
+ /**
6
+ * Ordered list of definition capture keys for tree-sitter query matches.
7
+ * Used to extract the definition node from a capture map.
8
+ */
9
+ export declare const DEFINITION_CAPTURE_KEYS: readonly ["definition.function", "definition.class", "definition.interface", "definition.method", "definition.struct", "definition.enum", "definition.namespace", "definition.module", "definition.trait", "definition.impl", "definition.type", "definition.const", "definition.static", "definition.typedef", "definition.macro", "definition.union", "definition.property", "definition.record", "definition.delegate", "definition.annotation", "definition.constructor", "definition.template"];
10
+ /** Extract the definition node from a tree-sitter query capture map. */
11
+ export declare const getDefinitionNodeFromCaptures: (captureMap: Record<string, any>) => SyntaxNode | null;
12
+ /**
13
+ * Node types that represent function/method definitions across languages.
14
+ * Used to find the enclosing function for a call site.
15
+ */
16
+ export declare const FUNCTION_NODE_TYPES: Set<string>;
17
+ /**
18
+ * Node types for standard function declarations that need C/C++ declarator handling.
19
+ * Used by extractFunctionName to determine how to extract the function name.
20
+ */
21
+ export declare const FUNCTION_DECLARATION_TYPES: Set<string>;
22
+ /**
23
+ * Built-in function/method names that should not be tracked as call targets.
24
+ * Covers JS/TS, Python, Kotlin, C/C++, PHP, Swift standard library functions.
25
+ */
26
+ export declare const BUILT_IN_NAMES: Set<string>;
27
+ /** Check if a name is a built-in function or common noise that should be filtered out */
28
+ export declare const isBuiltInOrNoise: (name: string) => boolean;
29
+ /** AST node types that represent a class-like container (for HAS_METHOD edge extraction) */
30
+ export declare const CLASS_CONTAINER_TYPES: Set<string>;
31
+ export declare const CONTAINER_TYPE_TO_LABEL: Record<string, string>;
32
+ /** Walk up AST to find enclosing class/struct/interface/impl, return its generateId or null.
33
+ * For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct). */
34
+ export declare const findEnclosingClassId: (node: any, filePath: string) => string | null;
35
+ /**
36
+ * Extract function name and label from a function_definition or similar AST node.
37
+ * Handles C/C++ qualified_identifier (ClassName::MethodName) and other language patterns.
38
+ */
39
+ export declare const extractFunctionName: (node: SyntaxNode) => {
40
+ funcName: string | null;
41
+ label: string;
42
+ };
43
+ /**
44
+ * Yield control to the event loop so spinners/progress can render.
45
+ * Call periodically in hot loops to prevent UI freezes.
46
+ */
47
+ export declare const yieldToEventLoop: () => Promise<void>;
48
+ /**
49
+ * Find a child of `childType` within a sibling node of `siblingType`.
50
+ * Used for Kotlin AST traversal where visibility_modifier lives inside a modifiers sibling.
51
+ */
52
+ export declare const findSiblingChild: (parent: any, siblingType: string, childType: string) => any | null;
53
+ /**
54
+ * Map file extension to SupportedLanguage enum
55
+ */
56
+ export declare const getLanguageFromFilename: (filename: string) => SupportedLanguages | null;
57
+ export interface MethodSignature {
58
+ parameterCount: number | undefined;
59
+ returnType: string | undefined;
60
+ }
61
+ /**
62
+ * Extract parameter count and return type text from an AST method/function node.
63
+ * Works across languages by looking for common AST patterns.
64
+ */
65
+ export declare const extractMethodSignature: (node: SyntaxNode | null | undefined) => MethodSignature;
66
+ /**
67
+ * Count direct arguments for a call expression across common tree-sitter grammars.
68
+ * Returns undefined when the argument container cannot be located cheaply.
69
+ */
70
+ export declare const countCallArguments: (callNode: SyntaxNode | null | undefined) => number | undefined;
71
+ type CallForm = 'free' | 'member' | 'constructor';
72
+ /**
73
+ * Infer whether a captured call site is a free call, member call, or constructor.
74
+ * Returns undefined if the form cannot be determined.
75
+ *
76
+ * Works by inspecting the AST structure between callNode (@call) and nameNode (@call.name).
77
+ * No tree-sitter query changes needed — the distinction is in the node types.
78
+ */
79
+ export declare const inferCallForm: (callNode: SyntaxNode, nameNode: SyntaxNode) => CallForm | undefined;
80
+ export declare const extractReceiverName: (nameNode: SyntaxNode) => string | undefined;
81
+ /**
82
+ * Extract the raw receiver AST node for a member call.
83
+ * Unlike extractReceiverName, this returns the receiver node regardless of its type —
84
+ * including call_expression / method_invocation nodes that appear in chained calls
85
+ * like `svc.getUser().save()`.
86
+ *
87
+ * Returns undefined when the call is not a member call or when no receiver node
88
+ * can be found (e.g. top-level free calls).
89
+ */
90
+ export declare const extractReceiverNode: (nameNode: SyntaxNode) => SyntaxNode | undefined;
91
+ export declare const isVerboseIngestionEnabled: () => boolean;
92
+ /** Node types representing call expressions across supported languages. */
93
+ export declare const CALL_EXPRESSION_TYPES: Set<string>;
94
+ /**
95
+ * Hard limit on chain depth to prevent runaway recursion.
96
+ * For `a.b().c().d()`, the chain has depth 2 (b and c before d).
97
+ */
98
+ export declare const MAX_CHAIN_DEPTH = 3;
99
+ /**
100
+ * Walk a receiver AST node that is itself a call expression, accumulating the
101
+ * chain of intermediate method names up to MAX_CHAIN_DEPTH.
102
+ *
103
+ * For `svc.getUser().save()`, called with the receiver of `save` (getUser() call):
104
+ * returns { chain: ['getUser'], baseReceiverName: 'svc' }
105
+ *
106
+ * For `a.b().c().d()`, called with the receiver of `d` (c() call):
107
+ * returns { chain: ['b', 'c'], baseReceiverName: 'a' }
108
+ */
109
+ export declare function extractCallChain(receiverCallNode: SyntaxNode): {
110
+ chain: string[];
111
+ baseReceiverName: string | undefined;
112
+ } | undefined;
113
+ /** One step in a mixed receiver chain. */
114
+ export type MixedChainStep = {
115
+ kind: 'field' | 'call';
116
+ name: string;
117
+ };
118
+ /**
119
+ * Walk a receiver AST node that may interleave field accesses and method calls,
120
+ * building a unified chain of steps up to MAX_CHAIN_DEPTH.
121
+ *
122
+ * For `svc.getUser().address.save()`, called with the receiver of `save`
123
+ * (`svc.getUser().address`, a field access node):
124
+ * returns { chain: [{ kind:'call', name:'getUser' }, { kind:'field', name:'address' }],
125
+ * baseReceiverName: 'svc' }
126
+ *
127
+ * For `user.getAddress().city.getName()`, called with receiver of `getName`
128
+ * (`user.getAddress().city`):
129
+ * returns { chain: [{ kind:'call', name:'getAddress' }, { kind:'field', name:'city' }],
130
+ * baseReceiverName: 'user' }
131
+ *
132
+ * Pure field chains and pure call chains are special cases (all steps same kind).
133
+ */
134
+ export declare function extractMixedChain(receiverNode: SyntaxNode): {
135
+ chain: MixedChainStep[];
136
+ baseReceiverName: string | undefined;
137
+ } | undefined;
138
+ export {};