@nanonets/graft 0.8.0 → 0.8.1

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.
Files changed (84) hide show
  1. package/README.md +38 -8
  2. package/dist/claude/hooks.d.ts +15 -0
  3. package/dist/claude/hooks.d.ts.map +1 -1
  4. package/dist/claude/hooks.js +52 -3
  5. package/dist/claude/hooks.js.map +1 -1
  6. package/dist/claude/init.d.ts +17 -0
  7. package/dist/claude/init.d.ts.map +1 -1
  8. package/dist/claude/init.js +45 -19
  9. package/dist/claude/init.js.map +1 -1
  10. package/dist/claude/settings-merge.d.ts.map +1 -1
  11. package/dist/claude/settings-merge.js +9 -1
  12. package/dist/claude/settings-merge.js.map +1 -1
  13. package/dist/claude/skill-template.d.ts.map +1 -1
  14. package/dist/claude/skill-template.js +11 -3
  15. package/dist/claude/skill-template.js.map +1 -1
  16. package/dist/claude/state.d.ts +2 -20
  17. package/dist/claude/state.d.ts.map +1 -1
  18. package/dist/claude/state.js +9 -72
  19. package/dist/claude/state.js.map +1 -1
  20. package/dist/cli-picker.d.ts +60 -0
  21. package/dist/cli-picker.d.ts.map +1 -0
  22. package/dist/cli-picker.js +268 -0
  23. package/dist/cli-picker.js.map +1 -0
  24. package/dist/cli.js +99 -10
  25. package/dist/cli.js.map +1 -1
  26. package/dist/engine.d.ts +2 -0
  27. package/dist/engine.d.ts.map +1 -1
  28. package/dist/engine.js +1 -0
  29. package/dist/engine.js.map +1 -1
  30. package/dist/graph/build.d.ts +13 -2
  31. package/dist/graph/build.d.ts.map +1 -1
  32. package/dist/graph/build.js +93 -19
  33. package/dist/graph/build.js.map +1 -1
  34. package/dist/graph/extract-cache.d.ts +96 -0
  35. package/dist/graph/extract-cache.d.ts.map +1 -0
  36. package/dist/graph/extract-cache.js +199 -0
  37. package/dist/graph/extract-cache.js.map +1 -0
  38. package/dist/graph/fingerprint.d.ts +70 -0
  39. package/dist/graph/fingerprint.d.ts.map +1 -0
  40. package/dist/graph/fingerprint.js +147 -0
  41. package/dist/graph/fingerprint.js.map +1 -0
  42. package/dist/graph/load.d.ts +11 -0
  43. package/dist/graph/load.d.ts.map +1 -1
  44. package/dist/graph/load.js +14 -0
  45. package/dist/graph/load.js.map +1 -1
  46. package/dist/graph/refresh.d.ts +49 -0
  47. package/dist/graph/refresh.d.ts.map +1 -0
  48. package/dist/graph/refresh.js +202 -0
  49. package/dist/graph/refresh.js.map +1 -0
  50. package/dist/graph/source-files.d.ts +18 -0
  51. package/dist/graph/source-files.d.ts.map +1 -0
  52. package/dist/graph/source-files.js +36 -0
  53. package/dist/graph/source-files.js.map +1 -0
  54. package/dist/hosts/codex-hooks.d.ts +8 -0
  55. package/dist/hosts/codex-hooks.d.ts.map +1 -1
  56. package/dist/hosts/codex-hooks.js +27 -4
  57. package/dist/hosts/codex-hooks.js.map +1 -1
  58. package/dist/hosts/init.d.ts +2 -0
  59. package/dist/hosts/init.d.ts.map +1 -1
  60. package/dist/hosts/init.js +5 -2
  61. package/dist/hosts/init.js.map +1 -1
  62. package/dist/hosts/mcp-config.d.ts +18 -0
  63. package/dist/hosts/mcp-config.d.ts.map +1 -1
  64. package/dist/hosts/mcp-config.js +31 -7
  65. package/dist/hosts/mcp-config.js.map +1 -1
  66. package/dist/hosts/plan.d.ts +32 -0
  67. package/dist/hosts/plan.d.ts.map +1 -0
  68. package/dist/hosts/plan.js +67 -0
  69. package/dist/hosts/plan.js.map +1 -0
  70. package/dist/hosts/registry.d.ts.map +1 -1
  71. package/dist/hosts/registry.js +9 -0
  72. package/dist/hosts/registry.js.map +1 -1
  73. package/dist/mcp/server.d.ts.map +1 -1
  74. package/dist/mcp/server.js +5 -2
  75. package/dist/mcp/server.js.map +1 -1
  76. package/dist/mcp/tools.d.ts +2 -2
  77. package/dist/mcp/tools.d.ts.map +1 -1
  78. package/dist/mcp/tools.js +106 -90
  79. package/dist/mcp/tools.js.map +1 -1
  80. package/dist/util/state.d.ts +45 -0
  81. package/dist/util/state.d.ts.map +1 -0
  82. package/dist/util/state.js +121 -0
  83. package/dist/util/state.js.map +1 -0
  84. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/claude/state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,eAAe,EACf,UAAU,GACX,MAAM,kBAAkB,CAAC;AAe1B,SAAS,YAAY;IACnB,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;AACrH,CAAC;AAED,SAAS,WAAW,CAAC,CAAS,EAAE,EAAU,IAAY,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAE1G,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,EAAU;IAC/C,OAAO,QAAQ,CAAe,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC;AACtE,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,EAAU,EAAE,CAAe;IACjE,eAAe,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { HostPlan, PlannedWrite } from './hosts/plan.js';
