gitnexus 1.6.3-rc.21 → 1.6.3-rc.23
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.
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/group.js +73 -0
- package/dist/core/embeddings/chunker.js +30 -25
- package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
- package/dist/core/embeddings/embedding-pipeline.js +15 -6
- package/dist/core/embeddings/text-generator.d.ts +1 -1
- package/dist/core/embeddings/text-generator.js +33 -24
- package/dist/core/embeddings/types.d.ts +43 -1
- package/dist/core/embeddings/types.js +101 -29
- package/dist/core/group/cross-impact.d.ts +41 -0
- package/dist/core/group/cross-impact.js +454 -0
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service.d.ts +9 -0
- package/dist/core/group/service.js +219 -20
- package/dist/core/group/types.d.ts +30 -0
- package/dist/core/lbug/lbug-adapter.d.ts +12 -0
- package/dist/core/lbug/lbug-adapter.js +30 -0
- package/dist/core/run-analyze.js +7 -12
- package/dist/core/search/bm25-index.d.ts +6 -0
- package/dist/core/search/bm25-index.js +54 -2
- package/dist/mcp/local/local-backend.d.ts +18 -3
- package/dist/mcp/local/local-backend.js +141 -15
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +100 -17
- package/dist/mcp/tools.d.ts +4 -1
- package/dist/mcp/tools.js +75 -54
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@ export { isWriteQuery };
|
|
|
16
16
|
import { parseDiffHunks } from '../../storage/git.js';
|
|
17
17
|
import { listRegisteredRepos, cleanupOldKuzuFiles, } from '../../storage/repo-manager.js';
|
|
18
18
|
import { GroupService } from '../../core/group/service.js';
|
|
19
|
+
import { resolveAtGroupMemberRepoPath } from '../../core/group/resolve-at-member.js';
|
|
19
20
|
import { collectBestChunks } from '../../core/embeddings/types.js';
|
|
20
21
|
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME } from '../../core/lbug/schema.js';
|
|
21
22
|
import { PhaseTimer } from '../../core/search/phase-timer.js';
|
|
@@ -171,6 +172,7 @@ export class LocalBackend {
|
|
|
171
172
|
impact: (r, p) => this.impact(r, p),
|
|
172
173
|
query: (r, p) => this.query(r, p),
|
|
173
174
|
impactByUid: (id, uid, d, o) => this.impactByUid(id, uid, d, o),
|
|
175
|
+
context: (r, p) => this.context(r, p),
|
|
174
176
|
};
|
|
175
177
|
this.groupToolSvc = new GroupService(port);
|
|
176
178
|
}
|
|
@@ -423,6 +425,12 @@ export class LocalBackend {
|
|
|
423
425
|
if (method.startsWith('group_')) {
|
|
424
426
|
return this.handleGroupTool(method, params || {});
|
|
425
427
|
}
|
|
428
|
+
const p = params && typeof params === 'object' ? params : {};
|
|
429
|
+
if ((method === 'impact' || method === 'query' || method === 'context') &&
|
|
430
|
+
typeof p.repo === 'string' &&
|
|
431
|
+
p.repo.startsWith('@')) {
|
|
432
|
+
return this.callToolAtGroupRepo(method, p);
|
|
433
|
+
}
|
|
426
434
|
// Resolve repo from optional param (re-reads registry on miss)
|
|
427
435
|
const repo = await this.resolveRepo(params?.repo);
|
|
428
436
|
switch (method) {
|
|
@@ -2301,32 +2309,150 @@ export class LocalBackend {
|
|
|
2301
2309
|
return this.groupList(params);
|
|
2302
2310
|
case 'group_sync':
|
|
2303
2311
|
return this.groupSync(params);
|
|
2304
|
-
case 'group_contracts':
|
|
2305
|
-
return this.groupContracts(params);
|
|
2306
|
-
case 'group_query':
|
|
2307
|
-
return this.groupQuery(params);
|
|
2308
|
-
case 'group_status':
|
|
2309
|
-
return this.groupStatus(params);
|
|
2310
2312
|
default:
|
|
2311
|
-
throw new Error(`Unknown group tool: ${method}
|
|
2313
|
+
throw new Error(`Unknown group tool: ${method}. Removed tools: use repo "@<groupName>" on impact, query, or context (optional "/<memberPath>"), or MCP resources.`);
|
|
2312
2314
|
}
|
|
2313
2315
|
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Dispatch impact/query/context when `repo` is `@groupName` or `@groupName/memberPath`
|
|
2318
|
+
* (group mode — not the global indexed-repo `repo` parameter).
|
|
2319
|
+
*/
|
|
2320
|
+
async callToolAtGroupRepo(method, params) {
|
|
2321
|
+
await this.refreshRepos();
|
|
2322
|
+
if (params.service !== undefined &&
|
|
2323
|
+
params.service !== null &&
|
|
2324
|
+
String(params.service).trim() === '') {
|
|
2325
|
+
return { error: 'service must not be an empty string' };
|
|
2326
|
+
}
|
|
2327
|
+
const raw = String(params.repo).slice(1);
|
|
2328
|
+
const slash = raw.indexOf('/');
|
|
2329
|
+
const groupName = (slash === -1 ? raw : raw.slice(0, slash)).trim();
|
|
2330
|
+
const memberRest = slash === -1 ? undefined : raw.slice(slash + 1).trim() || undefined;
|
|
2331
|
+
const resolved = await resolveAtGroupMemberRepoPath(groupName, memberRest);
|
|
2332
|
+
if (resolved.ok === false)
|
|
2333
|
+
return { error: resolved.error };
|
|
2334
|
+
const svc = this.getGroupService();
|
|
2335
|
+
if (method === 'impact') {
|
|
2336
|
+
const impactArgs = {
|
|
2337
|
+
name: groupName,
|
|
2338
|
+
repo: resolved.repoPath,
|
|
2339
|
+
target: params.target,
|
|
2340
|
+
direction: params.direction,
|
|
2341
|
+
};
|
|
2342
|
+
if (params.maxDepth !== undefined)
|
|
2343
|
+
impactArgs.maxDepth = params.maxDepth;
|
|
2344
|
+
if (params.crossDepth !== undefined)
|
|
2345
|
+
impactArgs.crossDepth = params.crossDepth;
|
|
2346
|
+
if (params.relationTypes !== undefined)
|
|
2347
|
+
impactArgs.relationTypes = params.relationTypes;
|
|
2348
|
+
if (params.includeTests !== undefined)
|
|
2349
|
+
impactArgs.includeTests = params.includeTests;
|
|
2350
|
+
if (params.minConfidence !== undefined)
|
|
2351
|
+
impactArgs.minConfidence = params.minConfidence;
|
|
2352
|
+
if (params.service !== undefined && params.service !== null)
|
|
2353
|
+
impactArgs.service = params.service;
|
|
2354
|
+
if (typeof params.subgroup === 'string')
|
|
2355
|
+
impactArgs.subgroup = params.subgroup;
|
|
2356
|
+
if (params.timeoutMs !== undefined)
|
|
2357
|
+
impactArgs.timeoutMs = params.timeoutMs;
|
|
2358
|
+
if (params.timeout !== undefined)
|
|
2359
|
+
impactArgs.timeout = params.timeout;
|
|
2360
|
+
return svc.groupImpact(impactArgs);
|
|
2361
|
+
}
|
|
2362
|
+
if (method === 'query') {
|
|
2363
|
+
const queryArgs = {
|
|
2364
|
+
name: groupName,
|
|
2365
|
+
query: params.query,
|
|
2366
|
+
};
|
|
2367
|
+
if (typeof params.task_context === 'string')
|
|
2368
|
+
queryArgs.task_context = params.task_context;
|
|
2369
|
+
if (typeof params.goal === 'string')
|
|
2370
|
+
queryArgs.goal = params.goal;
|
|
2371
|
+
if (typeof params.limit === 'number')
|
|
2372
|
+
queryArgs.limit = params.limit;
|
|
2373
|
+
if (typeof params.max_symbols === 'number')
|
|
2374
|
+
queryArgs.max_symbols = params.max_symbols;
|
|
2375
|
+
if (params.include_content !== undefined)
|
|
2376
|
+
queryArgs.include_content = params.include_content;
|
|
2377
|
+
if (params.service !== undefined && params.service !== null)
|
|
2378
|
+
queryArgs.service = params.service;
|
|
2379
|
+
if (memberRest !== undefined) {
|
|
2380
|
+
queryArgs.subgroup = memberRest;
|
|
2381
|
+
queryArgs.subgroupExact = true;
|
|
2382
|
+
}
|
|
2383
|
+
return svc.groupQuery(queryArgs);
|
|
2384
|
+
}
|
|
2385
|
+
if (method === 'context') {
|
|
2386
|
+
const targetSym = typeof params.target === 'string' && params.target.trim() !== ''
|
|
2387
|
+
? params.target.trim()
|
|
2388
|
+
: typeof params.name === 'string' && params.name.trim() !== ''
|
|
2389
|
+
? params.name.trim()
|
|
2390
|
+
: undefined;
|
|
2391
|
+
const contextArgs = {
|
|
2392
|
+
name: groupName,
|
|
2393
|
+
target: targetSym,
|
|
2394
|
+
};
|
|
2395
|
+
if (typeof params.uid === 'string')
|
|
2396
|
+
contextArgs.uid = params.uid;
|
|
2397
|
+
if (typeof params.file_path === 'string')
|
|
2398
|
+
contextArgs.file_path = params.file_path;
|
|
2399
|
+
if (params.include_content !== undefined)
|
|
2400
|
+
contextArgs.include_content = params.include_content;
|
|
2401
|
+
if (params.service !== undefined && params.service !== null)
|
|
2402
|
+
contextArgs.service = params.service;
|
|
2403
|
+
if (memberRest !== undefined) {
|
|
2404
|
+
contextArgs.subgroup = memberRest;
|
|
2405
|
+
contextArgs.subgroupExact = true;
|
|
2406
|
+
}
|
|
2407
|
+
return svc.groupContext(contextArgs);
|
|
2408
|
+
}
|
|
2409
|
+
throw new Error(`Internal: unsupported group-repo tool ${method}`);
|
|
2410
|
+
}
|
|
2314
2411
|
async groupList(params) {
|
|
2315
2412
|
return this.getGroupService().groupList(params);
|
|
2316
2413
|
}
|
|
2317
2414
|
async groupSync(params) {
|
|
2318
2415
|
return this.getGroupService().groupSync(params);
|
|
2319
2416
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2417
|
+
/**
|
|
2418
|
+
* MCP resource body for `gitnexus://group/{name}/contracts` (Issue #794).
|
|
2419
|
+
*/
|
|
2420
|
+
async readGroupContractsResource(groupName, filter) {
|
|
2421
|
+
try {
|
|
2422
|
+
const params = { name: groupName };
|
|
2423
|
+
if (filter.type !== undefined)
|
|
2424
|
+
params.type = filter.type;
|
|
2425
|
+
if (filter.repo !== undefined)
|
|
2426
|
+
params.repo = filter.repo;
|
|
2427
|
+
if (filter.unmatchedOnly === true)
|
|
2428
|
+
params.unmatchedOnly = true;
|
|
2429
|
+
const raw = await this.getGroupService().groupContracts(params);
|
|
2430
|
+
return LocalBackend.formatGroupResourcePayload(raw);
|
|
2431
|
+
}
|
|
2432
|
+
catch (e) {
|
|
2433
|
+
return `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
2434
|
+
}
|
|
2322
2435
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2436
|
+
/**
|
|
2437
|
+
* MCP resource body for `gitnexus://group/{name}/status` (Issue #794).
|
|
2438
|
+
*/
|
|
2439
|
+
async readGroupStatusResource(groupName) {
|
|
2440
|
+
try {
|
|
2441
|
+
const raw = await this.getGroupService().groupStatus({ name: groupName });
|
|
2442
|
+
return LocalBackend.formatGroupResourcePayload(raw);
|
|
2443
|
+
}
|
|
2444
|
+
catch (e) {
|
|
2445
|
+
return `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
2446
|
+
}
|
|
2326
2447
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2448
|
+
static formatGroupResourcePayload(raw) {
|
|
2449
|
+
if (raw && typeof raw === 'object' && 'error' in raw) {
|
|
2450
|
+
const err = raw.error;
|
|
2451
|
+
if (typeof err === 'string' && err.length > 0) {
|
|
2452
|
+
return `error: ${err}`;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
return JSON.stringify(raw, null, 2);
|
|
2330
2456
|
}
|
|
2331
2457
|
/**
|
|
2332
2458
|
* Fetch Route nodes with their consumers in a single query.
|
package/dist/mcp/resources.d.ts
CHANGED
|
@@ -25,6 +25,37 @@ export declare function getResourceDefinitions(): ResourceDefinition[];
|
|
|
25
25
|
* Dynamic resource templates
|
|
26
26
|
*/
|
|
27
27
|
export declare function getResourceTemplates(): ResourceTemplate[];
|
|
28
|
+
/** Query parameters for `gitnexus://group/{name}/contracts` */
|
|
29
|
+
export type GroupContractsResourceFilter = {
|
|
30
|
+
type?: string;
|
|
31
|
+
repo?: string;
|
|
32
|
+
unmatchedOnly?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** Normalized parse result for GitNexus MCP resource URIs */
|
|
35
|
+
export type ParsedGitnexusResource = {
|
|
36
|
+
kind: 'repos';
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'setup';
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'repo';
|
|
41
|
+
repoName: string;
|
|
42
|
+
resourceType: string;
|
|
43
|
+
param?: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'group';
|
|
46
|
+
groupName: string;
|
|
47
|
+
resourceType: 'contracts';
|
|
48
|
+
contractsFilter: GroupContractsResourceFilter;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'group';
|
|
51
|
+
groupName: string;
|
|
52
|
+
resourceType: 'status';
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Parse a GitNexus resource URI (repos, setup, per-repo, or per-group templates).
|
|
56
|
+
* Used by `readResource` and tests (round-trip / dispatch coverage).
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseResourceUri(uri: string): ParsedGitnexusResource;
|
|
28
59
|
/**
|
|
29
60
|
* Read a resource and return its content
|
|
30
61
|
*/
|
package/dist/mcp/resources.js
CHANGED
|
@@ -65,36 +65,113 @@ export function getResourceTemplates() {
|
|
|
65
65
|
description: 'Step-by-step execution trace',
|
|
66
66
|
mimeType: 'text/yaml',
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
uriTemplate: 'gitnexus://group/{name}/contracts',
|
|
70
|
+
name: 'Group Contract Registry',
|
|
71
|
+
description: 'Cross-repo contract registry for a repository group. Optional query: type, repo, unmatchedOnly (true|false).',
|
|
72
|
+
mimeType: 'text/yaml',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
uriTemplate: 'gitnexus://group/{name}/status',
|
|
76
|
+
name: 'Group Index Status',
|
|
77
|
+
description: 'Per-repo index and contract-registry staleness for a repository group',
|
|
78
|
+
mimeType: 'text/yaml',
|
|
79
|
+
},
|
|
68
80
|
];
|
|
69
81
|
}
|
|
82
|
+
function parseUnmatchedOnlyParam(raw) {
|
|
83
|
+
if (raw === null)
|
|
84
|
+
return undefined;
|
|
85
|
+
const v = raw.trim().toLowerCase();
|
|
86
|
+
if (v === 'true' || v === '1')
|
|
87
|
+
return true;
|
|
88
|
+
if (v === 'false' || v === '0')
|
|
89
|
+
return false;
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
70
92
|
/**
|
|
71
|
-
* Parse a resource URI
|
|
93
|
+
* Parse a GitNexus resource URI (repos, setup, per-repo, or per-group templates).
|
|
94
|
+
* Used by `readResource` and tests (round-trip / dispatch coverage).
|
|
72
95
|
*/
|
|
73
|
-
function
|
|
96
|
+
export function parseResourceUri(uri) {
|
|
74
97
|
if (uri === 'gitnexus://repos')
|
|
75
|
-
return {
|
|
98
|
+
return { kind: 'repos' };
|
|
76
99
|
if (uri === 'gitnexus://setup')
|
|
77
|
-
return {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
return { kind: 'setup' };
|
|
101
|
+
let u;
|
|
102
|
+
try {
|
|
103
|
+
u = new URL(uri);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
107
|
+
}
|
|
108
|
+
if (u.protocol !== 'gitnexus:') {
|
|
109
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
110
|
+
}
|
|
111
|
+
if (u.hostname === 'group') {
|
|
112
|
+
const segments = u.pathname
|
|
113
|
+
.replace(/^\/+|\/+$/g, '')
|
|
114
|
+
.split('/')
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
if (segments.length < 2) {
|
|
117
|
+
throw new Error(`Invalid group resource URI (expected gitnexus://group/{name}/contracts or .../status): ${uri}`);
|
|
118
|
+
}
|
|
119
|
+
const tail = segments[segments.length - 1];
|
|
120
|
+
if (tail !== 'contracts' && tail !== 'status') {
|
|
121
|
+
throw new Error(`Unknown group resource path in URI: ${uri}`);
|
|
122
|
+
}
|
|
123
|
+
const groupName = segments
|
|
124
|
+
.slice(0, -1)
|
|
125
|
+
.map((s) => decodeURIComponent(s))
|
|
126
|
+
.join('/');
|
|
127
|
+
if (!groupName) {
|
|
128
|
+
throw new Error(`Invalid group resource URI (empty group name): ${uri}`);
|
|
129
|
+
}
|
|
130
|
+
if (tail === 'status') {
|
|
131
|
+
return { kind: 'group', groupName, resourceType: 'status' };
|
|
132
|
+
}
|
|
133
|
+
const contractsFilter = {};
|
|
134
|
+
const type = u.searchParams.get('type');
|
|
135
|
+
if (type && type.trim())
|
|
136
|
+
contractsFilter.type = type.trim();
|
|
137
|
+
const repo = u.searchParams.get('repo');
|
|
138
|
+
if (repo && repo.trim())
|
|
139
|
+
contractsFilter.repo = repo.trim();
|
|
140
|
+
if (u.searchParams.has('unmatchedOnly')) {
|
|
141
|
+
const coerced = parseUnmatchedOnlyParam(u.searchParams.get('unmatchedOnly'));
|
|
142
|
+
if (coerced !== undefined)
|
|
143
|
+
contractsFilter.unmatchedOnly = coerced;
|
|
144
|
+
}
|
|
145
|
+
return { kind: 'group', groupName, resourceType: 'contracts', contractsFilter };
|
|
146
|
+
}
|
|
147
|
+
if (u.hostname === 'repo') {
|
|
148
|
+
const segments = u.pathname
|
|
149
|
+
.replace(/^\/+|\/+$/g, '')
|
|
150
|
+
.split('/')
|
|
151
|
+
.filter(Boolean);
|
|
152
|
+
if (segments.length < 2) {
|
|
153
|
+
throw new Error(`Unknown resource URI: ${uri}`);
|
|
154
|
+
}
|
|
155
|
+
const repoName = decodeURIComponent(segments[0]);
|
|
156
|
+
const restEncoded = segments.slice(1);
|
|
157
|
+
const rest = restEncoded.map((s) => decodeURIComponent(s)).join('/');
|
|
83
158
|
if (rest.startsWith('cluster/')) {
|
|
84
159
|
return {
|
|
160
|
+
kind: 'repo',
|
|
85
161
|
repoName,
|
|
86
162
|
resourceType: 'cluster',
|
|
87
|
-
param:
|
|
163
|
+
param: rest.replace(/^cluster\//, ''),
|
|
88
164
|
};
|
|
89
165
|
}
|
|
90
166
|
if (rest.startsWith('process/')) {
|
|
91
167
|
return {
|
|
168
|
+
kind: 'repo',
|
|
92
169
|
repoName,
|
|
93
170
|
resourceType: 'process',
|
|
94
|
-
param:
|
|
171
|
+
param: rest.replace(/^process\//, ''),
|
|
95
172
|
};
|
|
96
173
|
}
|
|
97
|
-
return { repoName, resourceType: rest };
|
|
174
|
+
return { kind: 'repo', repoName, resourceType: rest };
|
|
98
175
|
}
|
|
99
176
|
throw new Error(`Unknown resource URI: ${uri}`);
|
|
100
177
|
}
|
|
@@ -102,15 +179,19 @@ function parseUri(uri) {
|
|
|
102
179
|
* Read a resource and return its content
|
|
103
180
|
*/
|
|
104
181
|
export async function readResource(uri, backend) {
|
|
105
|
-
const parsed =
|
|
106
|
-
|
|
107
|
-
if (parsed.resourceType === 'repos') {
|
|
182
|
+
const parsed = parseResourceUri(uri);
|
|
183
|
+
if (parsed.kind === 'repos') {
|
|
108
184
|
return getReposResource(backend);
|
|
109
185
|
}
|
|
110
|
-
|
|
111
|
-
if (parsed.resourceType === 'setup') {
|
|
186
|
+
if (parsed.kind === 'setup') {
|
|
112
187
|
return getSetupResource(backend);
|
|
113
188
|
}
|
|
189
|
+
if (parsed.kind === 'group') {
|
|
190
|
+
if (parsed.resourceType === 'contracts') {
|
|
191
|
+
return backend.readGroupContractsResource(parsed.groupName, parsed.contractsFilter);
|
|
192
|
+
}
|
|
193
|
+
return backend.readGroupStatusResource(parsed.groupName);
|
|
194
|
+
}
|
|
114
195
|
const repoName = parsed.repoName;
|
|
115
196
|
switch (parsed.resourceType) {
|
|
116
197
|
case 'context':
|
|
@@ -202,6 +283,8 @@ async function getContextResource(backend, repoName) {
|
|
|
202
283
|
lines.push(` - gitnexus://repo/${context.projectName}/processes: All execution flows`);
|
|
203
284
|
lines.push(` - gitnexus://repo/${context.projectName}/cluster/{name}: Module details`);
|
|
204
285
|
lines.push(` - gitnexus://repo/${context.projectName}/process/{name}: Process trace`);
|
|
286
|
+
lines.push(' - gitnexus://group/{name}/contracts: Group contract registry (optional ?type=&repo=&unmatchedOnly=)');
|
|
287
|
+
lines.push(' - gitnexus://group/{name}/status: Group index / contract staleness');
|
|
205
288
|
return lines.join('\n');
|
|
206
289
|
}
|
|
207
290
|
/**
|
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -12,11 +12,14 @@ export interface ToolDefinition {
|
|
|
12
12
|
properties: Record<string, {
|
|
13
13
|
type: string;
|
|
14
14
|
description?: string;
|
|
15
|
-
default?:
|
|
15
|
+
default?: unknown;
|
|
16
16
|
items?: {
|
|
17
17
|
type: string;
|
|
18
18
|
};
|
|
19
19
|
enum?: string[];
|
|
20
|
+
minimum?: number;
|
|
21
|
+
maximum?: number;
|
|
22
|
+
minLength?: number;
|
|
20
23
|
}>;
|
|
21
24
|
required: string[];
|
|
22
25
|
};
|
package/dist/mcp/tools.js
CHANGED
|
@@ -35,7 +35,11 @@ Returns results grouped by process (execution flow):
|
|
|
35
35
|
- process_symbols: all symbols in those flows with file locations and module (functional area)
|
|
36
36
|
- definitions: standalone types/interfaces not in any process
|
|
37
37
|
|
|
38
|
-
Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank Fusion
|
|
38
|
+
Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank Fusion.
|
|
39
|
+
|
|
40
|
+
GROUP MODE: set "repo" to "@<groupName>" to search all member repos in that group (merged via RRF), or "@<groupName>/<groupRepoPath>" to run against a single member (same path keys as in group.yaml). If you use "@<groupName>" only, the member repo defaults to the lexicographically first key in group.yaml "repos". Prefer resources for contracts/status (see migration from legacy group_* tools).
|
|
41
|
+
|
|
42
|
+
SERVICE: optional monorepo path prefix (POSIX-style, case-sensitive segments). When "repo" starts with "@", only processes whose symbols fall under that prefix are included. For a normal indexed repo name (no leading @), this field is currently ignored by the server.`,
|
|
39
43
|
inputSchema: {
|
|
40
44
|
type: 'object',
|
|
41
45
|
properties: {
|
|
@@ -48,11 +52,19 @@ Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank
|
|
|
48
52
|
type: 'string',
|
|
49
53
|
description: 'What you want to find (e.g., "existing auth validation logic"). Helps ranking.',
|
|
50
54
|
},
|
|
51
|
-
limit: {
|
|
55
|
+
limit: {
|
|
56
|
+
type: 'number',
|
|
57
|
+
description: 'Max processes to return (default: 5)',
|
|
58
|
+
default: 5,
|
|
59
|
+
minimum: 1,
|
|
60
|
+
maximum: 100,
|
|
61
|
+
},
|
|
52
62
|
max_symbols: {
|
|
53
63
|
type: 'number',
|
|
54
64
|
description: 'Max symbols per process (default: 10)',
|
|
55
65
|
default: 10,
|
|
66
|
+
minimum: 1,
|
|
67
|
+
maximum: 200,
|
|
56
68
|
},
|
|
57
69
|
include_content: {
|
|
58
70
|
type: 'boolean',
|
|
@@ -61,7 +73,12 @@ Hybrid ranking: BM25 keyword + semantic vector search, ranked by Reciprocal Rank
|
|
|
61
73
|
},
|
|
62
74
|
repo: {
|
|
63
75
|
type: 'string',
|
|
64
|
-
description: '
|
|
76
|
+
description: 'Indexed repository name or path, or group mode "@<groupName>" / "@<groupName>/<memberPath>" (member path keys from group.yaml). Omit when only one indexed repo exists.',
|
|
77
|
+
},
|
|
78
|
+
service: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
minLength: 1,
|
|
81
|
+
description: 'Optional monorepo service root (relative path, "/" separators). In group mode (@repo), prefix-matches symbol file paths; ignored for a normal repo name. Empty string is rejected server-side.',
|
|
65
82
|
},
|
|
66
83
|
},
|
|
67
84
|
required: ['query'],
|
|
@@ -135,7 +152,11 @@ AFTER THIS: Use impact() if planning changes, or READ gitnexus://repo/{name}/pro
|
|
|
135
152
|
|
|
136
153
|
Handles disambiguation: if multiple symbols share the same name, returns ranked candidates (each with a relevance score) for you to pick from. Use uid for zero-ambiguity lookup, or narrow the search with file_path and/or kind hints.
|
|
137
154
|
|
|
138
|
-
NOTE: ACCESSES edges (field read/write tracking) are included in context results with reason 'read' or 'write'. CALLS edges resolve through field access chains and method-call chains (e.g., user.address.getCity().save() produces CALLS edges at each step)
|
|
155
|
+
NOTE: ACCESSES edges (field read/write tracking) are included in context results with reason 'read' or 'write'. CALLS edges resolve through field access chains and method-call chains (e.g., user.address.getCity().save() produces CALLS edges at each step).
|
|
156
|
+
|
|
157
|
+
GROUP MODE: set "repo" to "@<groupName>" to run context in each member repo (aggregated list), or "@<groupName>/<groupRepoPath>" for one member. If you use "@<groupName>" only, the member defaults to the lexicographically first key in group.yaml "repos".
|
|
158
|
+
|
|
159
|
+
SERVICE: optional monorepo path prefix (case-sensitive path segments). When "repo" starts with "@", prefix-matches resolved symbol file paths; when a hit is outside the prefix, that member returns an empty payload for the symbol. Ignored for a normal indexed repo name.`,
|
|
139
160
|
inputSchema: {
|
|
140
161
|
type: 'object',
|
|
141
162
|
properties: {
|
|
@@ -156,7 +177,12 @@ NOTE: ACCESSES edges (field read/write tracking) are included in context results
|
|
|
156
177
|
},
|
|
157
178
|
repo: {
|
|
158
179
|
type: 'string',
|
|
159
|
-
description: '
|
|
180
|
+
description: 'Indexed repository name or path, or group mode "@<groupName>" / "@<groupName>/<memberPath>". Omit if only one repo is indexed.',
|
|
181
|
+
},
|
|
182
|
+
service: {
|
|
183
|
+
type: 'string',
|
|
184
|
+
minLength: 1,
|
|
185
|
+
description: 'Optional monorepo service root (relative path). Applies in group mode (@repo) only; ignored for a normal repo name. Empty string is rejected server-side.',
|
|
160
186
|
},
|
|
161
187
|
},
|
|
162
188
|
required: [],
|
|
@@ -251,7 +277,11 @@ TIP: Default traversal uses CALLS/IMPORTS/EXTENDS/IMPLEMENTS. For class members,
|
|
|
251
277
|
Handles disambiguation: when multiple symbols share the target name, returns ranked candidates (each with a relevance score) instead of silently picking one. Use target_uid for zero-ambiguity lookup, or narrow with file_path and/or kind hints.
|
|
252
278
|
|
|
253
279
|
EdgeType: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, HAS_PROPERTY, METHOD_OVERRIDES, METHOD_IMPLEMENTS, ACCESSES
|
|
254
|
-
Confidence: 1.0 = certain, <0.8 = fuzzy match
|
|
280
|
+
Confidence: 1.0 = certain, <0.8 = fuzzy match
|
|
281
|
+
|
|
282
|
+
GROUP MODE: set "repo" to "@<groupName>" for cross-repo impact anchored at the default member (lexicographically first key in group.yaml "repos"), or "@<groupName>/<groupRepoPath>" to choose the member (same path keys as in group.yaml). Phase-1 walk runs in that member; cross-boundary fan-out uses the group bridge.
|
|
283
|
+
|
|
284
|
+
SERVICE: optional monorepo path prefix (case-sensitive path segments). When "repo" starts with "@", scopes the local impact walk and cross-repo symbol paths to files under that prefix; ignored for a normal indexed repo name.`,
|
|
255
285
|
inputSchema: {
|
|
256
286
|
type: 'object',
|
|
257
287
|
properties: {
|
|
@@ -274,8 +304,17 @@ Confidence: 1.0 = certain, <0.8 = fuzzy match`,
|
|
|
274
304
|
},
|
|
275
305
|
maxDepth: {
|
|
276
306
|
type: 'number',
|
|
277
|
-
description: 'Max relationship depth (default: 3)',
|
|
307
|
+
description: 'Max relationship depth (default: 3, server clamps to 1–32)',
|
|
278
308
|
default: 3,
|
|
309
|
+
minimum: 1,
|
|
310
|
+
maximum: 32,
|
|
311
|
+
},
|
|
312
|
+
crossDepth: {
|
|
313
|
+
type: 'number',
|
|
314
|
+
description: 'Cross-repository hop depth via contract bridge (default: 1; values above server maximum are clamped)',
|
|
315
|
+
default: 1,
|
|
316
|
+
minimum: 1,
|
|
317
|
+
maximum: 32,
|
|
279
318
|
},
|
|
280
319
|
relationTypes: {
|
|
281
320
|
type: 'array',
|
|
@@ -283,10 +322,37 @@ Confidence: 1.0 = certain, <0.8 = fuzzy match`,
|
|
|
283
322
|
description: 'Filter: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, HAS_METHOD, HAS_PROPERTY, METHOD_OVERRIDES, METHOD_IMPLEMENTS, ACCESSES (default: usage-based, ACCESSES excluded by default)',
|
|
284
323
|
},
|
|
285
324
|
includeTests: { type: 'boolean', description: 'Include test files (default: false)' },
|
|
286
|
-
minConfidence: {
|
|
325
|
+
minConfidence: {
|
|
326
|
+
type: 'number',
|
|
327
|
+
description: 'Minimum edge confidence 0–1 (default: 0 when omitted; server clamps to 0–1)',
|
|
328
|
+
default: 0,
|
|
329
|
+
minimum: 0,
|
|
330
|
+
maximum: 1,
|
|
331
|
+
},
|
|
287
332
|
repo: {
|
|
288
333
|
type: 'string',
|
|
289
|
-
description: '
|
|
334
|
+
description: 'Indexed repository name or path, or group mode "@<groupName>" / "@<groupName>/<memberPath>". Omit if only one repo is indexed.',
|
|
335
|
+
},
|
|
336
|
+
service: {
|
|
337
|
+
type: 'string',
|
|
338
|
+
minLength: 1,
|
|
339
|
+
description: 'Optional monorepo service root (relative path). Applies when "repo" is group mode (@…); ignored for a normal repo name. Empty string is rejected server-side.',
|
|
340
|
+
},
|
|
341
|
+
subgroup: {
|
|
342
|
+
type: 'string',
|
|
343
|
+
description: 'Optional group subgroup prefix (member repo paths) limiting which repos participate in cross fan-out.',
|
|
344
|
+
},
|
|
345
|
+
timeoutMs: {
|
|
346
|
+
type: 'number',
|
|
347
|
+
description: 'Wall-clock budget in milliseconds for the Phase-1 local impact leg (default 30000)',
|
|
348
|
+
minimum: 1,
|
|
349
|
+
maximum: 3600000,
|
|
350
|
+
},
|
|
351
|
+
timeout: {
|
|
352
|
+
type: 'number',
|
|
353
|
+
description: 'Alias of timeoutMs (milliseconds) when timeoutMs is omitted',
|
|
354
|
+
minimum: 1,
|
|
355
|
+
maximum: 3600000,
|
|
290
356
|
},
|
|
291
357
|
},
|
|
292
358
|
required: ['target', 'direction'],
|
|
@@ -404,49 +470,4 @@ WHEN TO USE: After changing group.yaml or re-indexing member repos.`,
|
|
|
404
470
|
required: ['name'],
|
|
405
471
|
},
|
|
406
472
|
},
|
|
407
|
-
{
|
|
408
|
-
name: 'group_contracts',
|
|
409
|
-
description: `Inspect contracts and cross-links from the group's contracts.json.
|
|
410
|
-
|
|
411
|
-
WHEN TO USE: Debug cross-repo links after group_sync.`,
|
|
412
|
-
inputSchema: {
|
|
413
|
-
type: 'object',
|
|
414
|
-
properties: {
|
|
415
|
-
name: { type: 'string', description: 'Group name' },
|
|
416
|
-
type: { type: 'string', description: 'Filter by contract type (http, topic, …)' },
|
|
417
|
-
repo: { type: 'string', description: 'Filter by group repo path (e.g. app/backend)' },
|
|
418
|
-
unmatchedOnly: { type: 'boolean', description: 'Only contracts with no cross-link' },
|
|
419
|
-
},
|
|
420
|
-
required: ['name'],
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
name: 'group_query',
|
|
425
|
-
description: `Run the query tool across all repos in a group and merge process results via reciprocal rank fusion.
|
|
426
|
-
|
|
427
|
-
WHEN TO USE: Semantic / hybrid search across a whole product group.`,
|
|
428
|
-
inputSchema: {
|
|
429
|
-
type: 'object',
|
|
430
|
-
properties: {
|
|
431
|
-
name: { type: 'string', description: 'Group name' },
|
|
432
|
-
query: { type: 'string', description: 'Search query' },
|
|
433
|
-
subgroup: { type: 'string', description: 'Limit to repo paths under this prefix' },
|
|
434
|
-
limit: { type: 'number', description: 'Max merged results (default 5)' },
|
|
435
|
-
},
|
|
436
|
-
required: ['name', 'query'],
|
|
437
|
-
},
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
name: 'group_status',
|
|
441
|
-
description: `Report index staleness (commit vs HEAD) and Contract Registry staleness (indexedAt) for each repo in a group.
|
|
442
|
-
|
|
443
|
-
WHEN TO USE: Before group_sync or when agents should refresh indexes.`,
|
|
444
|
-
inputSchema: {
|
|
445
|
-
type: 'object',
|
|
446
|
-
properties: {
|
|
447
|
-
name: { type: 'string', description: 'Group name' },
|
|
448
|
-
},
|
|
449
|
-
required: ['name'],
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
473
|
];
|
package/package.json
CHANGED