token-pilot 0.19.0 → 0.19.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 CHANGED
@@ -215,7 +215,7 @@ For more control, you can add rules to your project:
215
215
 
216
216
  | Tool | Description |
217
217
  |------|-------------|
218
- | `session_snapshot` | Capture session state as a compact markdown block (<200 tokens): goal, confirmed facts, relevant files, blockers, next step. Call before compaction or when switching direction. |
218
+ | `session_snapshot` | Capture session state as a compact markdown block (<200 tokens): goal, decisions (with reasoning), confirmed facts, relevant files, blockers, next step. Decisions field prevents revisiting rejected approaches after compaction. |
219
219
  | `session_analytics` | Token savings report: total saved, per-tool breakdown, top files, per-intent breakdown, decision insights, policy advisories. |
220
220
 
221
221
  ## CLI Commands
@@ -2,14 +2,14 @@ export interface BinaryStatus {
2
2
  available: boolean;
3
3
  path: string;
4
4
  version: string | null;
5
- source: 'system' | 'managed' | 'none';
5
+ source: 'system' | 'npm' | 'managed' | 'none';
6
6
  }
7
7
  /**
8
- * Find ast-index binary: check system PATH first, then managed install.
8
+ * Find ast-index binary: config system PATH npm global → managed install.
9
9
  */
10
10
  export declare function findBinary(configPath?: string | null): Promise<BinaryStatus>;
11
11
  /**
12
- * Download and install ast-index binary from GitHub releases.
12
+ * Install ast-index: tries npm global first (all platforms), falls back to GitHub download.
13
13
  */
14
14
  export declare function installBinary(onProgress?: (msg: string) => void): Promise<BinaryStatus>;
15
15
  /**
@@ -12,7 +12,7 @@ const REPO = 'defendend/Claude-ast-index-search';
12
12
  const BINARY_NAME = platform() === 'win32' ? 'ast-index.exe' : 'ast-index';
13
13
  const INSTALL_DIR = resolve(homedir(), '.token-pilot', 'bin');
14
14
  /**
15
- * Find ast-index binary: check system PATH first, then managed install.
15
+ * Find ast-index binary: config system PATH npm global → managed install.
16
16
  */