2
+ /** `/Users/me/.codex/x` → `~/.codex/x`, for display only. */
3
+ export declare function tilde(path: string, home: string): string;
4
+ /**
5
+ * One-line summary of what selecting a host writes: repo-relative paths, then
6
+ * a count for anything landing outside the repo — those are the writes users
7
+ * can't see in `git status`, so they get named explicitly.
8
+ */
9
+ export declare function describeWrites(writes: PlannedWrite[], repo: string, home: string, maxShown?: number): string;
10
+ export interface PickerRow {
11
+ id: string;
12
+ detected: boolean;
13
+ summary: string;
14
+ /** True when selecting this host writes outside the repo. */
15
+ hasGlobal: boolean;
16
+ }
17
+ export interface PickerState {
18
+ rows: PickerRow[];
19
+ cursor: number;
20
+ checked: ReadonlySet<string>;
21
+ done: boolean;
22
+ aborted: boolean;
23
+ }
24
+ /** Claude Code starts checked — it's the deep integration — everything else off. */
25
+ export declare function initialPickerState(plan: HostPlan[], repo: string, home: string): PickerState;
26
+ export type PickerKey = 'up' | 'down' | 'space' | 'all' | 'enter' | 'abort';
27
+ export declare const KEY_UP = "\u001B[A";
28
+ export declare const KEY_DOWN = "\u001B[B";
29
+ export declare const KEY_ESC = "\u001B";
30
+ export declare const KEY_CTRL_C = "\u0003";
31
+ /** A single keypress → the key we act on, or null to ignore. */
32
+ export declare function keyOf(chunk: string): PickerKey | null;
33
+ /**
34
+ * Split a raw stdin chunk into keypresses. A terminal is free to batch bytes —
35
+ * held keys, pasted input, or a pty replaying a script all arrive as one chunk —
36
+ * so a chunk is a sequence, never a single key. CSI sequences (ESC '[' final)
37
+ * are consumed whole so an arrow key never reads as a bare ESC (i.e. abort).
38
+ */
39
+ export declare function keysOf(chunk: string): PickerKey[];
40
+ export declare function reducePicker(state: PickerState, key: PickerKey): PickerState;
41
+ export declare function renderPicker(state: PickerState, tty?: boolean): string;
42
+ /** Ids in plan order, so output ordering never depends on toggle order. */
43
+ export declare function pickedIds(state: PickerState): string[];
44
+ /**
45
+ * Drive the picker on a real terminal. Resolves the chosen ids, or null if the
46
+ * user cancelled — in which case the caller must write nothing.
47
+ */
48
+ export declare function runPicker(plan: HostPlan[], repo: string, home: string): Promise<string[] | null>;
49
+ /**
50
+ * Shown when there's no TTY to prompt on and no flag saying what to wire.
51
+ * Writing nothing is deliberate: a scripted run shouldn't get files for every
52
+ * agent the machine happens to have installed.
53
+ */
54
+ export declare function formatNonInteractiveHelp(detectedIds: string[]): string;
55
+ /**
56
+ * `--dry-run` output: every path init would touch, repo writes first, then a
57
+ * separate section for anything outside the repo.
58
+ */
59
+ export declare function formatPlan(plan: HostPlan[], ids: string[], repo: string, home: string, tty?: boolean): string;
60
+ //# sourceMappingURL=cli-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-picker.d.ts","sourceRoot":"","sources":["../src/cli-picker.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAM9D,6DAA6D;AAC7D,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAWD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EAAE,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAI,GACX,MAAM,CAiBR;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAa5F;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5E,eAAO,MAAM,MAAM,aAAW,CAAC;AAC/B,eAAO,MAAM,QAAQ,aAAW,CAAC;AACjC,eAAO,MAAM,OAAO,WAAS,CAAC;AAC9B,eAAO,MAAM,UAAU,WAAS,CAAC;AAEjC,gEAAgE;AAChE,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAUrD;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAkBjD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,GAAG,WAAW,CAsB5E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,UAAO,GAAG,MAAM,CAqBnE;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,CAEtD;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,QAAQ,EAAE,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAiD1B;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CAmBtE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,QAAQ,EAAE,EAChB,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,UAAgC,GAClC,MAAM,CA4BR"}
@@ -0,0 +1,268 @@
1
+ /**
2
+ * `graft init`'s agent picker and `--dry-run` plan printer.
3
+ *
4
+ * Split three ways so the logic is testable without a TTY: `renderPicker` and
5
+ * `reducePicker` are pure, `runPicker` is the only part that touches stdin.
6
+ */
7
+ import { relative, dirname, sep } from 'node:path';
8
+ const indigo = (s) => `\x1b[38;2;84;111;255m${s}\x1b[0m`;
9
+ const muted = (s) => `\x1b[38;5;244m${s}\x1b[0m`;
10
+ const amber = (s) => `\x1b[38;5;179m${s}\x1b[0m`;
11
+ /** `/Users/me/.codex/x` → `~/.codex/x`, for display only. */
12
+ export function tilde(path, home) {
13
+ return path === home || path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;
14
+ }
15
+ /** Deepest directory containing every given path. */
16
+ function commonDir(paths) {
17
+ const split = paths.map((p) => dirname(p).split(sep));
18
+ const first = split[0] ?? [];
19
+ let i = 0;
20
+ while (i < first.length && split.every((s) => s[i] === first[i]))
21
+ i++;
22
+ return first.slice(0, i).join(sep);
23
+ }
24
+ /**
25
+ * One-line summary of what selecting a host writes: repo-relative paths, then
26
+ * a count for anything landing outside the repo — those are the writes users
27
+ * can't see in `git status`, so they get named explicitly.
28
+ */
29
+ export function describeWrites(writes, repo, home, maxShown = 3) {
30
+ const repoPaths = writes.filter((w) => w.scope === 'repo').map((w) => relative(repo, w.path));
31
+ const globals = writes.filter((w) => w.scope === 'global');
32
+ const parts = [];
33
+ if (repoPaths.length > 0) {
34
+ parts.push(repoPaths.length <= maxShown
35
+ ? repoPaths.join(', ')
36
+ : `${repoPaths.slice(0, maxShown).join(', ')} +${repoPaths.length - maxShown} more`);
37
+ }
38
+ if (globals.length > 0) {
39
+ const where = tilde(commonDir(globals.map((w) => w.path)), home);
40
+ parts.push(`+ ${globals.length} in ${where}/ (machine-wide)`);
41
+ }
42
+ if (!writes.some((w) => w.kind === 'mcp'))
43
+ parts.push('no MCP');
44
+ return parts.join(' · ');
45
+ }
46
+ /** Claude Code starts checked — it's the deep integration — everything else off. */
47
+ export function initialPickerState(plan, repo, home) {
48
+ return {
49
+ rows: plan.map((p) => ({
50
+ id: p.id,
51
+ detected: p.detected,
52
+ summary: describeWrites(p.writes, repo, home),
53
+ hasGlobal: p.writes.some((w) => w.scope === 'global'),
54
+ })),
55
+ cursor: 0,
56
+ checked: new Set(plan.some((p) => p.id === 'claude') ? ['claude'] : []),
57
+ done: false,
58
+ aborted: false,
59
+ };
60
+ }
61
+ export const KEY_UP = '\x1b[A';
62
+ export const KEY_DOWN = '\x1b[B';
63
+ export const KEY_ESC = '\x1b';
64
+ export const KEY_CTRL_C = '\x03';
65
+ /** A single keypress → the key we act on, or null to ignore. */
66
+ export function keyOf(chunk) {
67
+ switch (chunk) {
68
+ case KEY_UP:
69
+ case 'k': return 'up';
70
+ case KEY_DOWN:
71
+ case 'j': return 'down';
72
+ case ' ': return 'space';
73
+ case 'a': return 'all';
74
+ case '\r':
75
+ case '\n': return 'enter';
76
+ case KEY_ESC:
77
+ case KEY_CTRL_C:
78
+ case 'q': return 'abort';
79
+ default: return null;
80
+ }
81
+ }
82
+ /**
83
+ * Split a raw stdin chunk into keypresses. A terminal is free to batch bytes —
84
+ * held keys, pasted input, or a pty replaying a script all arrive as one chunk —
85
+ * so a chunk is a sequence, never a single key. CSI sequences (ESC '[' final)
86
+ * are consumed whole so an arrow key never reads as a bare ESC (i.e. abort).
87
+ */
88
+ export function keysOf(chunk) {
89
+ const keys = [];
90
+ let i = 0;
91
+ while (i < chunk.length) {
92
+ let token;
93
+ if (chunk[i] === KEY_ESC && chunk[i + 1] === '[') {
94
+ // ESC '[' then optional parameter bytes, then one final byte @-~.
95
+ let j = i + 2;
96
+ while (j < chunk.length && !/[@-~]/.test(chunk[j]))
97
+ j++;
98
+ token = chunk.slice(i, Math.min(j + 1, chunk.length));
99
+ }
100
+ else {
101
+ token = chunk[i];
102
+ }
103
+ i += token.length;
104
+ const key = keyOf(token);
105
+ if (key !== null)
106
+ keys.push(key);
107
+ }
108
+ return keys;
109
+ }
110
+ export function reducePicker(state, key) {
111
+ const n = state.rows.length;
112
+ switch (key) {
113
+ case 'up':
114
+ return { ...state, cursor: (state.cursor - 1 + n) % n };
115
+ case 'down':
116
+ return { ...state, cursor: (state.cursor + 1) % n };
117
+ case 'space': {
118
+ const next = new Set(state.checked);
119
+ const id = state.rows[state.cursor].id;
120
+ if (next.has(id))
121
+ next.delete(id);
122
+ else
123
+ next.add(id);
124
+ return { ...state, checked: next };
125
+ }
126
+ case 'all': {
127
+ const allOn = state.rows.every((r) => state.checked.has(r.id));
128
+ return { ...state, checked: new Set(allOn ? [] : state.rows.map((r) => r.id)) };
129
+ }
130
+ case 'enter':
131
+ return { ...state, done: true };
132
+ case 'abort':
133
+ return { ...state, aborted: true };
134
+ }
135
+ }
136
+ export function renderPicker(state, tty = true) {
137
+ const dim = tty ? muted : (s) => s;
138
+ const hot = tty ? indigo : (s) => s;
139
+ const warn = tty ? amber : (s) => s;
140
+ // Pad on the plain label so the summary column lines up regardless of which
141
+ // rows carry the '(not detected)' tag or the cursor's colour codes.
142
+ const label = (r) => `${r.id}${r.detected ? '' : ' (not detected)'}`;
143
+ const width = Math.max(...state.rows.map((r) => label(r).length));
144
+ const lines = ['graft init — select what to wire into this repo:', ''];
145
+ for (const [i, row] of state.rows.entries()) {
146
+ const here = i === state.cursor;
147
+ const box = state.checked.has(row.id) ? '[x]' : '[ ]';
148
+ const gap = ' '.repeat(width - label(row).length);
149
+ const name = here ? hot(row.id) : row.id;
150
+ const tag = row.detected ? '' : dim(' (not detected)');
151
+ const summary = row.hasGlobal ? warn(row.summary) : dim(row.summary);
152
+ lines.push(`${here ? '›' : ' '} ${box} ${name}${tag}${gap} ${summary}`);
153
+ }
154
+ lines.push('', dim('↑↓ move · space toggle · a all · enter confirm · esc cancel'));
155
+ return lines.join('\n');
156
+ }
157
+ /** Ids in plan order, so output ordering never depends on toggle order. */
158
+ export function pickedIds(state) {
159
+ return state.rows.filter((r) => state.checked.has(r.id)).map((r) => r.id);
160
+ }
161
+ /**
162
+ * Drive the picker on a real terminal. Resolves the chosen ids, or null if the
163
+ * user cancelled — in which case the caller must write nothing.
164
+ */
165
+ export async function runPicker(plan, repo, home) {
166
+ let state = initialPickerState(plan, repo, home);
167
+ const out = process.stderr;
168
+ const stdin = process.stdin;
169
+ let lastLines = 0;
170
+ const draw = () => {
171
+ if (lastLines > 0)
172
+ out.write(`\x1b[${lastLines}A\x1b[0J`);
173
+ const text = renderPicker(state);
174
+ out.write(`${text}\n`);
175
+ lastLines = text.split('\n').length;
176
+ };
177
+ const wasRaw = Boolean(stdin.isRaw);
178
+ stdin.setRawMode?.(true);
179
+ stdin.resume();
180
+ draw();
181
+ try {
182
+ return await new Promise((resolve) => {
183
+ // Both listeners come off together: a leftover 'end' would otherwise fire
184
+ // after a confirmed pick and redraw the picker over init's own output.
185
+ const finish = (onData) => {
186
+ stdin.off('data', onData);
187
+ stdin.off('end', onEnd);
188
+ draw();
189
+ resolve(state.aborted ? null : pickedIds(state));
190
+ };
191
+ const onData = (buf) => {
192
+ const keys = keysOf(buf.toString('utf8'));
193
+ if (keys.length === 0)
194
+ return;
195
+ for (const key of keys) {
196
+ state = reducePicker(state, key);
197
+ if (state.done || state.aborted) {
198
+ finish(onData);
199
+ return;
200
+ }
201
+ }
202
+ draw();
203
+ };
204
+ // A closed stdin (piped input that ran out) must not hang the prompt.
205
+ const onEnd = () => { state = { ...state, aborted: true }; finish(onData); };
206
+ stdin.on('data', onData);
207
+ stdin.on('end', onEnd);
208
+ });
209
+ }
210
+ finally {
211
+ stdin.setRawMode?.(wasRaw);
212
+ stdin.pause();
213
+ }
214
+ }
215
+ /**
216
+ * Shown when there's no TTY to prompt on and no flag saying what to wire.
217
+ * Writing nothing is deliberate: a scripted run shouldn't get files for every
218
+ * agent the machine happens to have installed.
219
+ */
220
+ export function formatNonInteractiveHelp(detectedIds) {
221
+ const list = detectedIds.length > 0 ? detectedIds.join(", ") : "none";
222
+ const examples = [
223
+ ...(detectedIds.length > 0
224
+ ? [
225
+ [`graft init --agents ${detectedIds.join(" ")}`, "wire these"],
226
+ ["graft init --yes", "same, without spelling them out"],
227
+ ]
228
+ : []),
229
+ ["graft init --agents claude", "Claude Code only"],
230
+ ["graft init --dry-run", "list every file first"],
231
+ ];
232
+ const width = Math.max(...examples.map(([cmd]) => cmd.length));
233
+ return [
234
+ "graft init: no TTY to prompt on, and no --agents/--yes given — nothing written.",
235
+ `detected: ${list}`,
236
+ "",
237
+ ...examples.map(([cmd, note]) => ` ${cmd.padEnd(width)} # ${note}`),
238
+ ].join("\n");
239
+ }
240
+ /**
241
+ * `--dry-run` output: every path init would touch, repo writes first, then a
242
+ * separate section for anything outside the repo.
243
+ */
244
+ export function formatPlan(plan, ids, repo, home, tty = Boolean(process.stderr.isTTY)) {
245
+ const dim = tty ? muted : (s) => s;
246
+ const warn = tty ? amber : (s) => s;
247
+ const writes = plan.filter((p) => ids.includes(p.id)).flatMap((p) => p.writes);
248
+ if (writes.length === 0)
249
+ return 'would write — nothing (no agents selected)';
250
+ const pad = (rows, f) => {
251
+ const w = Math.max(...rows.map((r) => f(r.path).length));
252
+ return rows.map((r) => ` ${f(r.path).padEnd(w)} ${dim(r.what)}`);
253
+ };
254
+ const lines = [];
255
+ const repoWrites = writes.filter((w) => w.scope === 'repo');
256
+ if (repoWrites.length > 0) {
257
+ lines.push('would write — this repo:', ...pad(repoWrites, (p) => relative(repo, p)));
258
+ }
259
+ const globalWrites = writes.filter((w) => w.scope === 'global');
260
+ if (globalWrites.length > 0) {
261
+ if (lines.length)
262
+ lines.push('');
263
+ lines.push(warn('would write — your machine, affects ALL repos:'), ...pad(globalWrites, (p) => tilde(p, home)), '', dim('suppress the out-of-repo writes with --no-global'));
264
+ }
265
+ lines.push('', dim('nothing was written (--dry-run)'));
266
+ return lines.join('\n');
267
+ }
268
+ //# sourceMappingURL=cli-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-picker.js","sourceRoot":"","sources":["../src/cli-picker.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -12,14 +12,18 @@ import { Graft } from "./engine.js";
12
12
  import { resolveConfig } from "./ai/providers.js";
