gitnexus 1.6.6-rc.74 → 1.6.6-rc.75

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.
@@ -4,12 +4,17 @@ export type { HttpDetection, HttpLanguagePlugin, HttpRole } from './types.js';
4
4
  * Glob for files worth scanning for HTTP routes. Kept alongside the
5
5
  * registry so adding a new language widens the glob in one edit.
6
6
  *
7
+ * `.kt`/`.kts` are always present in the glob even when the optional
8
+ * `tree-sitter-kotlin` grammar isn't installed — `getPluginForFile`
9
+ * will return `undefined` for those files in that case, so the
10
+ * orchestrator simply skips them at scan time without erroring.
11
+ *
7
12
  * `.vue` / `.svelte` files are intentionally omitted for the source-scan
8
13
  * path — they need their own grammar-aware extraction and the existing
9
14
  * regex fallback for them was never very accurate. The graph-assisted
10
15
  * Strategy A still handles them via the ingestion pipeline.
11
16
  */
12
- export declare const HTTP_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,go,py,php}";
17
+ export declare const HTTP_SCAN_GLOB = "**/*.{ts,tsx,js,jsx,java,kt,kts,go,py,php}";
13
18
  /**
14
19
  * Return the HTTP plugin registered for the given file's extension,
15
20
  * or `undefined` if the extension is not registered.
@@ -1,6 +1,7 @@
1
1
  import * as path from 'node:path';
2
2
  import { isBladeTemplateFilename } from '../../../../_shared/index.js';
3
3
  import { JAVA_HTTP_PLUGIN } from './java.js';
4
+ import { KOTLIN_HTTP_PLUGIN } from './kotlin.js';
4
5
  import { GO_HTTP_PLUGIN } from './go.js';
5
6
  import { PYTHON_HTTP_PLUGIN } from './python.js';
6
7
  import { PHP_HTTP_PLUGIN } from './php.js';
@@ -14,6 +15,11 @@ import { JAVASCRIPT_HTTP_PLUGIN, TYPESCRIPT_HTTP_PLUGIN, TSX_HTTP_PLUGIN } from
14
15
  * new language, drop a `http-patterns/<lang>.ts` that exports a
15
16
  * `HttpLanguagePlugin`, import it here and register the extension(s).
16
17
  * No edits to `http-route-extractor.ts` are required.
18
+ *
19
+ * Optional grammar plugins (e.g. `kotlin.ts`, which depends on the
20
+ * optionalDependency `tree-sitter-kotlin`) export `null` when the
21
+ * native binding is unavailable; we skip registration in that case so
22
+ * a missing optional grammar never crashes the orchestrator.
17
23
  */
