@graphpilot-oss/graphpilot 0.0.1 → 1.0.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.
- package/CHANGELOG.md +72 -126
- package/README.md +290 -102
- package/dist/cli.js +41 -1
- package/dist/cli.js.map +1 -1
- package/dist/edges.js +22 -11
- package/dist/edges.js.map +1 -1
- package/dist/indexer.js +3 -3
- package/dist/indexer.js.map +1 -1
- package/dist/init.d.ts +28 -0
- package/dist/init.js +112 -0
- package/dist/init.js.map +1 -0
- package/dist/interactions.d.ts +5 -4
- package/dist/interactions.js +0 -0
- package/dist/interactions.js.map +1 -1
- package/dist/mcp.js +119 -90
- package/dist/mcp.js.map +1 -1
- package/dist/repo-resolve.d.ts +47 -0
- package/dist/repo-resolve.js +195 -0
- package/dist/repo-resolve.js.map +1 -0
- package/dist/storage.js +10 -1
- package/dist/storage.js.map +1 -1
- package/dist/symbols.js +26 -2
- package/dist/symbols.js.map +1 -1
- package/dist/validation.js +30 -4
- package/dist/validation.js.map +1 -1
- package/dist/validators.d.ts +1 -5
- package/dist/validators.js +0 -11
- package/dist/validators.js.map +1 -1
- package/dist/watcher.d.ts +10 -0
- package/dist/watcher.js +70 -7
- package/dist/watcher.js.map +1 -1
- package/examples/README.md +105 -0
- package/examples/claude-code/README.md +125 -0
- package/examples/claude-code/claude-routing.md +102 -0
- package/examples/claude-code/claude_config.json +8 -0
- package/examples/cline/.clinerules +39 -0
- package/examples/cline/README.md +104 -0
- package/examples/cline/cline_mcp_settings.json +10 -0
- package/examples/continue/.continuerules +39 -0
- package/examples/continue/README.md +98 -0
- package/examples/continue/config.json +13 -0
- package/examples/cursor/.cursorrules +39 -0
- package/examples/cursor/README.md +98 -0
- package/examples/cursor/mcp.json +11 -0
- package/examples/windsurf/.windsurfrules +39 -0
- package/examples/windsurf/README.md +85 -0
- package/examples/windsurf/mcp_config.json +8 -0
- package/package.json +14 -4
- package/.editorconfig +0 -15
- package/.github/CODEOWNERS +0 -22
- package/.github/FUNDING.yml +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
- package/.github/ISSUE_TEMPLATE/config.yml +0 -5
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -19
- package/.github/dependabot.yml +0 -15
- package/.github/workflows/ci.yml +0 -62
- package/.github/workflows/release.yml +0 -50
- package/.prettierignore +0 -19
- package/.prettierrc.json +0 -20
- package/CODE_OF_CONDUCT.md +0 -83
- package/CONTRIBUTING.md +0 -111
- package/bench/README.md +0 -544
- package/bench/results/agent-tier-2026-05-22.md +0 -28
- package/bench/results/agent-tier-summary.md +0 -44
- package/bench/results/baseline-tier-2026-05-22.md +0 -23
- package/bench/results/baseline.json +0 -810
- package/bench/results/baseline.md +0 -28
- package/bench/run-agent-tier-automated.ts +0 -234
- package/bench/run-agent-tier.md +0 -125
- package/bench/run-baseline-tier.ts +0 -200
- package/bench/run.ts +0 -210
- package/bench/runner-baseline.ts +0 -177
- package/bench/runner-graphpilot.ts +0 -131
- package/bench/score-agent-tier.ts +0 -191
- package/bench/score.ts +0 -59
- package/bench/tasks.ts +0 -236
- package/dist/provenance.d.ts +0 -74
- package/dist/provenance.js +0 -95
- package/dist/provenance.js.map +0 -1
- package/docs/architecture.md +0 -311
- package/docs/limitations.md +0 -156
- package/docs/mcp-setup.md +0 -231
- package/docs/quickstart.md +0 -202
- package/eslint.config.js +0 -148
- package/lefthook.yml +0 -81
- package/pnpm-workspace.yaml +0 -6
- package/scripts/smoke-stdio.mjs +0 -97
- package/src/cli.ts +0 -171
- package/src/edges.ts +0 -202
- package/src/git.ts +0 -255
- package/src/graph-schema.ts +0 -229
- package/src/impact.ts +0 -218
- package/src/indexer.ts +0 -152
- package/src/interactions.ts +0 -0
- package/src/mcp.ts +0 -652
- package/src/parser.ts +0 -138
- package/src/provenance.ts +0 -115
- package/src/query.ts +0 -148
- package/src/redact.ts +0 -122
- package/src/storage.ts +0 -115
- package/src/symbols.ts +0 -173
- package/src/validation.ts +0 -69
- package/src/validators.ts +0 -253
- package/src/watcher.ts +0 -383
- package/tests/edges.test.ts +0 -175
- package/tests/fixtures/sample.ts +0 -32
- package/tests/git.test.ts +0 -303
- package/tests/graph-schema.test.ts +0 -321
- package/tests/impact.test.ts +0 -454
- package/tests/interactions.test.ts +0 -180
- package/tests/lint-policy.test.ts +0 -106
- package/tests/mcp-stdio.test.ts +0 -171
- package/tests/mcp.test.ts +0 -335
- package/tests/parser.test.ts +0 -31
- package/tests/provenance.test.ts +0 -132
- package/tests/query.test.ts +0 -160
- package/tests/redact.test.ts +0 -167
- package/tests/security.test.ts +0 -144
- package/tests/symbols.test.ts +0 -78
- package/tests/validators.test.ts +0 -193
- package/tests/watcher.test.ts +0 -250
- package/tsconfig.json +0 -18
package/dist/indexer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fg from 'fast-glob';
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
|
-
import { resolve, relative } from 'node:path';
|
|
3
|
+
import { resolve, relative, sep } from 'node:path';
|
|
4
4
|
import { parseFile } from './parser.js';
|
|
5
5
|
import { extractSymbols } from './symbols.js';
|
|
6
6
|
import { extractRawCalls, resolveCallEdges } from './edges.js';
|
|
@@ -61,7 +61,7 @@ export async function indexDirectory(rootPath, opts = {}) {
|
|
|
61
61
|
filesFailed++;
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
|
-
if (!realFile.startsWith(realRoot)) {
|
|
64
|
+
if (realFile !== realRoot && !realFile.startsWith(realRoot + sep)) {
|
|
65
65
|
filesSkippedSymlink++;
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
@@ -71,7 +71,7 @@ export async function indexDirectory(rootPath, opts = {}) {
|
|
|
71
71
|
const fileSymbols = extractSymbols(parsed);
|
|
72
72
|
const fileCalls = extractRawCalls(parsed, fileSymbols);
|
|
73
73
|
if (useRelative) {
|
|
74
|
-
const rel = relative(absRoot, file);
|
|
74
|
+
const rel = relative(absRoot, file).split(sep).join('/');
|
|
75
75
|
// Track id rewrites so call edges can be remapped in lockstep.
|
|
76
76
|
const idRewrites = new Map();
|
|
77
77
|
for (const s of fileSymbols) {
|
package/dist/indexer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../src/indexer.ts"],"names":[],"mappings":"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"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type ClientId = 'cursor' | 'claude-code' | 'cline' | 'windsurf' | 'continue';
|
|
2
|
+
export interface ClientSpec {
|
|
3
|
+
name: string;
|
|
4
|
+
/** Path to the client's own MCP config — its existence signals the client is installed. */
|
|
5
|
+
configPath: string;
|
|
6
|
+
/** Relative path inside the package's examples/ dir (the routing template). */
|
|
7
|
+
templateFile: string;
|
|
8
|
+
/** Filename to write at the target repo root. */
|
|
9
|
+
outputFile: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const CLIENTS: Record<ClientId, ClientSpec>;
|
|
12
|
+
export declare const EXAMPLES_DIR: string;
|
|
13
|
+
export declare function detectInstalledClients(): ClientId[];
|
|
14
|
+
export interface InitOptions {
|
|
15
|
+
repoPath: string;
|
|
16
|
+
clients?: ClientId[];
|
|
17
|
+
all?: boolean;
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
examplesDir?: string;
|
|
20
|
+
prompt?: (destPath: string) => Promise<'overwrite' | 'skip'>;
|
|
21
|
+
}
|
|
22
|
+
export type WriteAction = 'written' | 'skipped' | 'dry-run';
|
|
23
|
+
export interface WriteResult {
|
|
24
|
+
client: ClientId;
|
|
25
|
+
action: WriteAction;
|
|
26
|
+
destPath: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function runInit(opts: InitOptions): Promise<WriteResult[]>;
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { homedir, platform } from 'node:os';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { createInterface } from 'node:readline';
|
|
6
|
+
function clineConfigPath() {
|
|
7
|
+
const p = platform();
|
|
8
|
+
const globalStorage = join('Code', 'User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json');
|
|
9
|
+
if (p === 'darwin')
|
|
10
|
+
return join(homedir(), 'Library', 'Application Support', globalStorage);
|
|
11
|
+
if (p === 'win32') {
|
|
12
|
+
return join(process.env['APPDATA'] ?? join(homedir(), 'AppData', 'Roaming'), globalStorage);
|
|
13
|
+
}
|
|
14
|
+
return join(homedir(), '.config', globalStorage);
|
|
15
|
+
}
|
|
16
|
+
export const CLIENTS = {
|
|
17
|
+
cursor: {
|
|
18
|
+
name: 'Cursor',
|
|
19
|
+
configPath: join(homedir(), '.cursor', 'mcp.json'),
|
|
20
|
+
templateFile: join('cursor', '.cursorrules'),
|
|
21
|
+
outputFile: '.cursorrules',
|
|
22
|
+
},
|
|
23
|
+
'claude-code': {
|
|
24
|
+
name: 'Claude Code',
|
|
25
|
+
configPath: join(homedir(), '.claude.json'),
|
|
26
|
+
templateFile: join('claude-code', 'claude-routing.md'),
|
|
27
|
+
outputFile: 'CLAUDE.md',
|
|
28
|
+
},
|
|
29
|
+
cline: {
|
|
30
|
+
name: 'Cline',
|
|
31
|
+
configPath: clineConfigPath(),
|
|
32
|
+
templateFile: join('cline', '.clinerules'),
|
|
33
|
+
outputFile: '.clinerules',
|
|
34
|
+
},
|
|
35
|
+
windsurf: {
|
|
36
|
+
name: 'Windsurf',
|
|
37
|
+
configPath: join(homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
|
|
38
|
+
templateFile: join('windsurf', '.windsurfrules'),
|
|
39
|
+
outputFile: '.windsurfrules',
|
|
40
|
+
},
|
|
41
|
+
continue: {
|
|
42
|
+
name: 'Continue',
|
|
43
|
+
configPath: join(homedir(), '.continue', 'config.json'),
|
|
44
|
+
templateFile: join('continue', '.continuerules'),
|
|
45
|
+
outputFile: '.continuerules',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export const EXAMPLES_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'examples');
|
|
49
|
+
export function detectInstalledClients() {
|
|
50
|
+
return Object.keys(CLIENTS).filter((id) => existsSync(CLIENTS[id].configPath));
|
|
51
|
+
}
|
|
52
|
+
async function defaultPrompt(destPath) {
|
|
53
|
+
if (!process.stdin.isTTY)
|
|
54
|
+
return 'skip';
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
57
|
+
rl.question(` ${destPath} already exists. Overwrite? [y/N] `, (answer) => {
|
|
58
|
+
rl.close();
|
|
59
|
+
resolve(answer.toLowerCase() === 'y' ? 'overwrite' : 'skip');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export async function runInit(opts) {
|
|
64
|
+
const { repoPath, all = false, dryRun = false, examplesDir: exDir = EXAMPLES_DIR, prompt = defaultPrompt, } = opts;
|
|
65
|
+
let targets;
|
|
66
|
+
if (opts.clients && opts.clients.length > 0) {
|
|
67
|
+
targets = opts.clients;
|
|
68
|
+
}
|
|
69
|
+
else if (all) {
|
|
70
|
+
targets = Object.keys(CLIENTS);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
targets = detectInstalledClients();
|
|
74
|
+
if (targets.length === 0) {
|
|
75
|
+
process.stdout.write('No supported clients detected. Pass --all to write all rules files,\n' +
|
|
76
|
+
'or --client <name> for a specific one.\n' +
|
|
77
|
+
'Supported clients: cursor, claude-code, cline, windsurf, continue\n');
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
process.stdout.write(`Detected: ${targets.join(', ')}\n`);
|
|
81
|
+
}
|
|
82
|
+
const results = [];
|
|
83
|
+
for (const id of targets) {
|
|
84
|
+
const spec = CLIENTS[id];
|
|
85
|
+
const templatePath = join(exDir, spec.templateFile);
|
|
86
|
+
const destPath = join(repoPath, spec.outputFile);
|
|
87
|
+
if (!existsSync(templatePath)) {
|
|
88
|
+
process.stdout.write(` [${spec.name}] skip — template not found: ${templatePath}\n`);
|
|
89
|
+
results.push({ client: id, action: 'skipped', destPath });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const content = readFileSync(templatePath, 'utf8');
|
|
93
|
+
if (dryRun) {
|
|
94
|
+
process.stdout.write(` [dry-run] would write ${spec.outputFile} (${content.length} bytes)\n`);
|
|
95
|
+
results.push({ client: id, action: 'dry-run', destPath });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (existsSync(destPath)) {
|
|
99
|
+
const decision = await prompt(destPath);
|
|
100
|
+
if (decision === 'skip') {
|
|
101
|
+
process.stdout.write(` [${spec.name}] skipped (${spec.outputFile} already exists)\n`);
|
|
102
|
+
results.push({ client: id, action: 'skipped', destPath });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
writeFileSync(destPath, content, 'utf8');
|
|
107
|
+
process.stdout.write(` [${spec.name}] wrote ${spec.outputFile}\n`);
|
|
108
|
+
results.push({ client: id, action: 'written', destPath });
|
|
109
|
+
}
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/interactions.d.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Append-only interaction log. One JSONL file per indexed repo:
|
|
3
3
|
* ~/.graphpilot/<repo-id>/interactions.jsonl
|
|
4
4
|
*
|
|
5
|
-
* Why we keep this even though v1 never reads it:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Why we keep this even though v1 never reads it: day-1 logs become day-180
|
|
6
|
+
* personalized ranking input. A fork starting fresh can't catch up — the
|
|
7
|
+
* accumulated interaction history is what makes adaptive symbol ranking
|
|
8
|
+
* possible without retraining.
|
|
8
9
|
*
|
|
9
|
-
* Privacy constraints (do NOT relax without
|
|
10
|
+
* Privacy constraints (do NOT relax without re-reading SECURITY.md):
|
|
10
11
|
* - Never log file contents
|
|
11
12
|
* - Cap every string field at MAX_FIELD_LEN
|
|
12
13
|
* - Strip control characters from string values
|
package/dist/interactions.js
CHANGED
|
Binary file
|
package/dist/interactions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions.js","sourceRoot":"","sources":["../src/interactions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"interactions.js","sourceRoot":"","sources":["../src/interactions.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.js
CHANGED
|
@@ -1,40 +1,62 @@
|
|
|
1
|
+
import { statSync } from 'node:fs';
|
|
1
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, RootsListChangedNotificationSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { GraphIndex } from './query.js';
|
|
6
6
|
import { indexDirectory } from './indexer.js';
|
|
7
|
-
import { loadGraph, saveGraph, repoIdFor } from './storage.js';
|
|
7
|
+
import { loadGraph, saveGraph, repoIdFor, graphPath } from './storage.js';
|
|
8
8
|
import { validateRootPath } from './validation.js';
|
|
9
|
-
import { validateGpIndex, validateGpRecall, validateGpCallers, validateGpImpact,
|
|
9
|
+
import { validateGpIndex, validateGpRecall, validateGpCallers, validateGpImpact, } from './validators.js';
|
|
10
10
|
import { withInteractionLog } from './interactions.js';
|
|
11
11
|
import { analyzeImpact } from './impact.js';
|
|
12
12
|
import { getChangedFiles, resolveIndexRoot } from './git.js';
|
|
13
|
+
import { formatNoIndexError, getMcpClientRoots, resolveRepoPath, rootUriToFilesystemPath, setMcpClientRoots, } from './repo-resolve.js';
|
|
13
14
|
const SERVER_NAME = 'graphpilot';
|
|
14
15
|
const SERVER_VERSION = '0.0.1';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Shown on every tool's optional `path` field. */
|
|
17
|
+
const PATH_FIELD_DESC = 'Repo root with a GraphPilot index. Optional: when omitted, resolves via ' +
|
|
18
|
+
'GRAPHPILOT_ROOT, MCP workspace roots, parent walk, or a single ~/.graphpilot index.';
|
|
18
19
|
const indexCache = new Map();
|
|
20
|
+
/** Set when buildMcpServer wires roots handlers — used for lazy roots/list. */
|
|
21
|
+
let mcpServerForRoots = null;
|
|
22
|
+
let rootsRefreshInflight = null;
|
|
19
23
|
function getOrLoadIndex(rawPath) {
|
|
24
|
+
const requested = resolveRepoPath(rawPath);
|
|
20
25
|
// Re-root to the git worktree top so MCP tool calls from a subdir of a
|
|
21
26
|
// worktree still resolve to the branch-level index. Outside git this is
|
|
22
27
|
// a no-op.
|
|
23
|
-
const { root } = resolveIndexRoot(
|
|
28
|
+
const { root } = resolveIndexRoot(requested);
|
|
29
|
+
// Check whether graph.json changed since we last loaded it. Any external
|
|
30
|
+
// writer (CLI re-index, another MCP process, gp_index from a parallel
|
|
31
|
+
// session) will bump the mtime and trigger a cache miss here.
|
|
32
|
+
let currentMtimeMs = 0;
|
|
33
|
+
let currentSizeBytes = 0;
|
|
34
|
+
try {
|
|
35
|
+
const st = statSync(graphPath(root));
|
|
36
|
+
currentMtimeMs = st.mtimeMs;
|
|
37
|
+
currentSizeBytes = st.size;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// File doesn't exist yet — fall through; loadGraph will return null.
|
|
41
|
+
}
|
|
24
42
|
const cached = indexCache.get(root);
|
|
25
|
-
if (cached
|
|
26
|
-
|
|
43
|
+
if (cached &&
|
|
44
|
+
currentMtimeMs !== 0 &&
|
|
45
|
+
cached.mtimeMs === currentMtimeMs &&
|
|
46
|
+
cached.sizeBytes === currentSizeBytes) {
|
|
47
|
+
return { idx: cached.idx, root };
|
|
48
|
+
}
|
|
49
|
+
// Cache miss or stale entry — (re)load from disk.
|
|
50
|
+
indexCache.delete(root);
|
|
27
51
|
const graph = loadGraph(root);
|
|
28
52
|
if (!graph) {
|
|
29
53
|
return {
|
|
30
54
|
root,
|
|
31
|
-
error:
|
|
32
|
-
`Ask the user to run \`graphpilot index ${rawPath ?? '.'}\` first, or ` +
|
|
33
|
-
`call the gp_index tool to build one.`,
|
|
55
|
+
error: formatNoIndexError(requested, root),
|
|
34
56
|
};
|
|
35
57
|
}
|
|
36
58
|
const idx = new GraphIndex(graph);
|
|
37
|
-
indexCache.set(root, idx);
|
|
59
|
+
indexCache.set(root, { idx, mtimeMs: currentMtimeMs, sizeBytes: currentSizeBytes });
|
|
38
60
|
return { idx, root };
|
|
39
61
|
}
|
|
40
62
|
function invalidateCache(absRoot) {
|
|
@@ -85,29 +107,17 @@ function fmtEdge(e, idx, index) {
|
|
|
85
107
|
// Tool catalog (sent to clients via tools/list)
|
|
86
108
|
// ----------------------------------------------------------------------------
|
|
87
109
|
const TOOLS = [
|
|
88
|
-
{
|
|
89
|
-
name: 'gp_stats',
|
|
90
|
-
description: 'Show GraphPilot index health for a repo (symbol count, edge count, ' +
|
|
91
|
-
'when indexed). Use this to confirm the index is fresh before asking ' +
|
|
92
|
-
'structural questions.',
|
|
93
|
-
inputSchema: {
|
|
94
|
-
type: 'object',
|
|
95
|
-
properties: {
|
|
96
|
-
path: { type: 'string', description: 'Repo path. Default: cwd.' },
|
|
97
|
-
},
|
|
98
|
-
additionalProperties: false,
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
110
|
{
|
|
102
111
|
name: 'gp_index',
|
|
103
|
-
description: '
|
|
104
|
-
'
|
|
112
|
+
description: 'Re-index the repo after batch edits so subsequent gp_* calls see your changes. ' +
|
|
113
|
+
'Call after any non-trivial edit session. ' +
|
|
114
|
+
'Do NOT call before every query — indexing is slow; only needed when source files changed.',
|
|
105
115
|
inputSchema: {
|
|
106
116
|
type: 'object',
|
|
107
117
|
properties: {
|
|
108
118
|
path: {
|
|
109
119
|
type: 'string',
|
|
110
|
-
description:
|
|
120
|
+
description: PATH_FIELD_DESC,
|
|
111
121
|
},
|
|
112
122
|
},
|
|
113
123
|
additionalProperties: false,
|
|
@@ -115,9 +125,11 @@ const TOOLS = [
|
|
|
115
125
|
},
|
|
116
126
|
{
|
|
117
127
|
name: 'gp_recall',
|
|
118
|
-
description: '
|
|
119
|
-
'
|
|
120
|
-
'
|
|
128
|
+
description: 'Find a symbol definition by name — returns kind, file:line, and signature. ' +
|
|
129
|
+
'ALWAYS use instead of `grep -rn "function X"` or reading files to locate a definition: ' +
|
|
130
|
+
'pre-indexed, no false positives from comments or strings, sub-millisecond. ' +
|
|
131
|
+
'Pass substring:true for partial-name searches. ' +
|
|
132
|
+
'Do NOT use for "who calls X?" — use gp_callers for that.',
|
|
121
133
|
inputSchema: {
|
|
122
134
|
type: 'object',
|
|
123
135
|
properties: {
|
|
@@ -137,7 +149,7 @@ const TOOLS = [
|
|
|
137
149
|
},
|
|
138
150
|
path: {
|
|
139
151
|
type: 'string',
|
|
140
|
-
description:
|
|
152
|
+
description: PATH_FIELD_DESC,
|
|
141
153
|
},
|
|
142
154
|
},
|
|
143
155
|
required: ['query'],
|
|
@@ -146,9 +158,12 @@ const TOOLS = [
|
|
|
146
158
|
},
|
|
147
159
|
{
|
|
148
160
|
name: 'gp_callers',
|
|
149
|
-
description: 'List
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
description: 'List every caller of a symbol (direction=callers) or everything it calls ' +
|
|
162
|
+
'(direction=callees). ALWAYS use instead of `grep -rn "X("` for "who calls X?" — ' +
|
|
163
|
+
'pre-indexed reverse map, sub-millisecond, no false positives from comments or strings. ' +
|
|
164
|
+
'Use direction=callers to find dependents before a rename; direction=callees to ' +
|
|
165
|
+
'understand what a function depends on. ' +
|
|
166
|
+
'Do NOT use for full blast-radius analysis across multiple hops — use gp_impact instead.',
|
|
152
167
|
inputSchema: {
|
|
153
168
|
type: 'object',
|
|
154
169
|
properties: {
|
|
@@ -173,7 +188,7 @@ const TOOLS = [
|
|
|
173
188
|
},
|
|
174
189
|
path: {
|
|
175
190
|
type: 'string',
|
|
176
|
-
description:
|
|
191
|
+
description: PATH_FIELD_DESC,
|
|
177
192
|
},
|
|
178
193
|
},
|
|
179
194
|
required: ['symbol'],
|
|
@@ -182,15 +197,14 @@ const TOOLS = [
|
|
|
182
197
|
},
|
|
183
198
|
{
|
|
184
199
|
name: 'gp_impact',
|
|
185
|
-
description: '
|
|
186
|
-
'transitive callers
|
|
187
|
-
'
|
|
188
|
-
'
|
|
189
|
-
'
|
|
190
|
-
'
|
|
191
|
-
'
|
|
192
|
-
'
|
|
193
|
-
'and refactor scoping.',
|
|
200
|
+
description: 'Compute the blast radius of a rename or signature change: direct callers, ' +
|
|
201
|
+
'transitive callers up to depth 3, affected tests, and whether the symbol is ' +
|
|
202
|
+
'exported (breaking-change risk). ALWAYS call before proposing a rename, ' +
|
|
203
|
+
'signature change, or behavior change — replaces `git diff | xargs grep` with ' +
|
|
204
|
+
'a single structured answer. ' +
|
|
205
|
+
'Pass `since: <commit|branch>` to scope callers to files changed since that ref ' +
|
|
206
|
+
'(ideal for PR review or refactor scoping). ' +
|
|
207
|
+
'Do NOT use just to see direct callers; use gp_callers for that.',
|
|
194
208
|
inputSchema: {
|
|
195
209
|
type: 'object',
|
|
196
210
|
properties: {
|
|
@@ -206,7 +220,7 @@ const TOOLS = [
|
|
|
206
220
|
},
|
|
207
221
|
path: {
|
|
208
222
|
type: 'string',
|
|
209
|
-
description:
|
|
223
|
+
description: PATH_FIELD_DESC,
|
|
210
224
|
},
|
|
211
225
|
since: {
|
|
212
226
|
type: 'string',
|
|
@@ -219,35 +233,9 @@ const TOOLS = [
|
|
|
219
233
|
},
|
|
220
234
|
},
|
|
221
235
|
];
|
|
222
|
-
function handleGpStats(args) {
|
|
223
|
-
const out = getOrLoadIndex(args.path);
|
|
224
|
-
if ('error' in out) {
|
|
225
|
-
return { text: out.error, results: 0, isError: true };
|
|
226
|
-
}
|
|
227
|
-
const { idx } = out;
|
|
228
|
-
const s = idx.stats;
|
|
229
|
-
const g = idx.graph;
|
|
230
|
-
// Git provenance — surface branch + short SHA so the agent can cite
|
|
231
|
-
// the exact commit the index was built against. Omitted gracefully
|
|
232
|
-
// when the indexed root isn't a git repo.
|
|
233
|
-
const gitLines = [];
|
|
234
|
-
if (g.indexedBranch)
|
|
235
|
-
gitLines.push(`Branch: ${g.indexedBranch}`);
|
|
236
|
-
if (g.indexedSha)
|
|
237
|
-
gitLines.push(`Commit SHA: ${g.indexedSha.slice(0, 7)}`);
|
|
238
|
-
const text = [
|
|
239
|
-
`Repo: ${g.rootPath}`,
|
|
240
|
-
`Repo id: ${g.repoId}`,
|
|
241
|
-
`Indexed at: ${g.indexedAt}`,
|
|
242
|
-
...gitLines,
|
|
243
|
-
`Files: ${g.filesIndexed}`,
|
|
244
|
-
`Symbols: ${s.symbols}`,
|
|
245
|
-
`Calls: ${s.edges} (${s.resolvedEdges} resolved)`,
|
|
246
|
-
].join('\n');
|
|
247
|
-
return { text, results: 1 };
|
|
248
|
-
}
|
|
249
236
|
async function handleGpIndex(args) {
|
|
250
|
-
|
|
237
|
+
await ensureClientRootsCached();
|
|
238
|
+
const requested = resolveRepoPath(args.path);
|
|
251
239
|
const { root, redirected } = resolveIndexRoot(requested);
|
|
252
240
|
const refusal = validateRootPath(root);
|
|
253
241
|
if (refusal)
|
|
@@ -294,7 +282,8 @@ async function handleGpIndex(args) {
|
|
|
294
282
|
` Took: ${result.durationMs}ms`;
|
|
295
283
|
return { text, results: 1 };
|
|
296
284
|
}
|
|
297
|
-
function handleGpRecall(args) {
|
|
285
|
+
async function handleGpRecall(args) {
|
|
286
|
+
await ensureClientRootsCached();
|
|
298
287
|
const out = getOrLoadIndex(args.path);
|
|
299
288
|
if ('error' in out) {
|
|
300
289
|
return { text: out.error, results: 0, isError: true };
|
|
@@ -314,7 +303,8 @@ function handleGpRecall(args) {
|
|
|
314
303
|
const body = matches.map((s, i) => fmtSymbol(s, idx, i)).join('\n\n');
|
|
315
304
|
return { text: header + body, results: matches.length };
|
|
316
305
|
}
|
|
317
|
-
function handleGpCallers(args) {
|
|
306
|
+
async function handleGpCallers(args) {
|
|
307
|
+
await ensureClientRootsCached();
|
|
318
308
|
const out = getOrLoadIndex(args.path);
|
|
319
309
|
if ('error' in out) {
|
|
320
310
|
return { text: out.error, results: 0, isError: true };
|
|
@@ -423,6 +413,7 @@ function fmtImpactReport(report, idx, diff = { changedFileCount: null }) {
|
|
|
423
413
|
return lines.join('\n');
|
|
424
414
|
}
|
|
425
415
|
async function handleGpImpact(args) {
|
|
416
|
+
await ensureClientRootsCached();
|
|
426
417
|
const out = getOrLoadIndex(args.path);
|
|
427
418
|
if ('error' in out) {
|
|
428
419
|
return { text: out.error, results: 0, isError: true };
|
|
@@ -460,10 +451,57 @@ async function handleGpImpact(args) {
|
|
|
460
451
|
return { text, results: totalResults };
|
|
461
452
|
}
|
|
462
453
|
// ----------------------------------------------------------------------------
|
|
454
|
+
// MCP workspace roots (client → server)
|
|
455
|
+
// ----------------------------------------------------------------------------
|
|
456
|
+
/** Ensure roots/list has run when the client supports workspace roots. */
|
|
457
|
+
async function ensureClientRootsCached() {
|
|
458
|
+
if (!mcpServerForRoots?.getClientCapabilities()?.roots)
|
|
459
|
+
return;
|
|
460
|
+
if (getMcpClientRoots().length > 0)
|
|
461
|
+
return;
|
|
462
|
+
if (!rootsRefreshInflight) {
|
|
463
|
+
rootsRefreshInflight = refreshMcpClientRoots(mcpServerForRoots).finally(() => {
|
|
464
|
+
rootsRefreshInflight = null;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
await rootsRefreshInflight;
|
|
468
|
+
}
|
|
469
|
+
async function refreshMcpClientRoots(server) {
|
|
470
|
+
const caps = server.getClientCapabilities();
|
|
471
|
+
if (!caps?.roots) {
|
|
472
|
+
setMcpClientRoots([]);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
const { roots } = await server.listRoots();
|
|
477
|
+
const paths = roots
|
|
478
|
+
.map((r) => rootUriToFilesystemPath(r.uri))
|
|
479
|
+
.filter((p) => p !== null);
|
|
480
|
+
setMcpClientRoots(paths);
|
|
481
|
+
if (paths.length > 0) {
|
|
482
|
+
process.stderr.write(`[graphpilot] MCP workspace roots: ${paths.join(', ')}\n`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
process.stderr.write(`[graphpilot] roots/list failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
487
|
+
setMcpClientRoots([]);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function wireMcpClientRoots(server) {
|
|
491
|
+
mcpServerForRoots = server;
|
|
492
|
+
server.oninitialized = () => {
|
|
493
|
+
void refreshMcpClientRoots(server);
|
|
494
|
+
};
|
|
495
|
+
server.setNotificationHandler(RootsListChangedNotificationSchema, () => {
|
|
496
|
+
void refreshMcpClientRoots(server);
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
// ----------------------------------------------------------------------------
|
|
463
500
|
// Server builder + dispatcher
|
|
464
501
|
// ----------------------------------------------------------------------------
|
|
465
502
|
export function buildMcpServer() {
|
|
466
503
|
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
|
|
504
|
+
wireMcpClientRoots(server);
|
|
467
505
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
468
506
|
tools: TOOLS,
|
|
469
507
|
}));
|
|
@@ -474,21 +512,12 @@ export function buildMcpServer() {
|
|
|
474
512
|
// so the log captures even invalid-input attempts. Fall back to cwd.
|
|
475
513
|
// Resolve to the worktree top so per-tool calls from a subdir land in
|
|
476
514
|
// the same interaction log as the rest of the branch's work.
|
|
477
|
-
const requestedLogPath = typeof rawArgs.path === 'string' ? rawArgs.path :
|
|
515
|
+
const requestedLogPath = resolveRepoPath(typeof rawArgs.path === 'string' ? rawArgs.path : undefined);
|
|
478
516
|
const repoRootForLog = resolveIndexRoot(requestedLogPath).root;
|
|
479
517
|
return withInteractionLog(repoRootForLog, name, rawArgs, async () => {
|
|
480
518
|
// Validate first
|
|
481
519
|
let result;
|
|
482
520
|
switch (name) {
|
|
483
|
-
case 'gp_stats': {
|
|
484
|
-
const v = validateGpStats(rawArgs);
|
|
485
|
-
if (!v.ok) {
|
|
486
|
-
result = { text: `Invalid input: ${v.error}`, results: 0, isError: true };
|
|
487
|
-
break;
|
|
488
|
-
}
|
|
489
|
-
result = handleGpStats(v.value);
|
|
490
|
-
break;
|
|
491
|
-
}
|
|
492
521
|
case 'gp_index': {
|
|
493
522
|
const v = validateGpIndex(rawArgs);
|
|
494
523
|
if (!v.ok) {
|
|
@@ -504,7 +533,7 @@ export function buildMcpServer() {
|
|
|
504
533
|
result = { text: `Invalid input: ${v.error}`, results: 0, isError: true };
|
|
505
534
|
break;
|
|
506
535
|
}
|
|
507
|
-
result = handleGpRecall(v.value);
|
|
536
|
+
result = await handleGpRecall(v.value);
|
|
508
537
|
break;
|
|
509
538
|
}
|
|
510
539
|
case 'gp_callers': {
|
|
@@ -513,7 +542,7 @@ export function buildMcpServer() {
|
|
|
513
542
|
result = { text: `Invalid input: ${v.error}`, results: 0, isError: true };
|
|
514
543
|
break;
|
|
515
544
|
}
|
|
516
|
-
result = handleGpCallers(v.value);
|
|
545
|
+
result = await handleGpCallers(v.value);
|
|
517
546
|
break;
|
|
518
547
|
}
|
|
519
548
|
case 'gp_impact': {
|