17
17
  export async function findBinary(configPath) {
18
18
  // 1. Config override
@@ -28,7 +28,15 @@ export async function findBinary(configPath) {
28
28
  const version = await getBinaryVersion(systemPath);
29
29
  return { available: true, path: systemPath, version, source: 'system' };
30
30
  }
31
- // 3. Managed install
31
+ // 3. npm global install (@ast-index/cli)
32
+ const npmPath = await findViaNpmBin();
33
+ if (npmPath) {
34
+ const version = await getBinaryVersion(npmPath);
35
+ if (version) {
36
+ return { available: true, path: npmPath, version, source: 'npm' };
37
+ }
38
+ }
39
+ // 4. Managed install (GitHub download)
32
40
  const managedPath = resolve(INSTALL_DIR, BINARY_NAME);
33
41
  const version = await getBinaryVersion(managedPath);
34
42
  if (version) {
@@ -37,24 +45,53 @@ export async function findBinary(configPath) {
37
45
  return { available: false, path: '', version: null, source: 'none' };
38
46
  }
39
47
  /**
40
- * Download and install ast-index binary from GitHub releases.
48
+ * Install ast-index: tries npm global first (all platforms), falls back to GitHub download.
41
49
  */
42
50
  export async function installBinary(onProgress) {
43
51
  const log = onProgress ?? (() => { });
52
+ // Try npm first — simpler, handles all platforms including Windows
53
+ try {
54
+ return await installViaNpm(log);
55
+ }
56
+ catch (npmErr) {
57
+ log(`npm install failed (${npmErr instanceof Error ? npmErr.message : npmErr}), trying GitHub download...`);
58
+ }
59
+ // GitHub download fallback
60
+ return installViaNpmFallback(log);
61
+ }
62
+ async function installViaNpm(onProgress) {
63
+ onProgress('Installing @ast-index/cli via npm...');
64
+ await new Promise((resolve, reject) => {
65
+ execFile('npm', ['install', '-g', '@ast-index/cli'], { timeout: 120_000 }, (err, _stdout, stderr) => {
66
+ if (err)
67
+ reject(new Error(stderr.trim() || err.message));
68
+ else
69
+ resolve();
70
+ });
71
+ });
72
+ const binPath = await findViaNpmBin();
73
+ if (!binPath) {
74
+ throw new Error('@ast-index/cli installed but binary not found in npm prefix');
75
+ }
76
+ const version = await getBinaryVersion(binPath);
77
+ onProgress(`Installed ast-index ${version} via npm at ${binPath}`);
78
+ return { available: true, path: binPath, version, source: 'npm' };
79
+ }
80
+ async function installViaNpmFallback(onProgress) {
44
81
  // Determine platform/arch
45
82
  const plat = getPlatform();
46
83
  const ar = getArch();
47
84
  if (!plat || !ar) {
48
85
  throw new Error(`Unsupported platform: ${platform()} ${arch()}`);
49
86
  }
50
- log('Fetching latest release info...');
87
+ onProgress('Fetching latest release info...');
51
88
  const release = await fetchLatestRelease();
52
89
  const assetName = buildAssetName(release.tag, plat, ar);
53
90
  const asset = release.assets.find(a => a.name === assetName);
54
91
  if (!asset) {
55
92
  throw new Error(`No binary found for ${plat}-${ar}. Available: ${release.assets.map(a => a.name).join(', ')}`);
56
93
  }
57
- log(`Downloading ${asset.name} (${(asset.size / 1024 / 1024).toFixed(1)}MB)...`);
94
+ onProgress(`Downloading ${asset.name} (${(asset.size / 1024 / 1024).toFixed(1)}MB)...`);
58
95
  await mkdir(INSTALL_DIR, { recursive: true });
59
96
  const tmpPath = resolve(INSTALL_DIR, `${BINARY_NAME}.tmp`);
60
97
  const finalPath = resolve(INSTALL_DIR, BINARY_NAME);
@@ -62,19 +99,16 @@ export async function installBinary(onProgress) {
62
99
  if (assetName.endsWith('.tar.gz')) {
63
100
  await downloadAndExtractTarGz(asset.url, INSTALL_DIR, BINARY_NAME);
64
101
  }
65
- else if (assetName.endsWith('.zip')) {
66
- // For Windows, download zip and extract
102
+ else {
67
103
  await downloadFile(asset.url, tmpPath);
68
- // Simple approach: use system unzip if available
69
- throw new Error('ZIP extraction not yet supported. Please install ast-index manually on Windows.');
104
+ throw new Error('ZIP extraction not yet supported. Please use: npm install -g @ast-index/cli');
70
105
  }
71
106
  await chmod(finalPath, 0o755);
72
107
  const version = await getBinaryVersion(finalPath);
73
- log(`Installed ast-index ${version} to ${finalPath}`);
108
+ onProgress(`Installed ast-index ${version} to ${finalPath}`);
74
109
  return { available: true, path: finalPath, version, source: 'managed' };
75
110
  }
76
111
  catch (err) {
77
- // Cleanup on failure
78
112
  try {
79
113
  await rm(tmpPath, { force: true });
80
114
  }
@@ -130,6 +164,35 @@ export function isNewerVersion(current, latest) {
130
164
  return false;
131
165
  }
132
166
  // --- Internal helpers ---
167
+ /**
168
+ * Find ast-index binary installed via `npm install -g @ast-index/cli`.
169
+ * Checks the npm global prefix bin directory.
170
+ */
171
+ async function findViaNpmBin() {
172
+ try {
173
+ const prefix = await new Promise((resolve, reject) => {
174
+ execFile('npm', ['config', 'get', 'prefix'], { timeout: 3000 }, (err, stdout) => {
175
+ if (err)
176
+ reject(err);
177
+ else
178
+ resolve(stdout.trim());
179
+ });
180
+ });
181
+ // Unix: <prefix>/bin/ast-index | Windows: <prefix>\ast-index.exe or <prefix>\bin\ast-index.exe
182
+ const candidates = platform() === 'win32'
183
+ ? [resolve(prefix, BINARY_NAME), resolve(prefix, 'bin', BINARY_NAME)]
184
+ : [resolve(prefix, 'bin', BINARY_NAME)];
185
+ for (const candidate of candidates) {
186
+ try {
187
+ await access(candidate);
188
+ return candidate;
189
+ }
190
+ catch { }
191
+ }
192
+ }
193
+ catch { }
194
+ return null;
195
+ }
133
196
  function getPlatform() {
134
197
  switch (platform()) {
135
198
  case 'darwin': return 'darwin';
@@ -27,12 +27,16 @@ export declare class AstIndexClient {
27
27
  symbol(name: string): Promise<AstIndexSymbolDetail | null>;
28
28
  search(query: string, options?: {
29
29
  inFile?: string;
30
+ type?: string;
30
31
  maxResults?: number;
31
32
  fuzzy?: boolean;
32
33
  }): Promise<AstIndexSearchResult[]>;
33
34
  usages(symbolName: string): Promise<AstIndexUsageResult[]>;
34
35
  implementations(name: string): Promise<AstIndexImplementation[]>;
35
- hierarchy(name: string): Promise<AstIndexHierarchyNode | null>;
36
+ hierarchy(name: string, options?: {
37
+ inFile?: string;
38
+ module?: string;
39
+ }): Promise<AstIndexHierarchyNode | null>;
36
40
  stats(): Promise<string | null>;
37
41
  listFiles(): Promise<string[]>;
38
42
  refs(symbolName: string, limit?: number): Promise<AstIndexRefsResponse>;
@@ -223,6 +223,8 @@ export class AstIndexClient {
223
223
  const args = ['search', query, '--format', 'json'];
224
224
  if (options?.inFile)
225
225
  args.push('--in-file', options.inFile);
226
+ if (options?.type)
227
+ args.push('--type', options.type);
226
228
  if (options?.maxResults)
227
229
  args.push('--limit', String(options.maxResults));
228
230
  if (options?.fuzzy)
@@ -295,10 +297,15 @@ export class AstIndexClient {
295
297
  return [];
296
298
  }
297
299
  }
298
- async hierarchy(name) {
300
+ async hierarchy(name, options) {
299
301
  await this.ensureIndex();
300
302
  try {
301
- const result = await this.exec(['hierarchy', name, '--format', 'json']);
303
+ const args = ['hierarchy', name, '--format', 'json'];
304
+ if (options?.inFile)
305
+ args.push('--in-file', options.inFile);
306
+ if (options?.module)
307
+ args.push('--module', options.module);
308
+ const result = await this.exec(args);
302
309
  try {
303
310
  return JSON.parse(result);
304
311
  }
@@ -1,5 +1,6 @@
1
1
  export interface SessionSnapshotArgs {
2
2
  goal: string;
3
+ decisions?: string[];
3
4
  confirmed?: string[];
4
5
  files?: string[];
5
6
  blocked?: string;
@@ -1,6 +1,12 @@
1
1
  export function handleSessionSnapshot(args) {
2
2
  const lines = ['## Session State'];
3
3
  lines.push(`**Goal:** ${args.goal}`);
4
+ if (args.decisions?.length) {
5
+ lines.push('**Decisions:**');
6
+ for (const item of args.decisions) {
7
+ lines.push(`- ${item}`);
8
+ }
9
+ }
4
10
  if (args.confirmed?.length) {
5
11
  lines.push('**Confirmed:**');
6
12
  for (const item of args.confirmed) {
@@ -8,11 +8,17 @@ export interface HookUninstallResult {
8
8
  fatal: boolean;
9
9
  message: string;
10
10
  }
11
+ export interface HookInstallOptions {
12
+ /** Absolute path to the entry script (dist/index.js). When provided, hooks use absolute paths instead of bare "token-pilot". */
13
+ scriptPath?: string;
14
+ /** Absolute path to the node binary. Defaults to process.execPath. */
15
+ nodeExecPath?: string;
16
+ }
11
17
  /**
12
18
  * Install Token Pilot hook into Claude Code settings.
13
19
  * Creates or updates .claude/settings.json with PreToolUse hook.
14
20
  */
15
- export declare function installHook(projectRoot: string): Promise<HookInstallResult>;
21
+ export declare function installHook(projectRoot: string, options?: HookInstallOptions): Promise<HookInstallResult>;
16
22
  /**
17
23
  * Remove Token Pilot hook from Claude Code settings.
18
24
  */
@@ -1,35 +1,55 @@
1
1
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
2
  import { resolve, dirname } from 'node:path';
3
- const HOOK_CONFIG = {
4
- hooks: {
5
- PreToolUse: [
6
- {
7
- matcher: "Read",
8
- hooks: [
9
- {
10
- type: "command",
11
- command: "token-pilot hook-read",
12
- },
13
- ],
14
- },
15
- {
16
- matcher: "Edit",
17
- hooks: [
18
- {
19
- type: "command",
20
- command: "token-pilot hook-edit",
21
- },
22
- ],
23
- },
24
- ],
25
- },
26
- };
3
+ /**
4
+ * Build hook command that works in any shell (/bin/sh, bash, etc.)
5
+ * Uses absolute paths to node + script to avoid PATH/nvm issues.
6
+ * Falls back to bare "token-pilot" only for manual CLI installs.
7
+ */
8
+ function buildHookCommand(action, options) {
9
+ if (options?.scriptPath) {
10
+ const node = options.nodeExecPath || process.execPath;
11
+ return `${node} ${options.scriptPath} ${action}`;
12
+ }
13
+ return `token-pilot ${action}`;
14
+ }
15
+ function createHookConfig(options) {
16
+ return {
17
+ hooks: {
18
+ PreToolUse: [
19
+ {
20
+ matcher: "Read",
21
+ hooks: [
22
+ {
23
+ type: "command",
24
+ command: buildHookCommand('hook-read', options),
25
+ },
26
+ ],
27
+ },
28
+ {
29
+ matcher: "Edit",
30
+ hooks: [
31
+ {
32
+ type: "command",
33
+ command: buildHookCommand('hook-edit', options),
34
+ },
35
+ ],
36
+ },
37
+ ],
38
+ },
39
+ };
40
+ }
27
41
  /**
28
42
  * Install Token Pilot hook into Claude Code settings.
29
43
  * Creates or updates .claude/settings.json with PreToolUse hook.
30
44
  */
31
- export async function installHook(projectRoot) {
45
+ export async function installHook(projectRoot, options) {
46
+ // Skip auto-install when running as a Claude Code plugin —
47
+ // the plugin system already registers hooks via .claude-plugin/hooks/hooks.json
48
+ if (process.env.CLAUDE_PLUGIN_ROOT) {
49
+ return { installed: false, fatal: false, message: 'Running as plugin — hooks registered via plugin system.' };
50
+ }
32
51
  const settingsPath = resolve(projectRoot, '.claude', 'settings.json');
52
+ const hookConfig = createHookConfig(options);
33
53
  try {
34
54
  // Ensure .claude dir exists
35
55
  await mkdir(dirname(settingsPath), { recursive: true });
@@ -63,16 +83,26 @@ export async function installHook(projectRoot) {
63
83
  const existingHooks = settings.hooks?.PreToolUse;
64
84
  const isTokenPilotHook = (h) => h.hooks?.some((hook) => hook.command?.includes('token-pilot'));
65
85
  if (Array.isArray(existingHooks)) {
66
- const hasRead = existingHooks.some((h) => h.matcher === 'Read' && isTokenPilotHook(h));
67
- const hasEdit = existingHooks.some((h) => h.matcher === 'Edit' && isTokenPilotHook(h));
68
- if (hasRead && hasEdit) {
69
- return { installed: false, fatal: false, message: 'Token Pilot hooks already installed.' };
86
+ // Remove old broken hooks (bare "token-pilot" without absolute path)
87
+ // and replace with working ones using absolute paths
88
+ const oldBrokenHooks = existingHooks.filter((h) => isTokenPilotHook(h) && h.hooks?.some((hook) => hook.command?.match(/^token-pilot\s/) // bare command without path
89
+ ));
90
+ if (oldBrokenHooks.length > 0 && options?.scriptPath) {
91
+ // Remove old broken hooks, will re-add with absolute paths below
92
+ settings.hooks.PreToolUse = existingHooks.filter((h) => !isTokenPilotHook(h));
93
+ }
94
+ else {
95
+ const hasRead = existingHooks.some((h) => h.matcher === 'Read' && isTokenPilotHook(h));
96
+ const hasEdit = existingHooks.some((h) => h.matcher === 'Edit' && isTokenPilotHook(h));
97
+ if (hasRead && hasEdit) {
98
+ return { installed: false, fatal: false, message: 'Token Pilot hooks already installed.' };
99
+ }
70
100
  }
71
101
  // Add missing hooks
72
- for (const hookDef of HOOK_CONFIG.hooks.PreToolUse) {
73
- const exists = existingHooks.some((h) => h.matcher === hookDef.matcher && isTokenPilotHook(h));
102
+ for (const hookDef of hookConfig.hooks.PreToolUse) {
103
+ const exists = settings.hooks.PreToolUse.some((h) => h.matcher === hookDef.matcher && isTokenPilotHook(h));
74
104
  if (!exists) {
75
- existingHooks.push(hookDef);
105
+ settings.hooks.PreToolUse.push(hookDef);
76
106
  }
77
107
  }
78
108
  }
@@ -80,7 +110,7 @@ export async function installHook(projectRoot) {
80
110
  // Create hooks section
81
111
  if (!settings.hooks)
82
112
  settings.hooks = {};
83
- settings.hooks.PreToolUse = HOOK_CONFIG.hooks.PreToolUse;
113
+ settings.hooks.PreToolUse = hookConfig.hooks.PreToolUse;
84
114
  }
85
115
  await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n');
86
116
  return {
package/dist/index.js CHANGED
@@ -122,7 +122,16 @@ export async function startServer(cliArgs = process.argv.slice(2)) {
122
122
  const binaryStatus = await findBinary(config.astIndex.binaryPath);
123
123
  checkAllUpdates(config, binaryStatus).catch(() => { });
124
124
  // Auto-install PreToolUse hook (non-blocking, Claude Code only)
125
- installHook(projectRoot).then(result => {
125
+ // Uses absolute paths to node + script so hooks work in /bin/sh (nvm, npx, etc.)
126
+ let hookOptions;
127
+ try {
128
+ const rawPath = fileURLToPath(new URL('./index.js', import.meta.url));
129
+ hookOptions = { scriptPath: realpathSync(rawPath), nodeExecPath: process.execPath };
130
+ }
131
+ catch {
132
+ // Can't resolve script path (e.g. running from src/ in tests) — fall back to bare command
133
+ }
134
+ installHook(projectRoot, hookOptions).then(result => {
126
135
  if (result.installed) {
127
136
  console.error(`[token-pilot] hook auto-installed: ${result.message}`);
128
137
  }
@@ -239,7 +248,15 @@ export function handleHookEdit() {
239
248
  process.exit(0);
240
249
  }
241
250
  export async function handleInstallHook(projectRoot) {
242
- const result = await installHook(projectRoot);
251
+ let hookOptions;
252
+ try {
253
+ const rawPath = fileURLToPath(new URL('./index.js', import.meta.url));
254
+ hookOptions = { scriptPath: realpathSync(rawPath), nodeExecPath: process.execPath };
255
+ }
256
+ catch {
257
+ // Fall back to bare command
258
+ }
259
+ const result = await installHook(projectRoot, hookOptions);
243
260
  console.log(result.message);
244
261
  process.exit(result.fatal ? 1 : 0);
245
262
  }
@@ -443,25 +460,25 @@ export async function checkAllUpdates(config, binaryStatus) {
443
460
  }
444
461
  }
445
462
  export function printHelp() {
446
- console.log(`token-pilot v${getVersion()} — MCP server for token-efficient code reading
447
-
448
- Usage:
449
- token-pilot [project-root] Start MCP server (default: cwd)
450
- token-pilot init [dir] Create .mcp.json with token-pilot + context-mode
451
- token-pilot install-hook [root] Install PreToolUse hook (Claude Code only)
452
- token-pilot uninstall-hook [root] Remove PreToolUse hook
453
- token-pilot install-ast-index Download ast-index binary (auto on first run)
454
- token-pilot doctor Run diagnostics (check ast-index, config, updates)
455
- token-pilot --version Show version
456
- token-pilot --help Show this help
457
-
458
- Quick start:
459
- npx token-pilot init Setup .mcp.json (token-pilot + context-mode)
460
-
461
- MCP Tools (18):
462
- smart_read, read_symbol, read_range, read_diff, read_for_edit, smart_read_many,
463
- find_usages, find_unused, related_files, outline, project_overview, session_analytics,
464
- code_audit, module_info, smart_diff, explore_area, smart_log, test_summary
463
+ console.log(`token-pilot v${getVersion()} — MCP server for token-efficient code reading
464
+
465
+ Usage:
466
+ token-pilot [project-root] Start MCP server (default: cwd)
467
+ token-pilot init [dir] Create .mcp.json with token-pilot + context-mode
468
+ token-pilot install-hook [root] Install PreToolUse hook (Claude Code only)
469
+ token-pilot uninstall-hook [root] Remove PreToolUse hook
470
+ token-pilot install-ast-index Download ast-index binary (auto on first run)
471
+ token-pilot doctor Run diagnostics (check ast-index, config, updates)
472
+ token-pilot --version Show version
473
+ token-pilot --help Show this help
474
+
475
+ Quick start:
476
+ npx token-pilot init Setup .mcp.json (token-pilot + context-mode)
477
+
478
+ MCP Tools (18):
479
+ smart_read, read_symbol, read_range, read_diff, read_for_edit, smart_read_many,
480
+ find_usages, find_unused, related_files, outline, project_overview, session_analytics,
481
+ code_audit, module_info, smart_diff, explore_area, smart_log, test_summary
465
482
  `);
466
483
  process.exit(0);
467
484
  }
@@ -70,6 +70,7 @@ export declare const TOOL_DEFINITIONS: ({
70
70
  runner?: undefined;
71
71
  timeout?: undefined;
72
72
  goal?: undefined;
73
+ decisions?: undefined;
73
74
  confirmed?: undefined;
74
75
  files?: undefined;
75
76
  blocked?: undefined;
@@ -144,6 +145,7 @@ export declare const TOOL_DEFINITIONS: ({
144
145
  runner?: undefined;
145
146
  timeout?: undefined;
146
147
  goal?: undefined;
148
+ decisions?: undefined;
147
149
  confirmed?: undefined;
148
150
  files?: undefined;
149
151
  blocked?: undefined;
@@ -218,6 +220,7 @@ export declare const TOOL_DEFINITIONS: ({
218
220
  runner?: undefined;
219
221
  timeout?: undefined;
220
222
  goal?: undefined;
223
+ decisions?: undefined;
221
224
  confirmed?: undefined;
222
225
  files?: undefined;
223
226
  blocked?: undefined;
@@ -282,6 +285,7 @@ export declare const TOOL_DEFINITIONS: ({
282
285
  runner?: undefined;
283
286
  timeout?: undefined;
284
287
  goal?: undefined;
288
+ decisions?: undefined;
285
289
  confirmed?: undefined;
286
290
  files?: undefined;
287
291
  blocked?: undefined;
@@ -343,6 +347,7 @@ export declare const TOOL_DEFINITIONS: ({
343
347
  runner?: undefined;
344
348
  timeout?: undefined;
345
349
  goal?: undefined;
350
+ decisions?: undefined;
346
351
  confirmed?: undefined;
347
352
  files?: undefined;
348
353
  blocked?: undefined;
@@ -404,6 +409,7 @@ export declare const TOOL_DEFINITIONS: ({
404
409
  runner?: undefined;
405
410
  timeout?: undefined;
406
411
  goal?: undefined;
412
+ decisions?: undefined;
407
413
  confirmed?: undefined;
408
414
  files?: undefined;
409
415
  blocked?: undefined;
@@ -489,6 +495,7 @@ export declare const TOOL_DEFINITIONS: ({
489
495
  runner?: undefined;
490
496
  timeout?: undefined;
491
497
  goal?: undefined;
498
+ decisions?: undefined;
492
499
  confirmed?: undefined;
493
500
  files?: undefined;
494
501
  blocked?: undefined;
@@ -553,6 +560,7 @@ export declare const TOOL_DEFINITIONS: ({
553
560
  runner?: undefined;
554
561
  timeout?: undefined;
555
562
  goal?: undefined;
563
+ decisions?: undefined;
556
564
  confirmed?: undefined;
557
565
  files?: undefined;
558
566
  blocked?: undefined;
@@ -632,6 +640,7 @@ export declare const TOOL_DEFINITIONS: ({
632
640
  runner?: undefined;
633
641
  timeout?: undefined;
634
642
  goal?: undefined;
643
+ decisions?: undefined;
635
644
  confirmed?: undefined;
636
645
  files?: undefined;
637
646
  blocked?: undefined;
@@ -694,6 +703,7 @@ export declare const TOOL_DEFINITIONS: ({
694
703
  runner?: undefined;
695
704
  timeout?: undefined;
696
705
  goal?: undefined;
706
+ decisions?: undefined;
697
707
  confirmed?: undefined;
698
708
  files?: undefined;
699
709
  blocked?: undefined;
@@ -752,6 +762,7 @@ export declare const TOOL_DEFINITIONS: ({
752
762
  runner?: undefined;
753
763
  timeout?: undefined;
754
764
  goal?: undefined;
765
+ decisions?: undefined;
755
766
  confirmed?: undefined;
756
767
  files?: undefined;
757
768
  blocked?: undefined;
@@ -816,6 +827,7 @@ export declare const TOOL_DEFINITIONS: ({
816
827
  runner?: undefined;
817
828
  timeout?: undefined;
818
829
  goal?: undefined;
830
+ decisions?: undefined;
819
831
  confirmed?: undefined;
820
832
  files?: undefined;
821
833
  blocked?: undefined;
@@ -874,6 +886,7 @@ export declare const TOOL_DEFINITIONS: ({
874
886
  runner?: undefined;
875
887
  timeout?: undefined;
876
888
  goal?: undefined;
889
+ decisions?: undefined;
877
890
  confirmed?: undefined;
878
891
  files?: undefined;
879
892
  blocked?: undefined;
@@ -938,6 +951,7 @@ export declare const TOOL_DEFINITIONS: ({
938
951
  runner?: undefined;
939
952
  timeout?: undefined;
940
953
  goal?: undefined;
954
+ decisions?: undefined;
941
955
  confirmed?: undefined;
942
956
  files?: undefined;
943
957
  blocked?: undefined;
@@ -1009,6 +1023,7 @@ export declare const TOOL_DEFINITIONS: ({
1009
1023
  runner?: undefined;
1010
1024
  timeout?: undefined;
1011
1025
  goal?: undefined;
1026
+ decisions?: undefined;
1012
1027
  confirmed?: undefined;
1013
1028
  files?: undefined;
1014
1029
  blocked?: undefined;
@@ -1071,6 +1086,7 @@ export declare const TOOL_DEFINITIONS: ({
1071
1086
  runner?: undefined;
1072
1087
  timeout?: undefined;
1073
1088
  goal?: undefined;
1089
+ decisions?: undefined;
1074
1090
  confirmed?: undefined;
1075
1091
  files?: undefined;
1076
1092
  blocked?: undefined;
@@ -1136,6 +1152,7 @@ export declare const TOOL_DEFINITIONS: ({
1136
1152
  runner?: undefined;
1137
1153
  timeout?: undefined;
1138
1154
  goal?: undefined;
1155
+ decisions?: undefined;
1139
1156
  confirmed?: undefined;
1140
1157
  files?: undefined;
1141
1158
  blocked?: undefined;
@@ -1201,6 +1218,7 @@ export declare const TOOL_DEFINITIONS: ({
1201
1218
  runner?: undefined;
1202
1219
  timeout?: undefined;
1203
1220
  goal?: undefined;
1221
+ decisions?: undefined;
1204
1222
  confirmed?: undefined;
1205
1223
  files?: undefined;
1206
1224
  blocked?: undefined;
@@ -1265,6 +1283,7 @@ export declare const TOOL_DEFINITIONS: ({
1265
1283
  runner?: undefined;
1266
1284
  timeout?: undefined;
1267
1285
  goal?: undefined;
1286
+ decisions?: undefined;
1268
1287
  confirmed?: undefined;
1269
1288
  files?: undefined;
1270
1289
  blocked?: undefined;
@@ -1330,6 +1349,7 @@ export declare const TOOL_DEFINITIONS: ({
1330
1349
  ref?: undefined;
1331
1350
  count?: undefined;
1332
1351
  goal?: undefined;
1352
+ decisions?: undefined;
1333
1353
  confirmed?: undefined;
1334
1354
  files?: undefined;
1335
1355
  blocked?: undefined;
@@ -1347,6 +1367,13 @@ export declare const TOOL_DEFINITIONS: ({
1347
1367
  type: string;
1348
1368
  description: string;
1349
1369
  };
1370
+ decisions: {
1371
+ type: string;
1372
+ items: {
1373
+ type: string;
1374
+ };
1375
+ description: string;
1376
+ };
1350
1377
  confirmed: {
1351
1378
  type: string;
1352
1379
  items: {
@@ -29,7 +29,7 @@ export const MCP_INSTRUCTIONS = [
29
29
  '17. Module architecture → module_info (deps, dependents, public API)',
30
30
  '18. Read markdown/yaml/json/csv section → read_section (loads one heading/key/row-range, NOT the whole file)',
31
31
  ' - For editing sections: read_for_edit(path, section="Section Name")',
32
- '19. Long session / before compaction → session_snapshot (capture goal, confirmed facts, files, next step as <200 token block)',
32
+ '19. Long session / before compaction → session_snapshot (capture goal, decisions, confirmed facts, files, next step as <200 token block)',
33
33
  ' - Budget-constrained? Use smart_read(max_tokens=N) to auto-downgrade output size',
34
34
  '',
35
35
  'USE DEFAULT TOOLS ONLY FOR: regex text search → Grep | exact raw content → Read | non-code configs → Read',
@@ -362,6 +362,7 @@ export const TOOL_DEFINITIONS = [
362
362
  type: 'object',
363
363
  properties: {
364
364
  goal: { type: 'string', description: 'Session goal — what and why' },
365
+ decisions: { type: 'array', items: { type: 'string' }, description: 'Key decisions made and why (e.g., "removed sysfee step — caused double counting"). Prevents revisiting rejected approaches.' },
365
366
  confirmed: { type: 'array', items: { type: 'string' }, description: 'Established facts (what has been verified)' },
366
367
  files: { type: 'array', items: { type: 'string' }, description: 'Relevant file paths' },
367
368
  blocked: { type: 'string', description: 'Current blocker or obstacle' },