13
13
  import { formatCheckReport } from "./context/check.js";
14
14
  import { formatGraphCheckReport } from "./graph/check.js";
15
- import { runInit } from "./claude/init.js";
15
+ import { buildGraphIfMissing, runInit } from "./claude/init.js";
16
16
  import { runHostsInit } from "./hosts/init.js";
17
17
  import { hostIds } from "./hosts/registry.js";
18
18
  import { contextDirFor } from "./context/node-file.js";
19
19
  import { loadGraphCached } from "./graph/load.js";
20
+ import { ensureFreshChildren, ensureFreshGraph, refreshNote } from "./graph/refresh.js";
20
21
  import { isWorkspaceBuildRoot, readWorkspace } from "./graph/workspace.js";
21
22
  import { runWorkspaceAsk, runWorkspaceBuild, runWorkspaceCallers, runWorkspaceCheck, runWorkspaceGrep, runWorkspaceMap, } from "./graph/workspace-cli.js";
22
23
  import { formatInitEpilogue } from "./cli-epilogue.js";
24
+ import { planInit, selectedWrites } from "./hosts/plan.js";
25
+ import { formatNonInteractiveHelp, formatPlan, runPicker } from "./cli-picker.js";
26
+ import { homedir } from "node:os";
23
27
  import { formatUpgradeReport, formatVersionReport, getNpmViewVersion, readCurrentVersion, runUpgrade } from "./cli-meta.js";