18
24
  const REGISTRY = {
19
25
  '.java': JAVA_HTTP_PLUGIN,
@@ -25,16 +31,25 @@ const REGISTRY = {
25
31
  '.ts': TYPESCRIPT_HTTP_PLUGIN,
26
32
  '.tsx': TSX_HTTP_PLUGIN,
27
33
  };
34
+ if (KOTLIN_HTTP_PLUGIN) {
35
+ REGISTRY['.kt'] = KOTLIN_HTTP_PLUGIN;
36
+ REGISTRY['.kts'] = KOTLIN_HTTP_PLUGIN;
37
+ }
28
38
  /**
29
39
  * Glob for files worth scanning for HTTP routes. Kept alongside the
30
40
  * registry so adding a new language widens the glob in one edit.
31
41
  *
42
+ * `.kt`/`.kts` are always present in the glob even when the optional
43
+ * `tree-sitter-kotlin` grammar isn't installed — `getPluginForFile`
44
+ * will return `undefined` for those files in that case, so the
45
+ * orchestrator simply skips them at scan time without erroring.
46
+ *
32
47
  * `.vue` / `.svelte` files are intentionally omitted for the source-scan
33
48
  * path — they need their own grammar-aware extraction and the existing
34
49
  * regex fallback for them was never very accurate. The graph-assisted
35
50
  * Strategy A still handles them via the ingestion pipeline.
36
51
  */
37
- export const HTTP_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,go,py,php}';
52
+ export const HTTP_SCAN_GLOB = '**/*.{ts,tsx,js,jsx,java,kt,kts,go,py,php}';
38
53
  /**
39
54
  * Return the HTTP plugin registered for the given file's extension,
40
55
  * or `undefined` if the extension is not registered.
@@ -20,6 +20,19 @@ const METHOD_ANNOTATION_TO_HTTP = {
20
20
  PatchMapping: 'PATCH',
21
21
  };
22
22
  // ─── Provider: Spring class-level @RequestMapping prefix ──────────────
23
+ // Two patterns are needed because the AST shape differs depending on
24
+ // whether the annotation uses a positional argument or a named one:
25
+ // @RequestMapping("/api") → (annotation_argument_list (string_literal))
26
+ // @RequestMapping(path = "/api") → (annotation_argument_list (element_value_pair key:(identifier) value:(string_literal)))
27
+ // @RequestMapping(value = "/api") → same as above
28
+ //
29
+ // The named-argument pattern MUST constrain the `key` field to the route
30
+ // member names (`path`/`value`); without it, the query also captures
31
+ // non-route attributes such as `produces`, `consumes`, `headers`, `name`,
32
+ // `params` (their right-hand string literals would be mis-extracted as
33
+ // route prefixes — e.g. `produces = "application/json"` would corrupt
34
+ // every method route under that controller). The sibling
35
+ // `topic-patterns/java.ts` uses the same `key:` constraint approach.
23
36
  const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
24
37
  name: 'java-spring-class-prefix',
25
38
  language: Java,
@@ -34,9 +47,26 @@ const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
34
47
  arguments: (annotation_argument_list (string_literal) @prefix)))) @class
35
48
  `,
36
49
  },
50
+ {
51
+ meta: {},
52
+ query: `
53
+ (class_declaration
54
+ (modifiers
55
+ (annotation
56
+ name: (identifier) @ann (#eq? @ann "RequestMapping")
57
+ arguments: (annotation_argument_list
58
+ (element_value_pair
59
+ key: (identifier) @key (#match? @key "^(path|value)$")
60
+ value: (string_literal) @prefix))))) @class
61
+ `,
62
+ },
37
63
  ],
38
64
  });
39
65
  // ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
66
+ // Same dual-pattern approach: positional vs named argument. The named
67
+ // pattern restricts the annotation member name to `path`/`value` to
68
+ // avoid capturing unrelated string-valued attributes
69
+ // (`produces`, `consumes`, `headers`, `name`, `params`, ...).
40
70
  const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
41
71
  name: 'java-spring-method-route',
42
72
  language: Java,
@@ -52,6 +82,20 @@ const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
52
82
  name: (identifier) @method_name) @method
53
83
  `,
54
84
  },
85
+ {
86
+ meta: {},
87
+ query: `
88
+ (method_declaration
89
+ (modifiers
90
+ (annotation
91
+ name: (identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$")
92
+ arguments: (annotation_argument_list
93
+ (element_value_pair
94
+ key: (identifier) @key (#match? @key "^(path|value)$")
95
+ value: (string_literal) @path))))
96
+ name: (identifier) @method_name) @method
97
+ `,
98
+ },
55
99
  ],
56
100
  });
57
101
  // ─── Consumer: Spring RestTemplate (object-named + method-named) ──────
@@ -0,0 +1,8 @@
1
+ import type { HttpLanguagePlugin } from './types.js';
2
+ /**
3
+ * The exported plugin is `null` when tree-sitter-kotlin's native
4
+ * binding is unavailable. `http-patterns/index.ts` checks for null
5
+ * before registering `.kt`/`.kts` so missing optional grammars never
6
+ * crash the orchestrator.
7
+ */
8
+ export declare const KOTLIN_HTTP_PLUGIN: HttpLanguagePlugin | null;
@@ -0,0 +1,227 @@
1
+ import { createRequire } from 'node:module';
2
+ import { compilePatterns, runCompiledPatterns, unquoteLiteral, } from '../tree-sitter-scanner.js';
3
+ /**
4
+ * Kotlin HTTP plugin (Spring providers).
5
+ *
6
+ * Mirrors the Java plugin for Spring `@RequestMapping` class prefixes
7
+ * and `@(Get|Post|...)Mapping` method annotations on Kotlin Spring
8
+ * Boot controllers. Both positional shorthand (`@GetMapping("/x")`)
9
+ * and named annotation arguments (`@GetMapping(value = "/x")` and
10
+ * `@GetMapping(path = "/x")`) are supported.
11
+ *
12
+ * Consumer detection (RestTemplate / WebClient / OkHttp) is intentionally
13
+ * out of scope for this plugin — Kotlin call-site ASTs are sufficiently
14
+ * different from Java's `method_invocation` shape that they warrant a
15
+ * separate, focused follow-up.
16
+ *
17
+ * tree-sitter-kotlin (fwcd) AST shapes used here:
18
+ * class_declaration
19
+ * modifiers
20
+ * annotation
21
+ * constructor_invocation
22
+ * user_type → type_identifier ← annotation name
23
+ * value_arguments
24
+ * value_argument
25
+ * (simple_identifier "=")? ← absent for positional, present for named
26
+ * string_literal
27
+ * type_identifier ← class name
28
+ *
29
+ * tree-sitter-kotlin is an optional npm dependency — when its native
30
+ * binding is unavailable the plugin gracefully exports `null` and
31
+ * `http-patterns/index.ts` skips registration for `.kt`/`.kts` files.
32
+ */
33
+ const _require = createRequire(import.meta.url);
34
+ /** Loaded lazily; null when the grammar binding isn't installed. */
35
+ let Kotlin = null;
36
+ try {
37
+ Kotlin = _require('tree-sitter-kotlin');
38
+ }
39
+ catch {
40
+ Kotlin = null;
41
+ }
42
+ const METHOD_ANNOTATION_TO_HTTP = {
43
+ GetMapping: 'GET',
44
+ PostMapping: 'POST',
45
+ PutMapping: 'PUT',
46
+ DeleteMapping: 'DELETE',
47
+ PatchMapping: 'PATCH',
48
+ };
49
+ /**
50
+ * Build the plugin only if the Kotlin grammar is available. Compiling
51
+ * the queries against a null grammar would throw at module load time
52
+ * and abort the whole http-route-extractor module.
53
+ */
54
+ function buildKotlinPlugin(language) {
55
+ // ─── Provider: Spring class-level @RequestMapping prefix ──────────────
56
+ // Two patterns mirror the Java plugin's positional vs named split:
57
+ // @RequestMapping("/api") → value_argument has string_literal as its first named child
58
+ // @RequestMapping(path = "/api") → value_argument has [simple_identifier @key, string_literal]
59
+ // @RequestMapping(value = "/api") → same as above, with key="value"
60
+ //
61
+ // Tree-sitter-kotlin grammar (fwcd 0.3.8) does NOT have a separate
62
+ // node for named arguments — both positional and named forms share
63
+ // `value_argument`. The positional pattern uses the immediate-child
64
+ // anchor `.` so it only matches when the string_literal is the FIRST
65
+ // named child (i.e. no preceding simple_identifier "=" prefix). The
66
+ // named pattern explicitly captures the simple_identifier and uses
67
+ // `#match?` to restrict it to `path`/`value`, matching the same
68
+ // safety bar that the Java plugin enforces (see java.ts and the
69
+ // sibling topic-patterns/java.ts for the analogous constraint).
70
+ //
71
+ // Without the `key:` constraint the named query would also capture
72
+ // unrelated attributes like `produces`, `consumes`, `headers`,
73
+ // `name`, `params` — emitting bogus route contracts (a regression
74
+ // identical to the one Claude flagged on PR #1834 for Java).
75
+ const SPRING_CLASS_PREFIX_PATTERNS = compilePatterns({
76
+ name: 'kotlin-spring-class-prefix',
77
+ language,
78
+ patterns: [
79
+ {
80
+ meta: {},
81
+ query: `
82
+ (class_declaration
83
+ (modifiers
84
+ (annotation
85
+ (constructor_invocation
86
+ (user_type (type_identifier) @ann (#eq? @ann "RequestMapping"))
87
+ (value_arguments
88
+ (value_argument . (string_literal) @prefix)))))
89
+ (type_identifier) @cls) @class
90
+ `,
91
+ },
92
+ {
93
+ meta: {},
94
+ query: `
95
+ (class_declaration
96
+ (modifiers
97
+ (annotation
98
+ (constructor_invocation
99
+ (user_type (type_identifier) @ann (#eq? @ann "RequestMapping"))
100
+ (value_arguments
101
+ (value_argument
102
+ (simple_identifier) @key (#match? @key "^(path|value)$")
103
+ (string_literal) @prefix)))))
104
+ (type_identifier) @cls) @class
105
+ `,
106
+ },
107
+ ],
108
+ });
109
+ // ─── Provider: Spring @(Get|Post|...)Mapping method annotations ───────
110
+ // Same dual-pattern positional/named approach. The Kotlin AST puts the
111
+ // function name (`simple_identifier`) outside the `modifiers` subtree,
112
+ // so we capture it from `function_declaration` directly.
113
+ const SPRING_METHOD_ROUTE_PATTERNS = compilePatterns({
114
+ name: 'kotlin-spring-method-route',
115
+ language,
116
+ patterns: [
117
+ {
118
+ meta: {},
119
+ query: `
120
+ (function_declaration
121
+ (modifiers
122
+ (annotation
123
+ (constructor_invocation
124
+ (user_type (type_identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$"))
125
+ (value_arguments
126
+ (value_argument . (string_literal) @path)))))
127
+ (simple_identifier) @method_name) @method
128
+ `,
129
+ },
130
+ {
131
+ meta: {},
132
+ query: `
133
+ (function_declaration
134
+ (modifiers
135
+ (annotation
136
+ (constructor_invocation
137
+ (user_type (type_identifier) @ann (#match? @ann "^(Get|Post|Put|Delete|Patch)Mapping$"))
138
+ (value_arguments
139
+ (value_argument
140
+ (simple_identifier) @key (#match? @key "^(path|value)$")
141
+ (string_literal) @path)))))
142
+ (simple_identifier) @method_name) @method
143
+ `,
144
+ },
145
+ ],
146
+ });
147
+ /**
148
+ * Find the nearest enclosing class_declaration ancestor for a node, or
149
+ * null if the node is top-level. Mirrors the Java plugin's helper.
150
+ */
151
+ function findEnclosingClass(node) {
152
+ let cur = node.parent;
153
+ while (cur) {
154
+ if (cur.type === 'class_declaration')
155
+ return cur;
156
+ cur = cur.parent;
157
+ }
158
+ return null;
159
+ }
160
+ /**
161
+ * Join a class-level prefix and a method-level path. Identical
162
+ * semantics to the Java plugin: strip leading/trailing slashes on
163
+ * the prefix, strip leading slashes on the method path, ensure a
164
+ * single slash between them.
165
+ */
166
+ function joinPath(prefix, methodPath) {
167
+ const cleanPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
168
+ const cleanSub = methodPath.replace(/^\/+/, '');
169
+ if (!cleanPrefix)
170
+ return `/${cleanSub}`;
171
+ return `/${cleanPrefix}/${cleanSub}`;
172
+ }
173
+ return {
174
+ name: 'kotlin-http',
175
+ language,
176
+ scan(tree) {
177
+ const out = [];
178
+ // ─── Class prefixes ─────────────────────────────────────────────
179
+ const prefixByClassId = new Map();
180
+ for (const match of runCompiledPatterns(SPRING_CLASS_PREFIX_PATTERNS, tree)) {
181
+ const prefixNode = match.captures.prefix;
182
+ const classNode = match.captures.class;
183
+ if (!prefixNode || !classNode)
184
+ continue;
185
+ const prefix = unquoteLiteral(prefixNode.text);
186
+ if (prefix !== null)
187
+ prefixByClassId.set(classNode.id, prefix);
188
+ }
189
+ // ─── Method routes ──────────────────────────────────────────────
190
+ for (const match of runCompiledPatterns(SPRING_METHOD_ROUTE_PATTERNS, tree)) {
191
+ const annNode = match.captures.ann;
192
+ const pathNode = match.captures.path;
193
+ const nameNode = match.captures.method_name;
194
+ const methodNode = match.captures.method;
195
+ if (!annNode || !pathNode || !methodNode)
196
+ continue;
197
+ const httpMethod = METHOD_ANNOTATION_TO_HTTP[annNode.text];
198
+ if (!httpMethod)
199
+ continue;
200
+ const rawPath = unquoteLiteral(pathNode.text);
201
+ if (rawPath === null)
202
+ continue;
203
+ const enclosingClass = findEnclosingClass(methodNode);
204
+ const prefix = enclosingClass ? (prefixByClassId.get(enclosingClass.id) ?? '') : '';
205
+ const fullPath = joinPath(prefix, rawPath);
206
+ out.push({
207
+ role: 'provider',
208
+ framework: 'spring',
209
+ method: httpMethod,
210
+ path: fullPath,
211
+ name: nameNode?.text ?? null,
212
+ confidence: 0.8,
213
+ });
214
+ }
215
+ return out;
216
+ },
217
+ };
218
+ }
219
+ /**
220
+ * The exported plugin is `null` when tree-sitter-kotlin's native
221
+ * binding is unavailable. `http-patterns/index.ts` checks for null
222
+ * before registering `.kt`/`.kts` so missing optional grammars never
223
+ * crash the orchestrator.
224
+ */
225
+ export const KOTLIN_HTTP_PLUGIN = Kotlin
226
+ ? buildKotlinPlugin(Kotlin)
227
+ : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.74",
3
+ "version": "1.6.6-rc.75",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",