@nanonets/graft 0.8.0 → 0.8.2
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/README.md +45 -15
- package/dist/ask/ask.js +1 -1
- package/dist/ask/ask.js.map +1 -1
- package/dist/claude/format.d.ts +40 -13
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +54 -15
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts +15 -0
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +52 -3
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +17 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +47 -21
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +19 -2
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +13 -5
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/state.d.ts +5 -20
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js +10 -73
- package/dist/claude/state.js.map +1 -1
- package/dist/cli-picker.d.ts +60 -0
- package/dist/cli-picker.d.ts.map +1 -0
- package/dist/cli-picker.js +268 -0
- package/dist/cli-picker.js.map +1 -0
- package/dist/cli.js +101 -12
- package/dist/cli.js.map +1 -1
- package/dist/context/node-file.d.ts +21 -0
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +63 -1
- package/dist/context/node-file.js.map +1 -1
- package/dist/engine.d.ts +2 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/build.d.ts +13 -2
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +98 -20
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +8 -0
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/extract-cache.d.ts +96 -0
- package/dist/graph/extract-cache.d.ts.map +1 -0
- package/dist/graph/extract-cache.js +199 -0
- package/dist/graph/extract-cache.js.map +1 -0
- package/dist/graph/fingerprint.d.ts +70 -0
- package/dist/graph/fingerprint.d.ts.map +1 -0
- package/dist/graph/fingerprint.js +147 -0
- package/dist/graph/fingerprint.js.map +1 -0
- package/dist/graph/load.d.ts +11 -0
- package/dist/graph/load.d.ts.map +1 -1
- package/dist/graph/load.js +14 -0
- package/dist/graph/load.js.map +1 -1
- package/dist/graph/refresh.d.ts +49 -0
- package/dist/graph/refresh.d.ts.map +1 -0
- package/dist/graph/refresh.js +202 -0
- package/dist/graph/refresh.js.map +1 -0
- package/dist/graph/source-files.d.ts +18 -0
- package/dist/graph/source-files.d.ts.map +1 -0
- package/dist/graph/source-files.js +36 -0
- package/dist/graph/source-files.js.map +1 -0
- package/dist/graph/traverse-cli.d.ts +1 -1
- package/dist/graph/traverse-cli.js +2 -2
- package/dist/graph/traverse.d.ts +2 -2
- package/dist/graph/traverse.js +1 -1
- package/dist/hosts/codex-hooks.d.ts +8 -0
- package/dist/hosts/codex-hooks.d.ts.map +1 -1
- package/dist/hosts/codex-hooks.js +27 -4
- package/dist/hosts/codex-hooks.js.map +1 -1
- package/dist/hosts/init.d.ts +2 -0
- package/dist/hosts/init.d.ts.map +1 -1
- package/dist/hosts/init.js +5 -2
- package/dist/hosts/init.js.map +1 -1
- package/dist/hosts/mcp-config.d.ts +29 -1
- package/dist/hosts/mcp-config.d.ts.map +1 -1
- package/dist/hosts/mcp-config.js +77 -10
- package/dist/hosts/mcp-config.js.map +1 -1
- package/dist/hosts/plan.d.ts +32 -0
- package/dist/hosts/plan.d.ts.map +1 -0
- package/dist/hosts/plan.js +67 -0
- package/dist/hosts/plan.js.map +1 -0
- package/dist/hosts/registry.d.ts.map +1 -1
- package/dist/hosts/registry.js +9 -0
- package/dist/hosts/registry.js.map +1 -1
- package/dist/mcp/instructions.d.ts +29 -0
- package/dist/mcp/instructions.d.ts.map +1 -0
- package/dist/mcp/instructions.js +55 -0
- package/dist/mcp/instructions.js.map +1 -0
- package/dist/mcp/server.d.ts +6 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +15 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +4 -2
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +144 -104
- package/dist/mcp/tools.js.map +1 -1
- package/dist/search/grep-cli.js +1 -1
- package/dist/search/grep.js +1 -1
- package/dist/util/state.d.ts +45 -0
- package/dist/util/state.d.ts.map +1 -0
- package/dist/util/state.js +121 -0
- package/dist/util/state.js.map +1 -0
- package/package.json +1 -1
package/dist/hosts/mcp-config.js
CHANGED
|
@@ -2,12 +2,56 @@
|
|
|
2
2
|
* Register the graft MCP server in each host's config.
|
|
3
3
|
* JSON hosts get a keyed merge (other servers preserved; unparseable files
|
|
4
4
|
* are never rewritten). The TOML host gets an append-if-absent section.
|
|
5
|
+
*
|
|
6
|
+
* `mcpTargets()` is the pure "which files would this touch" half, so `graft
|
|
7
|
+
* init --dry-run` and the picker can report paths without writing;
|
|
8
|
+
* `registerMcpConfigs()` walks that same list to do the writing.
|
|
5
9
|
*/
|
|
6
10
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync } from 'node:fs';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
7
12
|
import { dirname, join } from 'node:path';
|
|
8
13
|
import { homedir } from 'node:os';
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
/**
|
|
15
|
+
* How to launch the MCP server, decided once at init time.
|
|
16
|
+
*
|
|
17
|
+
* `npx -y` resolves the package before it can serve: measured at a 211 ms
|
|
18
|
+
* spawn→`initialize` handshake against 80 ms for the installed binary, five runs
|
|
19
|
+
* each. The harness registers a server's tools only once that handshake lands, and
|
|
20
|
+
* a slow one can miss the first request entirely — in a traced session graft's
|
|
21
|
+
* tools arrived 13.9 s in, four model turns too late to shape the approach. (That
|
|
22
|
+
* 13.9 s is NOT explained by 130 ms; the gap's cause is still unknown. This is the
|
|
23
|
+
* cheap half of the fix, not the whole of it.)
|
|
24
|
+
*
|
|
25
|
+
* Deliberately a bare command name, never an absolute path: these files get
|
|
26
|
+
* committed and shared, and this repo already carries the scar of the alternative —
|
|
27
|
+
* a checked-in hook shim with another machine's home directory baked into it. A
|
|
28
|
+
* bare `graft` works on any machine that has it installed; `npx` remains the
|
|
29
|
+
* fallback for machines that don't.
|
|
30
|
+
*/
|
|
31
|
+
const NPX_LAUNCH = { command: 'npx', args: ['-y', '@nanonets/graft', 'mcp'] };
|
|
32
|
+
const BIN_LAUNCH = { command: 'graft', args: ['mcp'] };
|
|
33
|
+
function graftOnPath() {
|
|
34
|
+
const r = spawnSync('graft', ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
35
|
+
return r.status === 0;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* JSON hosts: `{ command, args }`.
|
|
39
|
+
*
|
|
40
|
+
* `GRAFT_MCP_NPX=1` forces the `npx` form — the escape hatch for a machine whose
|
|
41
|
+
* global install is stale or shadowed, and what the tests set so their expectations
|
|
42
|
+
* don't depend on whether the machine running them happens to have graft installed.
|
|
43
|
+
* `opts.onPath` is the same override for direct unit tests of both branches.
|
|
44
|
+
*/
|
|
45
|
+
export function serverEntry(opts = {}) {
|
|
46
|
+
const forced = process.env.GRAFT_MCP_NPX;
|
|
47
|
+
if (forced !== undefined && forced !== '' && forced !== '0' && forced !== 'false')
|
|
48
|
+
return NPX_LAUNCH;
|
|
49
|
+
return (opts.onPath ?? graftOnPath()) ? BIN_LAUNCH : NPX_LAUNCH;
|
|
50
|
+
}
|
|
51
|
+
function opencodeEntry() {
|
|
52
|
+
const { command, args } = serverEntry();
|
|
53
|
+
return { type: 'local', command: [command, ...args], enabled: true };
|
|
54
|
+
}
|
|
11
55
|
function dirExists(p) {
|
|
12
56
|
try {
|
|
13
57
|
return statSync(p).isDirectory();
|
|
@@ -44,38 +88,61 @@ function upsertCodexToml(id, path) {
|
|
|
44
88
|
const text = existed ? readFileSync(path, 'utf8') : '';
|
|
45
89
|
if (/^\[mcp_servers\.graft\]$/m.test(text))
|
|
46
90
|
return { id, path, action: 'unchanged' };
|
|
47
|
-
const
|
|
91
|
+
const { command, args } = serverEntry();
|
|
92
|
+
const argList = args.map((a) => JSON.stringify(a)).join(", ");
|
|
93
|
+
const section = `[mcp_servers.graft]\ncommand = \"${command}\"\nargs = [${argList}]\n`;
|
|
48
94
|
const sep = text.length === 0 ? '' : text.endsWith('\n') ? '\n' : '\n\n';
|
|
49
95
|
mkdirSync(dirname(path), { recursive: true });
|
|
50
96
|
writeFileSync(path, `${text}${sep}${section}`);
|
|
51
97
|
return { id, path, action: existed ? 'updated' : 'created' };
|
|
52
98
|
}
|
|
53
|
-
|
|
99
|
+
function jsonTarget(hostId, id, path, topKey, entry, scope = 'repo') {
|
|
100
|
+
return { hostId, id, path, scope, kind: 'mcp', what: `${topKey}.graft`, format: 'json', topKey, entry };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The MCP config files selecting these hosts would touch — pure, no writes.
|
|
104
|
+
* Codex's target is the user-level `~/.codex/config.toml`, so it is scoped
|
|
105
|
+
* 'global': registering there affects every project on the machine.
|
|
106
|
+
*/
|
|
107
|
+
export function mcpTargets(repo, ids, opts = {}) {
|
|
54
108
|
const home = opts.home ?? homedir();
|
|
109
|
+
const entry = serverEntry();
|
|
55
110
|
const out = [];
|
|
56
111
|
for (const id of ids) {
|
|
57
112
|
switch (id) {
|
|
58
113
|
case 'cursor':
|
|
59
|
-
out.push(
|
|
114
|
+
out.push(jsonTarget(id, id, join(repo, '.cursor', 'mcp.json'), 'mcpServers', entry));
|
|
60
115
|
break;
|
|
61
116
|
case 'gemini':
|
|
62
|
-
out.push(
|
|
117
|
+
out.push(jsonTarget(id, id, join(repo, '.gemini', 'settings.json'), 'mcpServers', entry));
|
|
63
118
|
break;
|
|
64
119
|
case 'kiro':
|
|
65
|
-
out.push(
|
|
120
|
+
out.push(jsonTarget(id, id, join(repo, '.kiro', 'settings', 'mcp.json'), 'mcpServers', entry));
|
|
66
121
|
break;
|
|
67
122
|
case 'agents':
|
|
123
|
+
// Guarded on the CLI actually being installed, so a plan only ever
|
|
124
|
+
// lists files a real run would touch.
|
|
68
125
|
if (dirExists(join(home, '.codex'))) {
|
|
69
|
-
out.push(
|
|
126
|
+
out.push({
|
|
127
|
+
hostId: id, id: 'codex', path: join(home, '.codex', 'config.toml'),
|
|
128
|
+
scope: 'global', kind: 'mcp', what: '[mcp_servers.graft]', format: 'toml',
|
|
129
|
+
});
|
|
70
130
|
}
|
|
71
131
|
if (dirExists(join(home, '.config', 'opencode'))) {
|
|
72
|
-
out.push(
|
|
132
|
+
out.push(jsonTarget(id, 'opencode', join(repo, 'opencode.json'), 'mcp', opencodeEntry()));
|
|
73
133
|
}
|
|
74
134
|
break;
|
|
75
135
|
default:
|
|
76
|
-
break; // copilot / windsurf /
|
|
136
|
+
break; // copilot / windsurf / adal: no MCP target in this phase
|
|
77
137
|
}
|
|
78
138
|
}
|
|
79
139
|
return out;
|
|
80
140
|
}
|
|
141
|
+
export function registerMcpConfigs(repo, ids, opts = {}) {
|
|
142
|
+
return mcpTargets(repo, ids, opts)
|
|
143
|
+
.filter((t) => opts.global !== false || t.scope !== 'global')
|
|
144
|
+
.map((t) => t.format === 'toml'
|
|
145
|
+
? upsertCodexToml(t.id, t.path)
|
|
146
|
+
: mergeJsonKey(t.id, t.path, t.topKey, t.entry));
|
|
147
|
+
}
|
|
81
148
|
//# sourceMappingURL=mcp-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../src/hosts/mcp-config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../src/hosts/mcp-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Where a write lands. 'global' = outside the repo, affects every project. */
|
|
2
|
+
export type WriteScope = 'repo' | 'global';
|
|
3
|
+
export interface PlannedWrite {
|
|
4
|
+
/** The selectable host this write belongs to ('claude', 'agents', 'cursor', …). */
|
|
5
|
+
hostId: string;
|
|
6
|
+
/** Reporting id — differs from hostId where one host writes several configs
|
|
7
|
+
* (the 'agents' host covers both 'codex' and 'opencode'). */
|
|
8
|
+
id: string;
|
|
9
|
+
path: string;
|
|
10
|
+
scope: WriteScope;
|
|
11
|
+
kind: 'instruction' | 'mcp' | 'hook' | 'claude';
|
|
12
|
+
/** Short human label for what changes in that file. */
|
|
13
|
+
what: string;
|
|
14
|
+
}
|
|
15
|
+
export interface HostPlan {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
detected: boolean;
|
|
19
|
+
writes: PlannedWrite[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every host graft can wire, each with the full set of files selecting it would
|
|
23
|
+
* touch. Claude Code comes first — it's the deep integration and the picker's
|
|
24
|
+
* default. `ids`, when given, restricts the plan to those hosts.
|
|
25
|
+
*/
|
|
26
|
+
export declare function planInit(repo: string, opts?: {
|
|
27
|
+
home?: string;
|
|
28
|
+
ids?: string[];
|
|
29
|
+
}): HostPlan[];
|
|
30
|
+
/** Flatten a plan down to the writes for the selected host ids. */
|
|
31
|
+
export declare function selectedWrites(plan: HostPlan[], ids: string[]): PlannedWrite[];
|
|
32
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/hosts/plan.ts"],"names":[],"mappings":"AAiBA,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf;kEAC8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB;AAqBD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAAG,QAAQ,EAAE,CAoB/F;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,YAAY,EAAE,CAE9E"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `graft init` *would* write, computed before anything is written.
|
|
3
|
+
*
|
|
4
|
+
* The picker and `--dry-run` both need the exact path list up front, so every
|
|
5
|
+
* writer in the init path (instruction files, MCP configs, hooks, the Claude
|
|
6
|
+
* Code layer) exposes a pure `*Targets()` function and then consumes that same
|
|
7
|
+
* list to do its writing. One source of truth, so a plan can never drift from
|
|
8
|
+
* what a real run touches.
|
|
9
|
+
*/
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { statSync } from 'node:fs';
|
|
13
|
+
import { HOSTS, detectHosts } from './registry.js';
|
|
14
|
+
import { mcpTargets } from './mcp-config.js';
|
|
15
|
+
import { hookTargets } from './codex-hooks.js';
|
|
16
|
+
import { claudeTargets } from '../claude/init.js';
|
|
17
|
+
function probeFor(home, repo) {
|
|
18
|
+
return {
|
|
19
|
+
home, repo,
|
|
20
|
+
dirExists: (p) => { try {
|
|
21
|
+
return statSync(p).isDirectory();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
} },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** The instruction-file write for one host, derived from its registry entry. */
|
|
29
|
+
function instructionTarget(repo, host) {
|
|
30
|
+
return {
|
|
31
|
+
hostId: host.id,
|
|
32
|
+
id: host.id,
|
|
33
|
+
path: join(repo, host.relPath),
|
|
34
|
+
scope: 'repo',
|
|
35
|
+
kind: 'instruction',
|
|
36
|
+
what: host.kind === 'owned' ? 'graft-owned file' : 'fenced graft section',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Every host graft can wire, each with the full set of files selecting it would
|
|
41
|
+
* touch. Claude Code comes first — it's the deep integration and the picker's
|
|
42
|
+
* default. `ids`, when given, restricts the plan to those hosts.
|
|
43
|
+
*/
|
|
44
|
+
export function planInit(repo, opts = {}) {
|
|
45
|
+
const home = opts.home ?? homedir();
|
|
46
|
+
const probe = probeFor(home, repo);
|
|
47
|
+
const detected = new Set(detectHosts(probe).map((h) => h.id));
|
|
48
|
+
const plans = [
|
|
49
|
+
{ id: 'claude', name: 'Claude Code', detected: true, writes: claudeTargets(repo) },
|
|
50
|
+
...HOSTS.map((host) => ({
|
|
51
|
+
id: host.id,
|
|
52
|
+
name: host.name,
|
|
53
|
+
detected: detected.has(host.id),
|
|
54
|
+
writes: [
|
|
55
|
+
instructionTarget(repo, host),
|
|
56
|
+
...mcpTargets(repo, [host.id], { home }),
|
|
57
|
+
...(host.id === 'agents' ? hookTargets(home) : []),
|
|
58
|
+
],
|
|
59
|
+
})),
|
|
60
|
+
];
|
|
61
|
+
return opts.ids ? plans.filter((p) => opts.ids.includes(p.id)) : plans;
|
|
62
|
+
}
|
|
63
|
+
/** Flatten a plan down to the writes for the selected host ids. */
|
|
64
|
+
export function selectedWrites(plan, ids) {
|
|
65
|
+
return plan.filter((p) => ids.includes(p.id)).flatMap((p) => p.writes);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/hosts/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/hosts/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/hosts/registry.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,IAAI,MAAM,CAAC;IAClB,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC;CACrC;AAED,eAAO,MAAM,KAAK,EAAE,UAAU,EA4D7B,CAAC;AAEF,wBAAgB,OAAO,IAAI,MAAM,EAAE,CAElC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,EAAE,CAE5D"}
|
package/dist/hosts/registry.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { instructionBody, cursorRule, kiroSteering, windsurfRule } from './instructions.js';
|
|
11
|
+
import { skillTemplate } from '../claude/skill-template.js';
|
|
11
12
|
export const HOSTS = [
|
|
12
13
|
{
|
|
13
14
|
id: 'agents',
|
|
@@ -19,6 +20,14 @@ export const HOSTS = [
|
|
|
19
20
|
p.dirExists(join(p.home, '.config', 'opencode')) ||
|
|
20
21
|
p.dirExists(join(p.home, '.config', 'agents')),
|
|
21
22
|
},
|
|
23
|
+
{
|
|
24
|
+
id: 'adal',
|
|
25
|
+
name: 'AdaL',
|
|
26
|
+
kind: 'owned',
|
|
27
|
+
relPath: join('.adal', 'skills', 'graft', 'SKILL.md'),
|
|
28
|
+
content: skillTemplate,
|
|
29
|
+
detect: (p) => p.dirExists(join(p.home, '.adal')) || p.dirExists(join(p.repo, '.adal')),
|
|
30
|
+
},
|
|
22
31
|
{
|
|
23
32
|
id: 'cursor',
|
|
24
33
|
name: 'Cursor',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hosts/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hosts/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `instructions` string returned in graft's MCP `initialize` response.
|
|
3
|
+
*
|
|
4
|
+
* This is the one piece of graft prose that survives **tool deferral**. When a
|
|
5
|
+
* host has more tools than its schema budget allows, it sends tool *names* only
|
|
6
|
+
* and withholds the JSONSchemas until a `ToolSearch`-style lookup fetches them —
|
|
7
|
+
* measured in a real session: 111 tools deferred, of which graft's six arrived as
|
|
8
|
+
* six bare strings with no descriptions at all. The MCP spec's `instructions`
|
|
9
|
+
* field is delivered on a separate track (Claude Code records it as its own
|
|
10
|
+
* `mcp_instructions_delta` context layer), so it lands whole even then. Other
|
|
11
|
+
* servers already rely on this — `claude-in-chrome` uses it for exactly the
|
|
12
|
+
* batch-your-ToolSearch instruction below; graft used to send nothing.
|
|
13
|
+
*
|
|
14
|
+
* Two jobs, in this order:
|
|
15
|
+
* 1. Defuse the deferral tax. One lookup loads all six tools for the whole
|
|
16
|
+
* session, so the cost is a single round trip, not two calls per use. An
|
|
17
|
+
* agent that doesn't know this can only assume the worse reading.
|
|
18
|
+
* 2. Say what each tool is FOR as a decision rule, not a feature summary —
|
|
19
|
+
* because in a host with no hooks and no skill listing (a plain chat client
|
|
20
|
+
* with an MCP config), this string plus the tool descriptions are the entire
|
|
21
|
+
* steering budget graft gets.
|
|
22
|
+
*
|
|
23
|
+
* Budget: keep this under ~1,000 characters. Observed sibling servers sit at
|
|
24
|
+
* 660–984, and nothing proves a longer one survives un-truncated.
|
|
25
|
+
*/
|
|
26
|
+
/** The `select:` argument that loads every graft tool in one lookup. */
|
|
27
|
+
export declare function toolSearchQuery(prefix?: string): string;
|
|
28
|
+
export declare function mcpInstructions(): string;
|
|
29
|
+
//# sourceMappingURL=instructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/mcp/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAWH,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,MAAM,SAAiB,GAAG,MAAM,CAE/D;AAED,wBAAgB,eAAe,IAAI,MAAM,CAgBxC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `instructions` string returned in graft's MCP `initialize` response.
|
|
3
|
+
*
|
|
4
|
+
* This is the one piece of graft prose that survives **tool deferral**. When a
|
|
5
|
+
* host has more tools than its schema budget allows, it sends tool *names* only
|
|
6
|
+
* and withholds the JSONSchemas until a `ToolSearch`-style lookup fetches them —
|
|
7
|
+
* measured in a real session: 111 tools deferred, of which graft's six arrived as
|
|
8
|
+
* six bare strings with no descriptions at all. The MCP spec's `instructions`
|
|
9
|
+
* field is delivered on a separate track (Claude Code records it as its own
|
|
10
|
+
* `mcp_instructions_delta` context layer), so it lands whole even then. Other
|
|
11
|
+
* servers already rely on this — `claude-in-chrome` uses it for exactly the
|
|
12
|
+
* batch-your-ToolSearch instruction below; graft used to send nothing.
|
|
13
|
+
*
|
|
14
|
+
* Two jobs, in this order:
|
|
15
|
+
* 1. Defuse the deferral tax. One lookup loads all six tools for the whole
|
|
16
|
+
* session, so the cost is a single round trip, not two calls per use. An
|
|
17
|
+
* agent that doesn't know this can only assume the worse reading.
|
|
18
|
+
* 2. Say what each tool is FOR as a decision rule, not a feature summary —
|
|
19
|
+
* because in a host with no hooks and no skill listing (a plain chat client
|
|
20
|
+
* with an MCP config), this string plus the tool descriptions are the entire
|
|
21
|
+
* steering budget graft gets.
|
|
22
|
+
*
|
|
23
|
+
* Budget: keep this under ~1,000 characters. Observed sibling servers sit at
|
|
24
|
+
* 660–984, and nothing proves a longer one survives un-truncated.
|
|
25
|
+
*/
|
|
26
|
+
/** Tool names in the order an agent should reach for them, most-used first. */
|
|
27
|
+
const TOOL_ORDER = [
|
|
28
|
+
'graft_find_code',
|
|
29
|
+
'graft_find_all',
|
|
30
|
+
'graft_trace_calls',
|
|
31
|
+
'graft_file_api',
|
|
32
|
+
'graft_repo_map',
|
|
33
|
+
];
|
|
34
|
+
/** The `select:` argument that loads every graft tool in one lookup. */
|
|
35
|
+
export function toolSearchQuery(prefix = 'mcp__graft__') {
|
|
36
|
+
return `select:${TOOL_ORDER.map((t) => `${prefix}${t}`).join(',')}`;
|
|
37
|
+
}
|
|
38
|
+
export function mcpInstructions() {
|
|
39
|
+
return [
|
|
40
|
+
'This repo is indexed by graft: a prebuilt graph of every symbol, its file:line',
|
|
41
|
+
'span, and who calls what. Prefer these tools over grep/read — one call usually',
|
|
42
|
+
'replaces several file reads.',
|
|
43
|
+
'',
|
|
44
|
+
`**If these tools are deferred (names shown, schemas withheld), load them all in ONE lookup:** ToolSearch "${toolSearchQuery()}" — one round trip for the whole session. Never load them one at a time.`,
|
|
45
|
+
'',
|
|
46
|
+
'- graft_find_code — "how does X work" / "where is Y": ranked hits, code inlined.',
|
|
47
|
+
'- graft_find_all — when you need EVERY occurrence; find_code is top-N and misses some.',
|
|
48
|
+
'- graft_trace_calls — who calls it, what it calls, blast radius before a rename.',
|
|
49
|
+
'- graft_file_api — a file\'s whole API in ~200 tokens.',
|
|
50
|
+
'- graft_repo_map — orientation in an unfamiliar repo.',
|
|
51
|
+
'',
|
|
52
|
+
'Results already reflect uncommitted edits — the graph refreshes before each query.',
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/mcp/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,+EAA+E;AAC/E,MAAM,UAAU,GAAG;IACjB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,gBAAgB;CACR,CAAC;AAEX,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,MAAM,GAAG,cAAc;IACrD,OAAO,UAAU,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO;QACL,gFAAgF;QAChF,gFAAgF;QAChF,8BAA8B;QAC9B,EAAE;QACF,6GAA6G,eAAe,EAAE,0EAA0E;QACxM,EAAE;QACF,kFAAkF;QAClF,wFAAwF;QACxF,kFAAkF;QAClF,wDAAwD;QACxD,uDAAuD;QACvD,EAAE;QACF,oFAAoF;KACrF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `version` is threaded in from the CLI rather than read here: `readCurrentVersion`
|
|
3
|
+
* resolves package.json relative to the calling module, and from `dist/mcp/` that
|
|
4
|
+
* lookup misses. The caller already knows it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function startMcpServer(root: string, dirOverride?: string, version?: string): void;
|
|
2
7
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,SAAM,GAAG,IAAI,CAsDtF"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
6
|
import { TOOLS, callTool } from './tools.js';
|
|
7
|
+
import { mcpInstructions } from './instructions.js';
|
|
7
8
|
function send(msg) {
|
|
8
9
|
process.stdout.write(`${JSON.stringify(msg)}\n`);
|
|
9
10
|
}
|
|
@@ -13,7 +14,12 @@ function reply(id, result) {
|
|
|
13
14
|
function replyError(id, code, message) {
|
|
14
15
|
send({ jsonrpc: '2.0', id, error: { code, message } });
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* `version` is threaded in from the CLI rather than read here: `readCurrentVersion`
|
|
19
|
+
* resolves package.json relative to the calling module, and from `dist/mcp/` that
|
|
20
|
+
* lookup misses. The caller already knows it.
|
|
21
|
+
*/
|
|
22
|
+
export function startMcpServer(root, dirOverride, version = '0') {
|
|
17
23
|
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
18
24
|
rl.on('line', (line) => {
|
|
19
25
|
const text = line.trim();
|
|
@@ -34,7 +40,9 @@ export function startMcpServer(root, dirOverride) {
|
|
|
34
40
|
reply(id, {
|
|
35
41
|
protocolVersion: params?.protocolVersion ?? '2024-11-05',
|
|
36
42
|
capabilities: { tools: {} },
|
|
37
|
-
serverInfo: { name: 'graft', version
|
|
43
|
+
serverInfo: { name: 'graft', version },
|
|
44
|
+
// The one channel that survives tool deferral — see ./instructions.ts.
|
|
45
|
+
instructions: mcpInstructions(),
|
|
38
46
|
});
|
|
39
47
|
return;
|
|
40
48
|
case 'notifications/initialized':
|
|
@@ -54,8 +62,11 @@ export function startMcpServer(root, dirOverride) {
|
|
|
54
62
|
return;
|
|
55
63
|
const name = String(params?.name ?? '');
|
|
56
64
|
const args = (params?.arguments ?? {});
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
// Async because a tool call may rebuild the graph first (see
|
|
66
|
+
// graph/refresh.ts). `callTool` absorbs its own errors, so the only thing
|
|
67
|
+
// that can reject here is a bug — surface it as a JSON-RPC error rather
|
|
68
|
+
// than an unhandled rejection that kills the server.
|
|
69
|
+
callTool(root, name, args, dirOverride).then((r) => reply(id, { content: [{ type: 'text', text: r.text }], isError: r.isError }), (err) => replyError(id, -32603, err instanceof Error ? err.message : String(err)));
|
|
59
70
|
return;
|
|
60
71
|
}
|
|
61
72
|
default:
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -4,8 +4,10 @@ export interface ToolDef {
|
|
|
4
4
|
inputSchema: object;
|
|
5
5
|
}
|
|
6
6
|
export declare const TOOLS: ToolDef[];
|
|
7
|
-
|
|
7
|
+
/** Canonical name for a requested tool: itself, or what it was renamed to. */
|
|
8
|
+
export declare function canonicalToolName(name: string): string;
|
|
9
|
+
export declare function callTool(root: string, requestedName: string, args: Record<string, unknown>, dirOverride?: string): Promise<{
|
|
8
10
|
text: string;
|
|
9
11
|
isError: boolean;
|
|
10
|
-
}
|
|
12
|
+
}>;
|
|
11
13
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/mcp/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD,eAAO,MAAM,KAAK,EAAE,OAAO,EAoF1B,CAAC;AAmGF,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAoB7C"}
|