24
28
  const program = new Command();
25
29
  const currentVersion = readCurrentVersion(import.meta.url);
@@ -44,6 +48,27 @@ function cliConfig() {
44
48
  };
45
49
  }
46
50
  const engineFrom = () => new Graft(cliConfig());
51
+ /**
52
+ * Bring the graph up to date with the working tree before a query answers from it
53
+ * — the same gate the MCP tools run (see `graph/refresh.ts`). Cheap when nothing
54
+ * moved; a structural, $0 rebuild when it did. The note goes to stderr so `--json`
55
+ * stdout stays machine-readable.
56
+ */
57
+ async function refreshBefore(dir, opts) {
58
+ const globalDir = program.opts().dir;
59
+ const root = resolve(dir);
60
+ const disabled = opts.refresh === false;
61
+ const ws = readWorkspace(root, globalDir);
62
+ const r = ws
63
+ ? await ensureFreshChildren(root, ws.children, { contextDir: globalDir, disabled })
64
+ : await ensureFreshGraph(root, { contextDir: globalDir, disabled });
65
+ const note = refreshNote(r);
66
+ if (note)
67
+ console.error(note);
68
+ }
69
+ /** Attached to every query command: `--no-refresh` answers from the graph exactly
70
+ * as it is on disk, no rebuild. */
71
+ const NO_REFRESH_FLAG = ["--no-refresh", "skip the freshness check — answer from the graph as-is"];
47
72
  program
