gitnexus 1.6.9-rc.23 → 1.6.9-rc.25
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.
|
@@ -25,6 +25,9 @@ export const TYPESCRIPT_QUERIES = `
|
|
|
25
25
|
(function_declaration
|
|
26
26
|
name: (identifier) @name) @definition.function
|
|
27
27
|
|
|
28
|
+
(generator_function_declaration
|
|
29
|
+
name: (identifier) @name) @definition.function
|
|
30
|
+
|
|
28
31
|
; TypeScript overload signatures (function_signature is a separate node type from function_declaration)
|
|
29
32
|
(function_signature
|
|
30
33
|
name: (identifier) @name) @definition.function
|
|
@@ -371,6 +374,9 @@ export const JAVASCRIPT_QUERIES = `
|
|
|
371
374
|
(function_declaration
|
|
372
375
|
name: (identifier) @name) @definition.function
|
|
373
376
|
|
|
377
|
+
(generator_function_declaration
|
|
378
|
+
name: (identifier) @name) @definition.function
|
|
379
|
+
|
|
374
380
|
(method_definition
|
|
375
381
|
name: (property_identifier) @name) @definition.method
|
|
376
382
|
|
|
@@ -5157,7 +5157,7 @@ export class LocalBackend {
|
|
|
5157
5157
|
RETURN n.id AS routeId, n.name AS routeName, n.filePath AS handlerFile,
|
|
5158
5158
|
n.responseKeys AS responseKeys, n.errorKeys AS errorKeys, n.middleware AS middleware,
|
|
5159
5159
|
consumer.name AS consumerName, consumer.filePath AS consumerFile,
|
|
5160
|
-
r.reason AS fetchReason
|
|
5160
|
+
r.reason AS fetchReason, n.method AS method
|
|
5161
5161
|
`, params);
|
|
5162
5162
|
// Strip wrapping quotes from DB array elements — CSV COPY stores ['key'] which
|
|
5163
5163
|
// LadybugDB may return as "'key'" rather than "key"
|
|
@@ -5173,10 +5173,16 @@ export class LocalBackend {
|
|
|
5173
5173
|
const consumerName = row.consumerName ?? row[6];
|
|
5174
5174
|
const consumerFile = row.consumerFile ?? row[7];
|
|
5175
5175
|
const fetchReason = row.fetchReason ?? row[8] ?? null;
|
|
5176
|
+
// Verb is the literal '*' for method-agnostic routes (Django function
|
|
5177
|
+
// views) and absent (null) for method-less routes (filesystem, Laravel
|
|
5178
|
+
// resource). Appended last in RETURN so positional fallbacks for the
|
|
5179
|
+
// consumer/reason columns above stay stable.
|
|
5180
|
+
const method = row.method ?? row[9] ?? null;
|
|
5176
5181
|
if (!routeMap.has(id)) {
|
|
5177
5182
|
routeMap.set(id, {
|
|
5178
5183
|
id,
|
|
5179
5184
|
name,
|
|
5185
|
+
method,
|
|
5180
5186
|
filePath,
|
|
5181
5187
|
responseKeys,
|
|
5182
5188
|
errorKeys,
|
|
@@ -5260,6 +5266,7 @@ export class LocalBackend {
|
|
|
5260
5266
|
return {
|
|
5261
5267
|
routes: routes.map((r) => ({
|
|
5262
5268
|
route: r.name,
|
|
5269
|
+
method: r.method,
|
|
5263
5270
|
handler: r.filePath,
|
|
5264
5271
|
middleware: r.middleware || [],
|
|
5265
5272
|
consumers: r.consumers,
|
|
@@ -5314,6 +5321,7 @@ export class LocalBackend {
|
|
|
5314
5321
|
const hasMismatches = consumers.some((c) => 'mismatched' in c && c.mismatched.length > 0);
|
|
5315
5322
|
return {
|
|
5316
5323
|
route: r.name,
|
|
5324
|
+
method: r.method,
|
|
5317
5325
|
handler: r.filePath,
|
|
5318
5326
|
...(responseKeys.length > 0 ? { responseKeys } : {}),
|
|
5319
5327
|
...(errorKeys.length > 0 ? { errorKeys } : {}),
|
|
@@ -5381,15 +5389,32 @@ export class LocalBackend {
|
|
|
5381
5389
|
routeFilter = `AND n.filePath CONTAINS $file`;
|
|
5382
5390
|
queryParams.file = params.file;
|
|
5383
5391
|
}
|
|
5384
|
-
|
|
5392
|
+
// After #2302 the same URL/handler can expose one Route node per HTTP verb.
|
|
5393
|
+
// An optional `method` narrows to that one verb so the response collapses to
|
|
5394
|
+
// the singular shape. A method-agnostic route (method `'*'`, e.g. a Django
|
|
5395
|
+
// function view) matches any selector; verbless routes (null method) never do.
|
|
5396
|
+
// `method` arrives unvalidated from the MCP envelope (the JSON schema is
|
|
5397
|
+
// advisory), so reject a non-string verb with a structured error instead of
|
|
5398
|
+
// throwing on `.toUpperCase()`; empty/whitespace collapses to no selector.
|
|
5399
|
+
const rawMethod = params.method;
|
|
5400
|
+
if (rawMethod !== undefined && typeof rawMethod !== 'string') {
|
|
5401
|
+
return { error: '"method" must be a string (e.g. "GET", "POST").' };
|
|
5402
|
+
}
|
|
5403
|
+
const wantedMethod = typeof rawMethod === 'string' ? rawMethod.trim().toUpperCase() || undefined : undefined;
|
|
5404
|
+
const matched = await this.fetchRoutesWithConsumers(repo.lbugPath, routeFilter, queryParams);
|
|
5405
|
+
const routes = matched.filter((r) => !wantedMethod || r.method === '*' || r.method?.toUpperCase() === wantedMethod);
|
|
5385
5406
|
if (routes.length === 0) {
|
|
5386
5407
|
const target = params.route || params.file;
|
|
5387
|
-
|
|
5408
|
+
// Only append the verb when the URL/file matched routes but none used it;
|
|
5409
|
+
// a non-existent URL/file gets the plain "no routes found" message.
|
|
5410
|
+
const verb = wantedMethod && matched.length > 0 ? ` with method "${wantedMethod}"` : '';
|
|
5411
|
+
return { error: `No routes found matching "${target}"${verb}.` };
|
|
5388
5412
|
}
|
|
5389
5413
|
const flowMap = await this.fetchLinkedFlowsBatch(repo.lbugPath, routes.map((r) => r.id));
|
|
5390
|
-
// Count
|
|
5414
|
+
// Count verbs per handler from the FULL match (before the method filter) so a
|
|
5415
|
+
// method-scoped query still flags a multi-verb handler's partial middleware.
|
|
5391
5416
|
const routeCountByHandler = new Map();
|
|
5392
|
-
for (const r of
|
|
5417
|
+
for (const r of matched) {
|
|
5393
5418
|
if (r.filePath) {
|
|
5394
5419
|
routeCountByHandler.set(r.filePath, (routeCountByHandler.get(r.filePath) ?? 0) + 1);
|
|
5395
5420
|
}
|
|
@@ -5459,6 +5484,7 @@ export class LocalBackend {
|
|
|
5459
5484
|
const middlewarePartial = middlewareArr.length > 0 && handlerRouteCount > 1;
|
|
5460
5485
|
return {
|
|
5461
5486
|
route: r.name,
|
|
5487
|
+
method: r.method,
|
|
5462
5488
|
handler: r.filePath,
|
|
5463
5489
|
responseShape: {
|
|
5464
5490
|
success: responseKeys,
|
|
@@ -5468,7 +5494,7 @@ export class LocalBackend {
|
|
|
5468
5494
|
...(middlewarePartial
|
|
5469
5495
|
? {
|
|
5470
5496
|
middlewareDetection: 'partial',
|
|
5471
|
-
middlewareNote: 'Middleware captured from first
|
|
5497
|
+
middlewareNote: 'Middleware captured from the first route export only — other route exports in this handler may use different middleware chains.',
|
|
5472
5498
|
}
|
|
5473
5499
|
: {}),
|
|
5474
5500
|
consumers,
|
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export interface ToolDefinition {
|
|
|
24
24
|
minLength?: number;
|
|
25
25
|
}>;
|
|
26
26
|
required: string[];
|
|
27
|
+
/**
|
|
28
|
+
* JSON-Schema `anyOf` for cross-property constraints `required` cannot express
|
|
29
|
+
* — e.g. "at least one of route/file". Forwarded verbatim to clients by the
|
|
30
|
+
* server's ListTools handler, so MCP clients see the constraint.
|
|
31
|
+
*/
|
|
32
|
+
anyOf?: Array<{
|
|
33
|
+
required: string[];
|
|
34
|
+
}>;
|
|
27
35
|
};
|
|
28
36
|
}
|
|
29
37
|
/**
|
package/dist/mcp/tools.js
CHANGED
|
@@ -615,7 +615,7 @@ CONTRACT CAVEATS:
|
|
|
615
615
|
WHEN TO USE: Understanding API consumption patterns, finding orphaned routes. For pre-change analysis, prefer \`api_impact\` which combines this data with mismatch detection and risk assessment.
|
|
616
616
|
AFTER THIS: Use impact() on specific route handlers to see full blast radius.
|
|
617
617
|
|
|
618
|
-
Returns: route nodes with their handlers, middleware wrapper chains (e.g., withAuth, withRateLimit), and consumers.`,
|
|
618
|
+
Returns: route nodes with their handlers, middleware wrapper chains (e.g., withAuth, withRateLimit), and consumers. Each route object includes its "method" (the HTTP verb, "*" for method-agnostic routes, or null for method-less routes).`,
|
|
619
619
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
620
620
|
inputSchema: {
|
|
621
621
|
type: 'object',
|
|
@@ -656,7 +656,7 @@ Returns: tool nodes with their handler files and descriptions.`,
|
|
|
656
656
|
WHEN TO USE: Detecting mismatches between what an API route returns and what consumers expect. Finding shape drift. For pre-change analysis, prefer \`api_impact\` which combines this data with mismatch detection and risk assessment.
|
|
657
657
|
REQUIRES: Route nodes with responseKeys (extracted from .json({...}) calls during indexing).
|
|
658
658
|
|
|
659
|
-
Returns routes that have both detected response keys AND consumers. Shows top-level keys each endpoint returns (e.g., data, pagination, error) and what keys each consumer accesses. Reports MISMATCH status when a consumer accesses keys not present in the route's response shape.`,
|
|
659
|
+
Returns routes that have both detected response keys AND consumers. Shows top-level keys each endpoint returns (e.g., data, pagination, error) and what keys each consumer accesses. Reports MISMATCH status when a consumer accesses keys not present in the route's response shape. Each route object includes its "method" (the HTTP verb, "*" for method-agnostic routes, or null for method-less routes).`,
|
|
660
660
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
661
661
|
inputSchema: {
|
|
662
662
|
type: 'object',
|
|
@@ -681,16 +681,23 @@ WHEN TO USE: BEFORE modifying any API route handler. Shows what consumers depend
|
|
|
681
681
|
|
|
682
682
|
Risk levels: LOW (0-3 consumers), MEDIUM (4-9 or any mismatches), HIGH (10+ consumers or mismatches with 4+ consumers). Mismatches with confidence "low" indicate the consumer file fetches multiple routes — property attribution is approximate.
|
|
683
683
|
|
|
684
|
-
|
|
684
|
+
Response shape is keyed on how many routes match, not on the data: exactly one match returns a single route object; two or more return { routes: [...], total: N }. The same URL can expose multiple HTTP verbs (e.g. GET and POST /api/orders are distinct routes that share the URL), so a bare-URL lookup may return the wrapped form — every route object carries its own "method" so verbs are distinguishable. Pass "method" to narrow to one verb; the single-object shape is returned only when exactly one route remains after filtering — a substring route/file match spanning several URLs can still return the wrapped form. A URL/file that exists but has no route for the given verb returns an error. Each route's "method" is the literal "*" for method-agnostic routes (e.g. Django function views), which match any "method" selector, or null for method-less routes (filesystem, Laravel resource), which never match a selector. Combines route_map, shape_check, and impact data.`,
|
|
685
685
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
686
686
|
inputSchema: {
|
|
687
687
|
type: 'object',
|
|
688
688
|
properties: {
|
|
689
689
|
route: { type: 'string', description: 'Route path (e.g., "/api/grants")' },
|
|
690
690
|
file: { type: 'string', description: 'Handler file path (alternative to route)' },
|
|
691
|
+
method: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
description: 'Optional HTTP verb — GET, POST, PUT, PATCH, DELETE, etc. — to narrow a multi-verb route or file lookup to a single method. Returns an error if no matched route uses that verb.',
|
|
694
|
+
},
|
|
691
695
|
repo: { type: 'string', description: 'Repository name or path.' },
|
|
692
696
|
},
|
|
693
697
|
required: [],
|
|
698
|
+
// Exactly one lookup key is needed, but either works (route wins if both
|
|
699
|
+
// are passed) — so the structural constraint is "at least one of route/file".
|
|
700
|
+
anyOf: [{ required: ['route'] }, { required: ['file'] }],
|
|
694
701
|
},
|
|
695
702
|
},
|
|
696
703
|
{
|
package/package.json
CHANGED