48
73
  .command("version")
49
74
  .description("Print the installed version and the latest published on npm")
@@ -68,6 +93,7 @@ program
68
93
  .option("--deep", "run the LLM pass: concept nodes (graft/*.md) + per-symbol summary/crux")
69
94
  .option("-e, --extensions <exts...>", 'code extensions to include (e.g. ".ts" ".py")')
70
95
  .option("-j, --concurrency <n>", "files summarized in parallel during --deep (default 5)")
96
+ .option("--no-reuse", "re-parse every file instead of replaying unchanged ones from the extraction cache")
71
97
  .action(async (dir, opts) => {
72
98
  const concurrency = opts.concurrency ? Math.max(1, Number(opts.concurrency)) : undefined;
73
99
  if (opts.concurrency && !Number.isFinite(concurrency)) {
@@ -119,10 +145,12 @@ program
119
145
  const g = await engine.graph(dir, {
120
146
  llm: deep,
121
147
  concurrency,
148
+ reuse: opts.reuse,
122
149
  onProgress: ({ phase, index, total, file }) => process.stderr.write(`\r${phase === "enrich" ? "summarizing" : "parsing"} ${index + 1}/${total}: ${file.slice(0, 50).padEnd(50)}`),
123
150
  });
124
151
  process.stderr.write("\n");
125
152
  console.log(`✓ wiring: ${g.nodes} nodes (${fmt(g.byKind)}), ${g.edges} edges, ${g.cards} cards [${g.languages.join(", ")}]`);
153
+ console.log(` parsed: ${g.parsed} of ${g.files} files (${g.reused} replayed from cache)`);
126
154
  if (deep) {
127
155
  const m = g.meaning;
128
156
  console.log(` meaning: ${m.computed} computed, ${m.cached} cached, ${m.stale} stale, ${m.pending} pending`);
@@ -143,7 +171,9 @@ program
143
171
  .option("--full", "with --source: inline whole definition spans instead of the default ≤8-line crux excerpts")
144
172
  .option("--in <path>", "narrow to nodes under this path prefix, filtered before scoring (segment-aware, like scopeOf)")
145
173
  .option("--json", "output the result as JSON")
174
+ .option(...NO_REFRESH_FLAG)
146
175
  .action(async (query, dir, opts) => {
176
+ await refreshBefore(dir, opts);
147
177
  const askGlobalDir = program.opts().dir;
148
178
  if (readWorkspace(resolve(dir), askGlobalDir)) {
149
179
  runWorkspaceAsk(resolve(dir), askGlobalDir, query, {
@@ -175,7 +205,9 @@ program
175
205
  .argument("<file>", "repo-relative path (or unique basename) of the file")
176
206
  .argument("[dir]", "repository root", ".")
177
207
  .option("--json", "output the result as JSON")
208
+ .option(...NO_REFRESH_FLAG)
178
209
  .action(async (file, dir, opts) => {
210
+ await refreshBefore(dir, opts);
179
211
  const { skeleton, formatSkeleton } = await import("./ask/ask.js");
180
212
  const globalOpts = program.opts();
181
213
  const r = skeleton(dir, file, { contextDir: globalOpts.dir });
@@ -275,7 +307,9 @@ program
275
307
  .option("-d, --depth <n>", 'walk transitively up to N hops for blast radius, or "all" for the full connected closure (default 1)')
276
308
  .option("--in <path>", "narrow matches to nodes whose path contains this substring")
277
309
  .option("--json", "output as JSON")
310
+ .option(...NO_REFRESH_FLAG)
278
311
  .action(async (symbol, dir, opts) => {
312
+ await refreshBefore(dir, opts);
279
313
  const globalOpts = program.opts();
280
314
  if (!opts.json && readWorkspace(resolve(dir), globalOpts.dir)) {
281
315
  runWorkspaceCallers(resolve(dir), globalOpts.dir, symbol, {
@@ -305,7 +339,9 @@ program
305
339
  .option("--fixed", "treat pattern as a literal string, not a regex")
306
340
  .option("--in <path>", "narrow to files whose path contains this substring")
307
341
  .option("--json", "output as JSON")
342
+ .option(...NO_REFRESH_FLAG)
308
343
  .action(async (pattern, dir, opts) => {
344
+ await refreshBefore(dir, opts);
309
345
  const globalOpts = program.opts();
310
346
  if (readWorkspace(resolve(dir), globalOpts.dir)) {
311
347
  runWorkspaceGrep(resolve(dir), globalOpts.dir, pattern, {
@@ -328,6 +364,7 @@ program
328
364
  .argument("[dir]", "repository root", ".")
329
365
  .option("--max-dirs <n>", "max directory entries shown, rest counted into dropped (default 16)")
330
366
  .option("--json", "output as JSON")
367
+ .option(...NO_REFRESH_FLAG)
331
368
  .action(async (dir, opts) => {
332
369
  const root = resolve(dir);
333
370
  const globalOpts = program.opts();
@@ -341,6 +378,7 @@ program
341
378
  }
342
379
  maxDirsW = n;
343
380
  }
381
+ await refreshBefore(dir, opts); // after arg validation: a bad flag shouldn't cost a rebuild
344
382
  if (!opts.json && readWorkspace(root, globalOpts.dir)) {
345
383
  runWorkspaceMap(root, globalOpts.dir, { maxDirs: maxDirsW });
346
384
  return;
@@ -371,7 +409,10 @@ program
371
409
  .option("--list-agents", "list known agent ids and exit")
372
410
  .option("--no-mcp", "skip MCP server registration for other agents")
373
411
  .option("--no-hooks", "skip hook installation for other agents")
374
- .action((dir, opts) => {
412
+ .option("--dry-run", "print every file init would touch, then exit without writing")
413
+ .option("-y, --yes", "skip the picker and wire every detected agent (the pre-0.8 default)")
414
+ .option("--no-global", "skip writes outside this repo (the ~/.codex/ config + hooks)")
415
+ .action(async (dir, opts) => {
375
416
  if (opts.listAgents) {
376
417
  for (const id of [...hostIds(), "claude"])
377
418
  console.log(id);
@@ -387,9 +428,46 @@ program
387
428
  process.exit(1);
388
429
  }
389
430
  }
390
- const wantClaude = !explicit || explicit.includes("claude");
431
+ // Which agents to wire, decided before anything is written. Explicit flags
432
+ // win; otherwise prompt on a TTY, and on a pipe write nothing rather than
433
+ // guessing (pre-0.8 this silently wired every agent the machine had ever
434
+ // installed — see --yes to get that back).
435
+ const home = homedir();
436
+ const plan = planInit(repo, { home });
437
+ const detectedIds = plan.filter((p) => p.detected).map((p) => p.id);
438
+ const noAgents = opts.agents === false;
439
+ let ids;
440
+ if (explicit)
441
+ ids = explicit;
442
+ else if (opts.allAgents)
443
+ ids = plan.map((p) => p.id);
444
+ else if (noAgents)
445
+ ids = ["claude"];
446
+ else if (opts.yes || opts.dryRun)
447
+ ids = detectedIds;
448
+ else if (process.stdin.isTTY && process.stderr.isTTY) {
449
+ const picked = await runPicker(plan, repo, home);
450
+ if (picked === null) {
451
+ console.error("· cancelled — nothing written");
452
+ return;
453
+ }
454
+ ids = picked;
455
+ }
456
+ else {
457
+ console.error(formatNonInteractiveHelp(detectedIds));
458
+ return;
459
+ }
460
+ if (opts.dryRun) {
461
+ console.error(formatPlan(plan, ids, repo, home));
462
+ return;
463
+ }
464
+ if (ids.length === 0) {
465
+ console.error("· no agents selected — nothing written");
466
+ return;
467
+ }
468
+ const wantClaude = ids.includes("claude");
469
+ const cliPath = fileURLToPath(import.meta.url);
391
470
  if (wantClaude) {
392
- const cliPath = fileURLToPath(import.meta.url);
393
471
  const res = runInit(repo, { build: opts.build, cliPath });
394
472
  console.error(`✓ wrote ${res.settingsPath}`);
395
473
  for (const s of res.shims)
@@ -405,22 +483,33 @@ program
405
483
  for (const w of res.warnings)
406
484
  console.error(`⚠ ${w}`);
407
485
  }
408
- const skipOthers = opts.agents === false;
409
- if (!skipOthers) {
486
+ // `ids` is already resolved, so hosts init is always driven by an explicit
487
+ // list — never by its own detection fallback.
488
+ const others = ids.filter((id) => id !== "claude");
489
+ if (others.length > 0) {
410
490
  const r = runHostsInit(repo, {
411
- agents: explicit?.filter((id) => id !== "claude"),
412
- all: opts.allAgents,
491
+ agents: others,
492
+ home,
413
493
  mcp: opts.mcp,
414
494
  hooks: opts.hooks,
495
+ global: opts.global,
415
496
  });
416
497
  for (const w of r.written)
417
498
  console.error(`✓ ${w.id}: ${w.path} (${w.action})`);
418
- if (!explicit && !opts.allAgents && r.written.length === 0)
419
- console.error("· no other agents detected (see --list-agents / --all-agents)");
420
499
  for (const m of r.mcp)
421
500
  console.error(`✓ mcp ${m.id}: ${m.path} (${m.action})`);
422
501
  for (const h of r.hooks)
423
502
  console.error(`✓ hook ${h.id}: ${h.path} (${h.action})`);
503
+ // Only worth saying when there was actually something out-of-repo to skip.
504
+ if (opts.global === false && selectedWrites(plan, ids).some((w) => w.scope === "global"))
505
+ console.error("· skipped out-of-repo writes (--no-global)");
506
+ }
507
+ // Every host's wiring points at graft/, so the graph is built whatever was
508
+ // selected — not only when Claude Code is in the list (runInit does its own).
509
+ if (!wantClaude) {
510
+ console.error(buildGraphIfMissing(repo, { build: opts.build, cliPath })
511
+ ? "✓ built the graph (graft build)"
512
+ : "· skipped graph build");
424
513
  }
425
514
  const globalOpts = program.opts();
426
515
  const outDir = contextDirFor(repo, globalOpts.dir);