@wrongstack/tools 0.9.20 → 0.10.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.
Files changed (74) hide show
  1. package/dist/audit.js +2 -2
  2. package/dist/audit.js.map +1 -1
  3. package/dist/bash.js +86 -16
  4. package/dist/bash.js.map +1 -1
  5. package/dist/batch-tool-use.js +2 -2
  6. package/dist/batch-tool-use.js.map +1 -1
  7. package/dist/builtin.js +376 -159
  8. package/dist/builtin.js.map +1 -1
  9. package/dist/codebase-index/index.d.ts +24 -4
  10. package/dist/codebase-index/index.js +32 -23
  11. package/dist/codebase-index/index.js.map +1 -1
  12. package/dist/diff.js +25 -9
  13. package/dist/diff.js.map +1 -1
  14. package/dist/document.js +3 -2
  15. package/dist/document.js.map +1 -1
  16. package/dist/edit.js +3 -2
  17. package/dist/edit.js.map +1 -1
  18. package/dist/exec.js +96 -12
  19. package/dist/exec.js.map +1 -1
  20. package/dist/fetch.js +13 -6
  21. package/dist/fetch.js.map +1 -1
  22. package/dist/format.js +74 -4
  23. package/dist/format.js.map +1 -1
  24. package/dist/git.js +81 -8
  25. package/dist/git.js.map +1 -1
  26. package/dist/glob.js +15 -5
  27. package/dist/glob.js.map +1 -1
  28. package/dist/grep.js +32 -9
  29. package/dist/grep.js.map +1 -1
  30. package/dist/index.js +394 -170
  31. package/dist/index.js.map +1 -1
  32. package/dist/install.js +85 -8
  33. package/dist/install.js.map +1 -1
  34. package/dist/json.js +2 -2
  35. package/dist/json.js.map +1 -1
  36. package/dist/lint.js +74 -4
  37. package/dist/lint.js.map +1 -1
  38. package/dist/logs.js +2 -2
  39. package/dist/logs.js.map +1 -1
  40. package/dist/memory.js +13 -6
  41. package/dist/memory.js.map +1 -1
  42. package/dist/mode.js +4 -4
  43. package/dist/mode.js.map +1 -1
  44. package/dist/outdated.js +2 -2
  45. package/dist/outdated.js.map +1 -1
  46. package/dist/pack.js +377 -160
  47. package/dist/pack.js.map +1 -1
  48. package/dist/patch.js +3 -2
  49. package/dist/patch.js.map +1 -1
  50. package/dist/read.js +16 -5
  51. package/dist/read.js.map +1 -1
  52. package/dist/replace.js +3 -2
  53. package/dist/replace.js.map +1 -1
  54. package/dist/scaffold.js +3 -2
  55. package/dist/scaffold.js.map +1 -1
  56. package/dist/search.js +3 -2
  57. package/dist/search.js.map +1 -1
  58. package/dist/test.js +74 -4
  59. package/dist/test.js.map +1 -1
  60. package/dist/todo.js +21 -7
  61. package/dist/todo.js.map +1 -1
  62. package/dist/tool-help.js +5 -5
  63. package/dist/tool-help.js.map +1 -1
  64. package/dist/tool-search.js +2 -2
  65. package/dist/tool-search.js.map +1 -1
  66. package/dist/tool-use.js +4 -4
  67. package/dist/tool-use.js.map +1 -1
  68. package/dist/tree.js +16 -8
  69. package/dist/tree.js.map +1 -1
  70. package/dist/typecheck.js +74 -4
  71. package/dist/typecheck.js.map +1 -1
  72. package/dist/write.js +11 -4
  73. package/dist/write.js.map +1 -1
  74. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import * as fs4 from 'node:fs/promises';
2
2
  import { stat } from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
4
  import { resolve, sep, dirname } from 'node:path';
5
- import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, stripAnsi, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan } from '@wrongstack/core';
5
+ import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, stripAnsi, resolveWstackPaths } from '@wrongstack/core';
6
6
  import { spawn, execFileSync, spawnSync } from 'node:child_process';
7
7
  import * as os from 'node:os';
8
8
  import * as dns from 'node:dns/promises';
@@ -78,24 +78,104 @@ function isBinaryBuffer(buf) {
78
78
  }
79
79
  return false;
80
80
  }
81
+ var COMMAND_OUTPUT_MAX_BYTES = 32768;
82
+ var REPEAT_RUN_THRESHOLD = 3;
83
+ function collapseCarriageReturns(text) {
84
+ const lf = text.replace(/\r\n/g, "\n");
85
+ if (!lf.includes("\r")) return lf;
86
+ return lf.split("\n").map((line) => line.includes("\r") ? line.slice(line.lastIndexOf("\r") + 1) : line).join("\n");
87
+ }
88
+ function collapseConsecutiveDuplicates(text, minRun = REPEAT_RUN_THRESHOLD) {
89
+ const lines = text.split("\n");
90
+ const out = [];
91
+ let i = 0;
92
+ while (i < lines.length) {
93
+ let j = i + 1;
94
+ while (j < lines.length && lines[j] === lines[i]) j++;
95
+ const run = j - i;
96
+ if (run >= minRun) {
97
+ out.push(lines[i], `\u2026 \u27E8repeated ${run}\xD7\u27E9`);
98
+ } else {
99
+ for (let k = i; k < j; k++) out.push(lines[k]);
100
+ }
101
+ i = j;
102
+ }
103
+ return out.join("\n");
104
+ }
105
+ function takeHeadBytes(s, maxBytes) {
106
+ if (maxBytes <= 0) return "";
107
+ if (Buffer.byteLength(s, "utf8") <= maxBytes) return s;
108
+ let lo = 0;
109
+ let hi = s.length;
110
+ while (lo < hi) {
111
+ const mid = Math.ceil((lo + hi) / 2);
112
+ if (Buffer.byteLength(s.slice(0, mid), "utf8") <= maxBytes) lo = mid;
113
+ else hi = mid - 1;
114
+ }
115
+ return s.slice(0, lo);
116
+ }
117
+ function takeTailBytes(s, maxBytes) {
118
+ if (maxBytes <= 0) return "";
119
+ if (Buffer.byteLength(s, "utf8") <= maxBytes) return s;
120
+ let lo = 0;
121
+ let hi = s.length;
122
+ while (lo < hi) {
123
+ const mid = Math.ceil((lo + hi) / 2);
124
+ if (Buffer.byteLength(s.slice(s.length - mid), "utf8") <= maxBytes) lo = mid;
125
+ else hi = mid - 1;
126
+ }
127
+ return s.slice(s.length - lo);
128
+ }
129
+ function truncateHeadTail(s, maxBytes) {
130
+ const total = Buffer.byteLength(s, "utf8");
131
+ if (total <= maxBytes) return s;
132
+ const MARKER_RESERVE = 64;
133
+ const avail = Math.max(0, maxBytes - MARKER_RESERVE);
134
+ const headBudget = Math.floor(avail * 0.45);
135
+ const head = takeHeadBytes(s, headBudget);
136
+ const tail = takeTailBytes(s, avail - Buffer.byteLength(head, "utf8"));
137
+ const kept = Buffer.byteLength(head, "utf8") + Buffer.byteLength(tail, "utf8");
138
+ return `${head}
139
+ \u2026[truncated ${total - kept} bytes]\u2026
140
+ ${tail}`;
141
+ }
142
+ function normalizeCommandOutput(raw, opts = {}) {
143
+ if (!raw) return raw;
144
+ let text = stripAnsi(raw);
145
+ text = collapseCarriageReturns(text);
146
+ text = text.replace(/[ \t]+$/gm, "");
147
+ text = collapseConsecutiveDuplicates(text);
148
+ text = text.replace(/\n{3,}/g, "\n\n");
149
+ return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);
150
+ }
81
151
 
82
152
  // src/read.ts
83
153
  var MAX_BYTES = 5 * 1024 * 1024;
84
154
  var readTool = {
85
155
  name: "read",
86
156
  category: "Filesystem",
87
- description: "Read the contents of a file. Lines are 1-indexed and prefixed with line numbers.",
88
- usageHint: "Read a file before editing it. Returns lines numbered like ` 1\u2192content`. Use `offset` and `limit` for large files (default reads up to 2000 lines).",
157
+ description: "Read the contents of a file with line numbers. This is the primary way to inspect source code, configuration, or any text file before making changes. Lines are returned 1-indexed with a ` N| ` prefix for easy reference in edits.",
158
+ usageHint: "FOUNDATIONAL TOOL \u2014 call this before almost any edit operation.\n\nBest practices:\n- Always read a file before using `edit`, `replace`, or `write` on it (the system often requires it for safety).\n- Use `offset` + `limit` for very large files instead of reading everything at once.\n- Default limit is generous (2000 lines) but can be increased.\n- The output format is designed to be directly usable as context for `edit` operations.",
89
159
  permission: "auto",
90
160
  mutating: false,
161
+ capabilities: ["fs.read"],
91
162
  maxOutputBytes: 262144,
92
163
  timeoutMs: 5e3,
93
164
  inputSchema: {
94
165
  type: "object",
95
166
  properties: {
96
- path: { type: "string", description: "File path (absolute or relative to cwd)" },
97
- offset: { type: "integer", description: "1-based line number to start from" },
98
- limit: { type: "integer", description: "Max lines to read (default 2000)" }
167
+ path: {
168
+ type: "string",
169
+ description: "Path to the file (relative to project root or absolute within project)."
170
+ },
171
+ offset: {
172
+ type: "integer",
173
+ description: "1-based starting line number. Use together with `limit` for large files."
174
+ },
175
+ limit: {
176
+ type: "integer",
177
+ description: "Maximum number of lines to return (default is 2000)."
178
+ }
99
179
  },
100
180
  required: ["path"]
101
181
  },
@@ -145,16 +225,23 @@ var readTool = {
145
225
  var writeTool = {
146
226
  name: "write",
147
227
  category: "Filesystem",
148
- description: "Write or overwrite a file. For existing files, prefer `edit` over `write`.",
149
- usageHint: "Use `write` for new files or full replacements. For partial edits use `edit`. Existing files must have been `read` first in this session.",
228
+ description: "Write or completely overwrite a file on disk. This is a high-privilege operation. For modifying existing files, you should almost always prefer the `edit` tool instead, because `edit` is safer and works on the last-read version of the file.",
229
+ usageHint: "RULES FOR CORRECT USAGE:\n- Use `write` primarily for **new files** or when you want to replace the entire content.\n- For any existing file, strongly prefer `edit` (it requires a prior `read` in the same session and is more precise).\n- You MUST have called `read` on the file earlier in the conversation before using `write` on an existing path (the system enforces this for safety).\n- The path is resolved relative to the project root and protected against escaping the workspace.",
150
230
  permission: "confirm",
151
231
  mutating: true,
152
232
  timeoutMs: 5e3,
233
+ capabilities: ["fs.write"],
153
234
  inputSchema: {
154
235
  type: "object",
155
236
  properties: {
156
- path: { type: "string" },
157
- content: { type: "string" }
237
+ path: {
238
+ type: "string",
239
+ description: "Relative path from project root. Must not escape the project."
240
+ },
241
+ content: {
242
+ type: "string",
243
+ description: "The complete new content of the file."
244
+ }
158
245
  },
159
246
  required: ["path", "content"]
160
247
  },
@@ -202,10 +289,11 @@ var writeTool = {
202
289
  var editTool = {
203
290
  name: "edit",
204
291
  category: "Filesystem",
205
- description: "Make a surgical edit by replacing exact text. Fails if `old_string` is not unique unless `replace_all` is true.",
206
- usageHint: "Always `read` the file first. `old_string` must be an EXACT match (whitespace included). If multiple matches exist, either narrow `old_string` with more context or set `replace_all: true`.",
292
+ description: "Perform a precise, surgical text replacement in a file. This is the preferred tool for modifying existing code. It requires that you have previously called `read` on the file in the current session. Fails safely if the `old_string` appears more than once unless `replace_all` is set.",
293
+ usageHint: "MANDATORY WORKFLOW:\n1. Call `read` on the target file first (in the same conversation).\n2. Use a sufficiently unique `old_string` (include surrounding lines/context if needed).\n3. If the string appears multiple times and you want to change all of them, set `replace_all: true`.\n4. `new_string` must be the exact replacement text.\n\nThis tool is much safer than `write` for existing files because it works against the last-read version.",
207
294
  permission: "confirm",
208
295
  mutating: true,
296
+ capabilities: ["fs.write"],
209
297
  timeoutMs: 5e3,
210
298
  inputSchema: {
211
299
  type: "object",
@@ -367,10 +455,11 @@ var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverag
367
455
  var replaceTool = {
368
456
  name: "replace",
369
457
  category: "Transform",
370
- description: "Batch replace a pattern across multiple files matched by glob. Returns diff for each modified file.",
371
- usageHint: 'Use `glob` for broad patterns (e.g. "**/*.ts"). Set `dry_run: true` to preview without modifying. `files` can be a single path, comma-separated list, or glob pattern.',
458
+ description: "Perform a search-and-replace across multiple files using a regex pattern. This is a powerful bulk transformation tool. Always use `dry_run: true` first on anything non-trivial.",
459
+ usageHint: "DANGEROUS IF USED CARELESSLY \u2014 review the diff output carefully.\n\nRecommended workflow:\n1. Start with `dry_run: true` to see exactly what would change.\n2. Use a specific enough `pattern` (and `glob` / `files`) to avoid accidental broad changes.\n3. `replace_all` controls whether only the first match per file or all matches are replaced.\nThis tool is excellent for large-scale refactors (renaming, import updates, etc.) but must be used with caution.",
372
460
  permission: "confirm",
373
461
  mutating: true,
462
+ capabilities: ["fs.write"],
374
463
  timeoutMs: 3e4,
375
464
  inputSchema: {
376
465
  type: "object",
@@ -562,18 +651,28 @@ var DEFAULT_IGNORE2 = ["node_modules", ".git", "dist", "build", ".next", "covera
562
651
  var globTool = {
563
652
  name: "glob",
564
653
  category: "Filesystem",
565
- description: "Find files matching a glob pattern. Returns paths sorted by mtime (newest first).",
566
- usageHint: "Examples: `**/*.ts`, `src/**/*.test.ts`, `*.json`. Common dirs (node_modules, .git, dist) are ignored by default. Returns up to 1000 paths.",
654
+ description: "Find files matching a glob pattern. Fast way to discover relevant files before reading, grepping, or editing them.",
655
+ usageHint: "RECOMMENDED FOR SCOPING SEARCHES:\n\n- Use early to get a list of files you actually care about.\n- Combine with `path` and `limit`.\n- Default ignores common build/dependency directories.\nMuch more efficient than shell `find` for most use cases inside the agent.",
567
656
  permission: "auto",
568
657
  mutating: false,
658
+ capabilities: ["fs.read"],
569
659
  maxOutputBytes: 65536,
570
660
  timeoutMs: 5e3,
571
661
  inputSchema: {
572
662
  type: "object",
573
663
  properties: {
574
- pattern: { type: "string" },
575
- path: { type: "string", description: "Base directory (defaults to cwd)" },
576
- limit: { type: "integer" }
664
+ pattern: {
665
+ type: "string",
666
+ description: 'Glob pattern to match (e.g. "**/*.ts", "src/**").'
667
+ },
668
+ path: {
669
+ type: "string",
670
+ description: "Base directory to search from (defaults to project root)."
671
+ },
672
+ limit: {
673
+ type: "integer",
674
+ description: "Maximum number of results to return (default 1000, max 5000)."
675
+ }
577
676
  },
578
677
  required: ["pattern"]
579
678
  },
@@ -637,22 +736,45 @@ var DEFAULT_IGNORE3 = ["node_modules", ".git", "dist", "build", ".next", "covera
637
736
  var grepTool = {
638
737
  name: "grep",
639
738
  category: "Search",
640
- description: "Search file contents with a regex. Uses ripgrep when available.",
641
- usageHint: 'Pattern is regex. Use `output_mode: "content"` for matched lines, `"files_with_matches"` for paths, `"count"` for tallies. `glob` filters files (e.g. `*.ts`).',
739
+ description: "Search across files using a regular expression. This is one of the primary code search tools. Prefers ripgrep for speed and features when available.",
740
+ usageHint: 'POWERFUL CODE SEARCH TOOL:\n\n- `pattern` is a regular expression.\n- Use `output_mode: "content"` (default) to get matching lines with context.\n- Use `"files_with_matches"` when you only need the list of files.\n- Use `"count"` for quick statistics.\n- `glob` and `path` let you narrow the search scope significantly.\n- Always prefer this over `bash grep` when searching code.',
642
741
  permission: "auto",
643
742
  mutating: false,
743
+ capabilities: ["fs.read"],
644
744
  maxOutputBytes: 131072,
645
745
  timeoutMs: 1e4,
646
746
  inputSchema: {
647
747
  type: "object",
648
748
  properties: {
649
- pattern: { type: "string" },
650
- path: { type: "string" },
651
- glob: { type: "string" },
652
- output_mode: { type: "string", enum: ["content", "files_with_matches", "count"] },
653
- context_lines: { type: "integer" },
654
- case_insensitive: { type: "boolean" },
655
- limit: { type: "integer" }
749
+ pattern: {
750
+ type: "string",
751
+ description: "Regular expression pattern to search for in file contents."
752
+ },
753
+ path: {
754
+ type: "string",
755
+ description: "Limit search to this directory or file (relative to project root)."
756
+ },
757
+ glob: {
758
+ type: "string",
759
+ description: 'Glob filter for which files to include (e.g. "**/*.ts", "src/**").'
760
+ },
761
+ output_mode: {
762
+ type: "string",
763
+ enum: ["content", "files_with_matches", "count"],
764
+ description: "Return style: detailed matches, just file list, or count only."
765
+ },
766
+ context_lines: {
767
+ type: "integer",
768
+ description: "How many lines of surrounding context to include with each match."
769
+ },
770
+ case_insensitive: {
771
+ type: "boolean",
772
+ description: "Ignore case when matching."
773
+ },
774
+ limit: {
775
+ type: "integer",
776
+ description: "Maximum number of matches to return."
777
+ }
656
778
  },
657
779
  required: ["pattern"]
658
780
  },
@@ -1250,23 +1372,33 @@ var STREAM_FLUSH_BYTES = 4 * 1024;
1250
1372
  var bashTool = {
1251
1373
  name: "bash",
1252
1374
  category: "Shell",
1253
- description: "Run a shell command. stdout and stderr are merged.",
1254
- usageHint: "Runs via `bash -c` (or `cmd /c` on Windows). Cwd is the project root. Default timeout 30s. Output truncated from the middle if oversized. Use for git, npm, builds, tests.",
1375
+ description: "Execute an arbitrary command in the user's default shell (bash/zsh/pwsh/cmd). stdout and stderr are merged into one stream. This is the most powerful and dangerous tool \u2014 it gives the model full access to the developer's machine. Prefer specialized tools whenever possible.",
1376
+ usageHint: "SECURITY WARNING: This tool runs with the full privileges of the current user.\n\nBest practices for the model:\n- Strongly prefer `exec` for known safe commands (node, npm, pnpm, tsc, git, etc.).\n- Use bash only when you genuinely need shell features (pipes, redirection, complex one-liners).\n- Prefer single focused commands over huge `&&` chains.\n- Use `background: true` only for long-running processes (dev servers, watchers).\n- The working directory is the project root.\n- Output may be truncated in the middle for very large results.",
1255
1377
  permission: "confirm",
1256
1378
  mutating: true,
1257
1379
  // Trust rules match on the literal `command` string. Without subjectKey
1258
1380
  // the policy heuristic would have done the same here, but declaring it
1259
1381
  // explicitly removes the implicit cross-tool aliasing.
1260
1382
  subjectKey: "command",
1383
+ capabilities: ["shell.arbitrary"],
1261
1384
  timeoutMs: 3e4,
1262
1385
  maxOutputBytes: MAX_OUTPUT,
1263
1386
  estimatedDurationMs: 3e3,
1264
1387
  inputSchema: {
1265
1388
  type: "object",
1266
1389
  properties: {
1267
- command: { type: "string" },
1268
- timeout_ms: { type: "integer" },
1269
- background: { type: "boolean" }
1390
+ command: {
1391
+ type: "string",
1392
+ description: "The exact shell command to run. Prefer simple, focused commands."
1393
+ },
1394
+ timeout_ms: {
1395
+ type: "integer",
1396
+ description: "Optional timeout for this specific command in milliseconds."
1397
+ },
1398
+ background: {
1399
+ type: "boolean",
1400
+ description: "If true, launch the process in the background and return the PID immediately."
1401
+ }
1270
1402
  },
1271
1403
  required: ["command"]
1272
1404
  },
@@ -1348,7 +1480,7 @@ var bashTool = {
1348
1480
  yield {
1349
1481
  type: "final",
1350
1482
  output: {
1351
- output: truncated ? buf2.slice(0, MAX_OUTPUT) + "\u2026[truncated]" : buf2,
1483
+ output: normalizeCommandOutput(buf2),
1352
1484
  exit_code: null,
1353
1485
  timed_out: false,
1354
1486
  pid: pid2
@@ -1475,11 +1607,10 @@ var bashTool = {
1475
1607
  if (remainder !== null) {
1476
1608
  yield { type: "partial_output", text: remainder };
1477
1609
  }
1478
- const cleaned = stripAnsi(buf).replace(/\r\n?/g, "\n");
1479
1610
  yield {
1480
1611
  type: "final",
1481
1612
  output: {
1482
- output: truncateMiddle(cleaned, MAX_OUTPUT),
1613
+ output: normalizeCommandOutput(buf),
1483
1614
  exit_code: c.code,
1484
1615
  timed_out: timedOut
1485
1616
  }
@@ -1600,18 +1731,32 @@ function validateArgs(cmd, args) {
1600
1731
  var execTool = {
1601
1732
  name: "exec",
1602
1733
  category: "Shell",
1603
- description: "Restricted shell that only runs pre-approved commands with constrained arguments. Safer alternative to `bash`.",
1604
- usageHint: "Set `command` (must be in allowlist). `args` passed through. For arbitrary shell access use the `bash` tool instead.",
1734
+ description: "Execute a **whitelisted, restricted set of commands** with strict argument validation. This is the **preferred and safer** alternative to the `bash` tool for running development tools (node, npm, pnpm, tsc, git, tests, linters, etc.). It prevents arbitrary command injection and limits what the model can do.",
1735
+ usageHint: "PREFERRED SHELL TOOL for most cases.\n\nUse this instead of `bash` whenever possible.\n- `command` must be one of the allowed commands (node, npm, pnpm, git, tsc, eslint, vitest, etc.).\n- Arguments are passed as a clean array (no shell interpretation).\n- `cwd` is validated to stay inside the project.\n- For anything that requires real shell features (pipes, complex redirection, arbitrary commands), fall back to `bash` (with strong justification).\nThis tool significantly reduces the risk compared to full shell access.",
1605
1736
  permission: "confirm",
1606
1737
  mutating: true,
1607
1738
  timeoutMs: TIMEOUT_MS,
1739
+ capabilities: ["shell.restricted"],
1608
1740
  inputSchema: {
1609
1741
  type: "object",
1610
1742
  properties: {
1611
- command: { type: "string", description: "Command to run (must be in allowlist)" },
1612
- args: { type: "array", items: { type: "string" }, description: "Arguments" },
1613
- cwd: { type: "string", description: "Working directory (must resolve inside project root)" },
1614
- timeout: { type: "integer", description: "Timeout in ms (default: 30000)" }
1743
+ command: {
1744
+ type: "string",
1745
+ description: 'The base command to run. Must be in the internal allowlist (e.g. "node", "pnpm", "git", "tsc").'
1746
+ },
1747
+ args: {
1748
+ type: "array",
1749
+ items: { type: "string" },
1750
+ description: "Arguments passed to the command. Passed as an array (no shell parsing)."
1751
+ },
1752
+ cwd: {
1753
+ type: "string",
1754
+ description: "Optional working directory. Must resolve inside the project root."
1755
+ },
1756
+ timeout: {
1757
+ type: "integer",
1758
+ description: "Per-command timeout in milliseconds."
1759
+ }
1615
1760
  },
1616
1761
  required: ["command"]
1617
1762
  },
@@ -1720,10 +1865,10 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1720
1865
  resolve7({
1721
1866
  command: cmd,
1722
1867
  args,
1723
- stdout: stdout.slice(0, MAX_OUTPUT2),
1724
- stderr: stderr.slice(0, MAX_OUTPUT2),
1868
+ stdout: normalizeCommandOutput(stdout),
1869
+ stderr: normalizeCommandOutput(stderr),
1725
1870
  exitCode,
1726
- truncated: stdout.length >= MAX_OUTPUT2 || stderr.length >= MAX_OUTPUT2,
1871
+ truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
1727
1872
  allowed: true
1728
1873
  });
1729
1874
  });
@@ -1734,10 +1879,10 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1734
1879
  resolve7({
1735
1880
  command: cmd,
1736
1881
  args,
1737
- stdout: stdout.slice(0, MAX_OUTPUT2),
1882
+ stdout: normalizeCommandOutput(stdout),
1738
1883
  stderr: err.message,
1739
1884
  exitCode: 1,
1740
- truncated: false,
1885
+ truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES,
1741
1886
  allowed: true
1742
1887
  });
1743
1888
  });
@@ -1827,10 +1972,11 @@ async function guardedFetch(url, maxRedirects, signal, headers = {
1827
1972
  var fetchTool = {
1828
1973
  name: "fetch",
1829
1974
  category: "Network",
1830
- description: "Fetch the contents of a URL. HTML is converted to markdown by default.",
1831
- usageHint: "HTTPS only by default. Localhost and RFC1918 ranges blocked unless WRONGSTACK_FETCH_ALLOW_PRIVATE=1. Max 5 redirects, 20s timeout, 128KB cap.",
1975
+ description: "Fetch a URL and return its content. HTML pages are automatically converted to clean markdown. This tool has strong SSRF protections (private IPs, localhost, and cloud metadata endpoints are blocked by default).",
1976
+ usageHint: "Use this when you need external information (documentation, API responses, web pages, etc.).\n\nSecurity notes:\n- Only HTTPS is allowed by default.\n- Internal/private networks are blocked unless explicitly enabled via environment variable.\n- Redirects are followed but re-validated at each hop.\n- Output is capped (128KB by default) to avoid flooding context.\nPrefer this over raw `bash curl` or `bash wget`.",
1832
1977
  permission: "confirm",
1833
1978
  mutating: false,
1979
+ capabilities: ["net.outbound"],
1834
1980
  // Trust rules for fetch match on the literal URL — declare it explicitly
1835
1981
  // so a user can trust `https://api.example.com/*` without accidentally
1836
1982
  // matching that pattern on any other tool that happens to have a `url`
@@ -1841,8 +1987,15 @@ var fetchTool = {
1841
1987
  inputSchema: {
1842
1988
  type: "object",
1843
1989
  properties: {
1844
- url: { type: "string" },
1845
- format: { type: "string", enum: ["markdown", "text", "raw"] }
1990
+ url: {
1991
+ type: "string",
1992
+ description: "The target URL (must use https://)."
1993
+ },
1994
+ format: {
1995
+ type: "string",
1996
+ enum: ["markdown", "text", "raw"],
1997
+ description: 'Output format. "markdown" is recommended for HTML pages.'
1998
+ }
1846
1999
  },
1847
2000
  required: ["url"]
1848
2001
  },
@@ -2082,10 +2235,11 @@ var TIMEOUT_MS3 = 15e3;
2082
2235
  var searchTool = {
2083
2236
  name: "search",
2084
2237
  category: "Search",
2085
- description: "Search the web for information. Returns title, URL, and snippet for each result.",
2086
- usageHint: "Set `num_results` (1-50, default 10). Use `source` to pick engine: duckduckgo (default), google, bing.",
2238
+ description: "Perform a web search and return results with title, URL, and snippet. Use this when you need up-to-date external information that is not in the local codebase.",
2239
+ usageHint: "Good for: API documentation, error messages, library usage examples, current best practices.\n\n- Prefer specific queries over very broad ones.\n- Results go through the guarded fetch system (same protections as the `fetch` tool).\n- This is often better than the model trying to recall outdated knowledge.",
2087
2240
  permission: "confirm",
2088
2241
  mutating: false,
2242
+ capabilities: ["net.outbound"],
2089
2243
  timeoutMs: TIMEOUT_MS3,
2090
2244
  inputSchema: {
2091
2245
  type: "object",
@@ -2290,8 +2444,8 @@ function stripTags2(html) {
2290
2444
  var todoTool = {
2291
2445
  name: "todo",
2292
2446
  category: "Session",
2293
- description: "Replace the current todo list with a new set of items.",
2294
- usageHint: "Use for multi-step tasks. Replace the full list on each call. At most ONE task may be in_progress at a time. Items have id, content, status (pending|in_progress|completed), and optional activeForm.",
2447
+ description: "Manage the session-level todo list. This is the primary mechanism for tracking multi-step work. The list is fully replaced on every call (not appended).",
2448
+ usageHint: "BEST PRACTICE for complex tasks:\n- At the beginning of a non-trivial task, create a clear todo list with specific, actionable items.\n- Only **one** item should be `in_progress` at any time.\n- Update the list frequently as work progresses (mark items done, add new ones, change status).\n- The system and user can see this list, so keep it honest and up-to-date.\nThis tool is extremely valuable for maintaining focus and giving the user visibility into your plan.",
2295
2449
  permission: "auto",
2296
2450
  mutating: false,
2297
2451
  timeoutMs: 1e3,
@@ -2303,13 +2457,27 @@ var todoTool = {
2303
2457
  items: {
2304
2458
  type: "object",
2305
2459
  properties: {
2306
- id: { type: "string" },
2307
- content: { type: "string" },
2308
- status: { type: "string", enum: ["pending", "in_progress", "completed"] },
2309
- activeForm: { type: "string" }
2460
+ id: {
2461
+ type: "string",
2462
+ description: 'Unique identifier for the todo item (e.g. "1", "auth-flow").'
2463
+ },
2464
+ content: {
2465
+ type: "string",
2466
+ description: "Clear, actionable description of the task."
2467
+ },
2468
+ status: {
2469
+ type: "string",
2470
+ enum: ["pending", "in_progress", "completed"],
2471
+ description: 'Current status. Only one item should be "in_progress" at a time.'
2472
+ },
2473
+ activeForm: {
2474
+ type: "string",
2475
+ description: 'Optional present-tense form shown while the task is active (e.g. "Fixing auth bug").'
2476
+ }
2310
2477
  },
2311
2478
  required: ["id", "content", "status"]
2312
- }
2479
+ },
2480
+ description: "The complete new list of todos. This replaces the previous list entirely."
2313
2481
  }
2314
2482
  },
2315
2483
  required: ["todos"]
@@ -2339,8 +2507,8 @@ var todoTool = {
2339
2507
  var planTool = {
2340
2508
  name: "plan",
2341
2509
  category: "Session",
2342
- description: "Inspect or edit the strategic plan board for this session. Plans persist across resume (unlike todos). Use this to lay out the multi-step approach before diving in, then mark steps in_progress/done as the work proceeds. Promote a plan item to todos to start working on it. Apply templates for common workflows.",
2343
- usageHint: 'Set action to one of: show | add | start | done | remove | promote | derive | template_use | clear. Pass `title` for add. Pass `target` (item id, 1-based index, or title substring) for start/done/remove/promote/derive. Pass `subtasks` for promote/derive to break the plan item into multiple todos. Pass `template` (e.g. "new-feature", "bug-fix", "refactor", "release") for template_use. Always returns the formatted plan plus open/total counts.',
2510
+ description: "Manage a persistent strategic plan for the current session. Unlike todos, plans are meant for higher-level, multi-phase approaches and survive across conversation resumptions. Use this to outline big-picture work, then promote concrete items into the todo list when ready to execute.",
2511
+ usageHint: 'RECOMMENDED FOR COMPLEX, MULTI-PHASE WORK:\n\n- Start by creating a high-level plan with `action: "add"` or using templates (`template_use`).\n- Use `promote` to turn a plan item into actionable todos.\n- Keep plans at the "why and what" level, and todos at the "how and next step" level.\n- Common templates: "new-feature", "bug-fix", "refactor", "release", "security-audit".\n\nThis tool is excellent for maintaining long-term direction across many turns or even multiple sessions.',
2344
2512
  permission: "auto",
2345
2513
  mutating: false,
2346
2514
  timeoutMs: 2e3,
@@ -2349,22 +2517,29 @@ var planTool = {
2349
2517
  properties: {
2350
2518
  action: {
2351
2519
  type: "string",
2352
- enum: ["show", "add", "start", "done", "remove", "promote", "derive", "template_use", "clear"]
2520
+ enum: ["show", "add", "start", "done", "remove", "promote", "derive", "template_use", "clear"],
2521
+ description: "The operation to perform on the plan board."
2522
+ },
2523
+ title: {
2524
+ type: "string",
2525
+ description: "Title of the plan item. Required for action=add."
2526
+ },
2527
+ details: {
2528
+ type: "string",
2529
+ description: "Additional details or description for a new plan item (action=add)."
2353
2530
  },
2354
- title: { type: "string", description: "Required when action = add." },
2355
- details: { type: "string", description: "Optional extra context for add." },
2356
2531
  target: {
2357
2532
  type: "string",
2358
- description: "Plan item id, 1-based index, or title substring. Required for start/done/remove/promote/derive."
2533
+ description: "Identifier for the target plan item (id, 1-based index, or partial title). Required for most actions except add/show/clear."
2359
2534
  },
2360
2535
  subtasks: {
2361
2536
  type: "array",
2362
2537
  items: { type: "string" },
2363
- description: "Optional subtasks for promote/derive. If omitted, a single todo is created from the plan item title."
2538
+ description: "List of subtask titles. Used with promote or derive to break a plan item into multiple todos."
2364
2539
  },
2365
2540
  template: {
2366
2541
  type: "string",
2367
- description: "Template name for template_use action. Available: new-feature, bug-fix, refactor, release, security-audit, onboarding."
2542
+ description: "Template identifier when using action=template_use. Common values: new-feature, bug-fix, refactor, release, security-audit."
2368
2543
  }
2369
2544
  },
2370
2545
  required: ["action"]
@@ -2478,13 +2653,14 @@ var MAX_OUTPUT3 = 1e5;
2478
2653
  var gitTool = {
2479
2654
  name: "git",
2480
2655
  category: "Git",
2481
- description: "Run git commands. Wraps common operations: status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset, worktree.",
2482
- usageHint: "Prefer built-in subcommands over raw args. `command` is required. `message` for commits. `branch` for checkout/branch. `files` for status/diff. `format` for log.",
2656
+ description: "Safe wrapper around common git operations. Supports status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset, worktree, etc. This is the preferred way to interact with git instead of using the raw `bash` or `exec` tools.",
2657
+ usageHint: "ALWAYS prefer this tool over raw shell git commands.\n\nKey fields:\n- `command`: one of the supported subcommands (status, log, diff, commit, etc.)\n- Use `message` only for commit operations.\n- Use `files` array for operations that take paths (status, diff, add, etc.).\n- Non-mutating commands (status, log, diff, branch, fetch) are still permission:confirm for safety.\nNever pass raw git flags through `args` for dangerous operations \u2014 use the structured fields.",
2483
2658
  permission: "confirm",
2484
2659
  // Conservative: any of these may mutate. The non-mutating commands
2485
2660
  // (status/log/diff/branch/fetch) are still gated on `permission: 'confirm'`
2486
2661
  // and `MUTATING_SUBCOMMANDS` is consulted at runtime for per-call checks.
2487
2662
  mutating: true,
2663
+ capabilities: ["fs.write", "shell.restricted"],
2488
2664
  timeoutMs: TIMEOUT_MS4,
2489
2665
  inputSchema: {
2490
2666
  type: "object",
@@ -2699,19 +2875,19 @@ function runGit(args, cwd, signal) {
2699
2875
  child.on("error", (err) => {
2700
2876
  resolve7({
2701
2877
  command: args[0],
2702
- stdout,
2878
+ stdout: normalizeCommandOutput(stdout),
2703
2879
  stderr: err.message,
2704
2880
  exitCode: 1,
2705
- truncated: stdout.length >= MAX_OUTPUT3
2881
+ truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES
2706
2882
  });
2707
2883
  });
2708
2884
  child.on("close", (code) => {
2709
2885
  resolve7({
2710
2886
  command: args[0],
2711
- stdout: stdout.slice(0, MAX_OUTPUT3),
2712
- stderr: stderr.slice(0, MAX_OUTPUT3),
2887
+ stdout: normalizeCommandOutput(stdout),
2888
+ stderr: normalizeCommandOutput(stderr),
2713
2889
  exitCode: code ?? 1,
2714
- truncated: stdout.length >= MAX_OUTPUT3 || stderr.length >= MAX_OUTPUT3
2890
+ truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES
2715
2891
  });
2716
2892
  });
2717
2893
  });
@@ -2719,10 +2895,11 @@ function runGit(args, cwd, signal) {
2719
2895
  var patchTool = {
2720
2896
  name: "patch",
2721
2897
  category: "Filesystem",
2722
- description: "Apply a unified diff patch to files. Writes .orig and .rej files on failure.",
2723
- usageHint: "Set `patch` (the diff text). `directory` defaults to cwd. `strip` removes leading path components. `dry_run` previews.",
2898
+ description: "Apply a unified diff (patch) to the project. This is the correct tool when you have a diff that needs to be applied precisely, including handling of rejects.",
2899
+ usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n- On failure it creates .rej and .orig files for manual review.\nOften cleaner than many small `edit` operations for larger changes.",
2724
2900
  permission: "confirm",
2725
2901
  mutating: true,
2902
+ capabilities: ["fs.write"],
2726
2903
  timeoutMs: 3e4,
2727
2904
  inputSchema: {
2728
2905
  type: "object",
@@ -2828,8 +3005,8 @@ function extractPatchedFiles(output) {
2828
3005
  var jsonTool = {
2829
3006
  name: "json",
2830
3007
  category: "Data",
2831
- description: "Parse, query, and validate JSON/JSON5/YAML. Use `query` with JMESPath-like paths to extract values.",
2832
- usageHint: 'Provide `file` path or `data` string. `query` supports dot notation (e.g. "results[0].name"). `format` outputs in specified format.',
3008
+ description: "Parse, pretty-print, query, and convert between JSON, JSON5, and YAML. Supports simple path-based queries.",
3009
+ usageHint: "VERY USEFUL FOR DATA INSPECTION:\n\n- Use on package.json, tsconfig, config files, or any structured data.\n- `query` lets you extract specific values without reading the whole file.\n- Great for validating that a file has the expected structure.\nPrefer this over raw `read` + manual parsing when dealing with configuration or data files.",
2833
3010
  permission: "auto",
2834
3011
  mutating: false,
2835
3012
  timeoutMs: 5e3,
@@ -2949,28 +3126,44 @@ function toYaml(data, indent = 0) {
2949
3126
  var diffTool = {
2950
3127
  name: "diff",
2951
3128
  category: "Filesystem",
2952
- description: "Show differences between files, commits, or branches. Supports staged vs working tree.",
2953
- usageHint: "Use `files` for file paths, `a`/`b` for commit refs, `staged` for git index. `mode`: unified (default), stat, side-by-side.",
3129
+ description: "Show code differences between files, commits, branches, or staged changes. A safer and more structured alternative to raw `git diff` via shell.",
3130
+ usageHint: 'USE FOR CODE REVIEW AND CHANGE INSPECTION:\n\n- `files` + no `a`/`b` \u2192 diff working tree vs HEAD for those files.\n- `a` and/or `b` \u2192 git-style commit/branch diff.\n- `staged: true` \u2192 only show staged changes.\n- `mode` can be "unified", "stat", or "side-by-side".\nThis tool has important safety guards against flag injection (see previous security findings).',
2954
3131
  permission: "auto",
2955
3132
  mutating: false,
3133
+ capabilities: ["fs.read"],
2956
3134
  timeoutMs: 1e4,
2957
3135
  inputSchema: {
2958
3136
  type: "object",
2959
3137
  properties: {
2960
- path: { type: "string", description: "Working directory for diff" },
3138
+ path: {
3139
+ type: "string",
3140
+ description: "Working directory for the diff operation (defaults to project root)."
3141
+ },
2961
3142
  files: {
2962
3143
  type: "string",
2963
- description: 'File(s) to diff: single path, comma-separated, or "**/*.ts" glob'
3144
+ description: 'Files or globs to diff (e.g. "src/**/*.ts" or comma-separated list).'
3145
+ },
3146
+ a: {
3147
+ type: "string",
3148
+ description: "First ref/commit/branch for git diff (e.g. HEAD, main, a commit hash)."
3149
+ },
3150
+ b: {
3151
+ type: "string",
3152
+ description: "Second ref/commit/branch for git diff."
3153
+ },
3154
+ staged: {
3155
+ type: "boolean",
3156
+ description: "If true, only show changes that are staged in git."
2964
3157
  },
2965
- a: { type: "string", description: "First commit/branch/ref (for git diff)" },
2966
- b: { type: "string", description: "Second commit/branch/ref (for git diff)" },
2967
- staged: { type: "boolean", description: "Diff staged changes only" },
2968
3158
  mode: {
2969
3159
  type: "string",
2970
3160
  enum: ["unified", "side-by-side", "stat"],
2971
- description: "Output mode (default: unified)"
3161
+ description: 'Output format. "unified" is default, "stat" shows summary only.'
2972
3162
  },
2973
- context: { type: "integer", description: "Context lines for unified diff (default: 3)" }
3163
+ context: {
3164
+ type: "integer",
3165
+ description: "Number of context lines for unified diffs (default: 3)."
3166
+ }
2974
3167
  }
2975
3168
  },
2976
3169
  async execute(input, ctx, opts) {
@@ -3089,34 +3282,41 @@ var DEFAULT_IGNORE4 = [
3089
3282
  var treeTool = {
3090
3283
  name: "tree",
3091
3284
  category: "Filesystem",
3092
- description: "Display directory structure as an ASCII tree. Shows files and folders with indentation.",
3093
- usageHint: "Set `path` (default: cwd). `depth` limits nesting (default: 3). `glob` filters files. `exclude` ignores dirs. `show_files` toggles file listing (default: true).",
3285
+ description: "Display a directory tree of the project (or a subpath). This is the recommended way to explore the high-level structure of a codebase before reading specific files.",
3286
+ usageHint: "BEST PRACTICE FOR INITIAL EXPLORATION:\n\n- Call early when working with an unfamiliar project or module.\n- Tune `depth` (default 3) and use `glob`/`exclude` to focus the view.\n- Prefer this over raw `bash find` or `glob` + manual reading when you need a quick structural overview.\nOutput is truncated for very large trees.",
3094
3287
  permission: "auto",
3095
3288
  mutating: false,
3289
+ capabilities: ["fs.read"],
3096
3290
  timeoutMs: 15e3,
3097
3291
  inputSchema: {
3098
3292
  type: "object",
3099
3293
  properties: {
3100
- path: { type: "string", description: "Root directory (default: cwd)" },
3294
+ path: {
3295
+ type: "string",
3296
+ description: "Root directory to display the tree from (defaults to project root)."
3297
+ },
3101
3298
  depth: {
3102
3299
  type: "integer",
3103
- description: "Max nesting depth (default: 3, 0 for unlimited)",
3300
+ description: "Maximum directory depth to traverse (default 3, use 0 for unlimited).",
3104
3301
  minimum: 0,
3105
3302
  maximum: 20
3106
3303
  },
3107
- glob: { type: "string", description: 'Filter files matching glob (e.g. "*.ts")' },
3304
+ glob: {
3305
+ type: "string",
3306
+ description: "Only include files matching this glob pattern."
3307
+ },
3108
3308
  exclude: {
3109
3309
  type: "array",
3110
3310
  items: { type: "string" },
3111
- description: "Directory names to exclude"
3311
+ description: "List of directory names to completely ignore."
3112
3312
  },
3113
3313
  show_files: {
3114
3314
  type: "boolean",
3115
- description: "Show files (default: true, false shows dirs only)"
3315
+ description: "Whether to show individual files (default true)."
3116
3316
  },
3117
3317
  show_dirs: {
3118
3318
  type: "boolean",
3119
- description: "Show directories (default: true)"
3319
+ description: "Whether to show directories (default true)."
3120
3320
  },
3121
3321
  show_hidden: {
3122
3322
  type: "boolean",
@@ -3324,8 +3524,8 @@ async function* spawnStream(opts) {
3324
3524
  var lintTool = {
3325
3525
  name: "lint",
3326
3526
  category: "Code Quality",
3327
- description: "Run a linter on files. Auto-detects biome, eslint, or tslint. Use `fix` to auto-fix issues.",
3328
- usageHint: "Set `files` (glob or comma-separated). `fix` applies corrections. `linter` forces specific tool.",
3527
+ description: "Run the project linter (primarily Biome in this repo). Detects style violations, potential bugs, and formatting issues.",
3528
+ usageHint: "RUN OFTEN DURING DEVELOPMENT:\n\n- `fix: true` will automatically correct what it can.\n- Target specific files or globs when you only want to check part of the project.\nThis is a fast and important quality gate. Use it before typecheck in most workflows.",
3329
3529
  permission: "confirm",
3330
3530
  mutating: false,
3331
3531
  timeoutMs: 6e4,
@@ -3390,7 +3590,7 @@ var lintTool = {
3390
3590
  files_checked: input.files ? Array.isArray(input.files) ? input.files.length : input.files.split(",").length : 0,
3391
3591
  errors,
3392
3592
  warnings,
3393
- output: result.stdout,
3593
+ output: normalizeCommandOutput(result.stdout),
3394
3594
  fix_applied: input.fix ?? false,
3395
3595
  truncated: result.truncated
3396
3596
  }
@@ -3416,8 +3616,8 @@ async function detectLinter(cwd) {
3416
3616
  var formatTool = {
3417
3617
  name: "format",
3418
3618
  category: "Code Quality",
3419
- description: "Format files with biome or prettier. Use `check` to verify without modifying.",
3420
- usageHint: "Set `files` (glob or comma-separated). `check` only validates. `fixer` forces tool.",
3619
+ description: "Format source files according to project style (Biome). Can also run in check-only mode.",
3620
+ usageHint: "RUN REGULARLY:\n\n- Use on changed files before committing.\n- `check: true` verifies formatting without making changes (useful in CI-like flows).\nThis project has very consistent formatting expectations. Always ensure your changes are formatted.",
3421
3621
  permission: "confirm",
3422
3622
  mutating: true,
3423
3623
  timeoutMs: 6e4,
@@ -3490,7 +3690,7 @@ var formatTool = {
3490
3690
  fixer: detected,
3491
3691
  files_checked: 0,
3492
3692
  files_changed: changed,
3493
- output: result.stdout || result.stderr || result.error || "",
3693
+ output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
3494
3694
  truncated: result.truncated
3495
3695
  }
3496
3696
  };
@@ -3513,8 +3713,8 @@ async function detectFixer(cwd) {
3513
3713
  var typecheckTool = {
3514
3714
  name: "typecheck",
3515
3715
  category: "Code Quality",
3516
- description: "Run TypeScript type checking with `tsc --noEmit`. Checks for type errors without compiling.",
3517
- usageHint: "Set `project` for tsconfig path (default: nearest). `strict` enables strictest flags. `all` checks all projects in workspace.",
3716
+ description: "Run the project's TypeScript type checker (`tsc --noEmit` or equivalent). Essential for verifying type safety before making changes or committing.",
3717
+ usageHint: "ALWAYS RUN BEFORE CONSIDERING WORK COMPLETE:\n\n- Use this to catch type errors early.\n- In monorepos, `all: true` will check every package.\n- This is one of the most important quality gates in this project.\nNever claim a task is done without a clean typecheck (unless the user explicitly says otherwise).",
3518
3718
  permission: "confirm",
3519
3719
  mutating: false,
3520
3720
  timeoutMs: 12e4,
@@ -3572,7 +3772,7 @@ var typecheckTool = {
3572
3772
  exit_code: result.exitCode,
3573
3773
  errors,
3574
3774
  warnings,
3575
- output: result.stdout || result.stderr || result.error || "",
3775
+ output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
3576
3776
  truncated: result.truncated
3577
3777
  }
3578
3778
  };
@@ -3593,8 +3793,8 @@ async function findTsConfig(cwd) {
3593
3793
  var testTool = {
3594
3794
  name: "test",
3595
3795
  category: "Code Quality",
3596
- description: "Run tests with vitest, jest, or mocha. Returns pass/fail counts and output.",
3597
- usageHint: "Set `files` for specific tests. `watch` enables watch mode. `coverage` generates coverage report. `grep` filters by name.",
3796
+ description: "Execute the project's test suite. This is one of the most critical tools for validating that your changes are correct.",
3797
+ usageHint: "ESSENTIAL BEFORE CONSIDERING WORK DONE:\n\n- Use `files` or `grep` to run only relevant tests during development.\n- `coverage: true` is useful when working on critical paths.\nRun tests frequently. A clean test run is usually required before the task can be considered complete.",
3598
3798
  permission: "confirm",
3599
3799
  mutating: false,
3600
3800
  timeoutMs: 12e4,
@@ -3732,7 +3932,7 @@ function parseResult(runner, result, duration) {
3732
3932
  passed,
3733
3933
  failed,
3734
3934
  duration_ms: duration,
3735
- output: result.stdout || result.error || "",
3935
+ output: normalizeCommandOutput(result.stdout || result.error || ""),
3736
3936
  truncated: result.truncated
3737
3937
  };
3738
3938
  }
@@ -3741,11 +3941,12 @@ function parseResult(runner, result, duration) {
3741
3941
  var installTool = {
3742
3942
  name: "install",
3743
3943
  category: "Package Management",
3744
- description: "Install npm packages. Detects pnpm/npm/yarn and uses the right package manager.",
3745
- usageHint: "Set `packages` to install. `save` as dependency type. `global` for global install. `dry_run` to preview.",
3944
+ description: "Install, update or manage packages using the detected package manager (pnpm/npm/yarn). Strongly preferred over raw shell commands for dependency management because it is structured and safer.",
3945
+ usageHint: "ALWAYS USE THIS INSTEAD OF BASH FOR PACKAGE WORK:\n\n- Empty `packages` \u2192 normal `install` (respects lockfile).\n- Provide names \u2192 adds/updates specific packages.\n- `dry_run: true` for safe preview.\n- Set `save` appropriately.\nThis tool has proper capability declaration and is heavily recommended in the security posture of the project.",
3746
3946
  permission: "confirm",
3747
3947
  mutating: true,
3748
3948
  timeoutMs: 12e4,
3949
+ capabilities: ["package.install", "shell.restricted"],
3749
3950
  inputSchema: {
3750
3951
  type: "object",
3751
3952
  properties: {
@@ -3756,14 +3957,20 @@ var installTool = {
3756
3957
  save: {
3757
3958
  type: "string",
3758
3959
  enum: ["dependency", "dev", "optional"],
3759
- description: "Save as regular, dev, or optional dependency"
3960
+ description: 'Where to save the package(s): "dependency", "devDependencies", or "optionalDependencies".'
3961
+ },
3962
+ cwd: {
3963
+ type: "string",
3964
+ description: "Working directory for the install command (must stay inside project)."
3760
3965
  },
3761
- cwd: { type: "string", description: "Working directory (default: cwd)" },
3762
3966
  dry_run: {
3763
3967
  type: "boolean",
3764
- description: "Preview install without modifying (default: false)"
3968
+ description: "If true, show what would be installed without actually modifying package.json or node_modules."
3765
3969
  },
3766
- global: { type: "boolean", description: "Install globally (default: false)" }
3970
+ global: {
3971
+ type: "boolean",
3972
+ description: "Whether to perform a global install (use with caution)."
3973
+ }
3767
3974
  }
3768
3975
  },
3769
3976
  async execute(input, ctx, opts) {
@@ -3827,7 +4034,7 @@ var installTool = {
3827
4034
  output: {
3828
4035
  packages: pkgList,
3829
4036
  exit_code: result.exitCode,
3830
- output: result.stdout || result.stderr || result.error || "",
4037
+ output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
3831
4038
  dry_run: args.includes("--dry-run"),
3832
4039
  truncated: result.truncated
3833
4040
  }
@@ -3853,8 +4060,8 @@ async function detectPackageManager(cwd) {
3853
4060
  var auditTool = {
3854
4061
  name: "audit",
3855
4062
  category: "Package Management",
3856
- description: "Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.",
3857
- usageHint: "Set `level` to filter minimum severity. `fix` attempts auto-fix. `packages` checks specific packages.",
4063
+ description: "Run a security audit against project dependencies (using pnpm/npm audit). Reports known vulnerabilities with severity.",
4064
+ usageHint: "CRITICAL SECURITY TOOL:\n\n- Run regularly and especially before any release.\n- Use `level` to focus on high/critical issues.\n- `fix` can attempt automatic remediation for some vulnerabilities.\nThis is one of the most important tools for supply chain security.",
3858
4065
  permission: "confirm",
3859
4066
  mutating: false,
3860
4067
  timeoutMs: 6e4,
@@ -3961,8 +4168,8 @@ function parseAuditOutput(json, exitCode) {
3961
4168
  var outdatedTool = {
3962
4169
  name: "outdated",
3963
4170
  category: "Package Management",
3964
- description: "Check for outdated npm packages. Shows current, wanted, and latest versions.",
3965
- usageHint: "Set `check` to filter specific packages. `format` as list or table. `include_deprecated` shows deprecated packages.",
4171
+ description: "Check for outdated dependencies in the project. Reports current, wanted (semver range), and latest versions available.",
4172
+ usageHint: "MAINTENANCE & SECURITY TOOL:\n\n- Run periodically or before dependency-related work.\n- Helps surface packages that may need updates for security or features.\n- Safe, read-only operation.\nUse the output to decide on upgrades. Prefer this over manual shell commands for dependency hygiene.",
3966
4173
  permission: "auto",
3967
4174
  mutating: true,
3968
4175
  timeoutMs: 6e4,
@@ -4071,8 +4278,8 @@ function parseOutdatedOutput(json, exitCode) {
4071
4278
  var logsTool = {
4072
4279
  name: "logs",
4073
4280
  category: "Logs",
4074
- description: "Stream or fetch logs from a service or file. Supports Docker, systemd, or plain log files.",
4075
- usageHint: "Set `service` for Docker/systemd, `path` for file. `lines` limits output. `stream` for tail -f behavior. `filter` regex filters lines.",
4281
+ description: "Read or stream logs from files, Docker containers, or systemd services. Useful for debugging running applications.",
4282
+ usageHint: "DEBUGGING TOOL \u2014 USE CAREFULLY IN AUTONOMOUS MODE:\n\n- Prefer `path` for local files or `service` for containers/systemd.\n- `stream: true` = live tail (can be expensive).\n- Always use `filter` (regex) when possible to reduce noise and token usage.\n- Long-running streams should be avoided unless the user explicitly wants live logs.",
4076
4283
  permission: "confirm",
4077
4284
  mutating: false,
4078
4285
  timeoutMs: 3e4,
@@ -4272,8 +4479,8 @@ function parseLine(line) {
4272
4479
  var documentTool = {
4273
4480
  name: "document",
4274
4481
  category: "Project",
4275
- description: "Generate or update documentation comments for functions, classes, and types. Supports JSDoc, TSDoc, and block comments.",
4276
- usageHint: "Set `target` for what to document. `files` for paths. `style` for comment format. `overwrite` replaces existing docs.",
4482
+ description: "Automatically generate or update documentation comments (JSDoc/TSDoc style) for code. Can target specific symbols or entire files/directories.",
4483
+ usageHint: "USE FOR IMPROVING CODE DOCUMENTATION:\n\n- Good for adding missing docs to public APIs or complex functions.\n- `overwrite: true` will replace existing documentation (use carefully).\n- You can target specific symbols via `target` or whole files/directories via `files`.\nAlways review the generated documentation before committing \u2014 the model can hallucinate details.",
4277
4484
  permission: "confirm",
4278
4485
  mutating: true,
4279
4486
  timeoutMs: 3e4,
@@ -4511,10 +4718,11 @@ describe('{{Name}}', () => {
4511
4718
  var scaffoldTool = {
4512
4719
  name: "scaffold",
4513
4720
  category: "Project",
4514
- description: "Generate boilerplate code from built-in templates or paths. Creates package.json, source files, tests.",
4515
- usageHint: "Set `template` (npm-package, cli-tool, react-component) and `name`. `vars` for template variables. `dry_run` preview.",
4721
+ description: "Generate new files and folder structures from built-in templates or custom definitions. This is the recommended way to bootstrap new packages, components, or modules instead of creating files one by one with `write`.",
4722
+ usageHint: "PREFERRED FOR SCAFFOLDING:\n\n- Use built-in templates when they match your needs (e.g. react-component, npm-package).\n- Supports `dry_run` so you can preview exactly what will be created.\n- Has the powerful `fs.write.outside-project` capability \u2014 review paths carefully.\nMuch cleaner and safer than manually writing multiple files.",
4516
4723
  permission: "confirm",
4517
4724
  mutating: true,
4725
+ capabilities: ["fs.write.outside-project", "fs.write"],
4518
4726
  timeoutMs: 3e4,
4519
4727
  inputSchema: {
4520
4728
  type: "object",
@@ -4604,8 +4812,8 @@ function substituteVars(content, name, vars) {
4604
4812
  var toolSearchTool = {
4605
4813
  name: "tool_search",
4606
4814
  category: "Meta",
4607
- description: "Search available tools by name, description, tags, permission level, or mutating flag.",
4608
- usageHint: "Set `query` for keyword search. `tags` to filter by category. `permission` to filter by required permission. `mutating` to filter by mutating flag.",
4815
+ description: "Search the catalog of available tools. Very useful when you are unsure which tool to use for a task.",
4816
+ usageHint: "SELF-DISCOVERY TOOL:\n\n- Use when you need to find the right tool for a job.\n- `query` searches names and descriptions.\n- You can filter by `tags` (category), `permission`, or `mutating`.\nCall this before guessing tool names. It helps you discover the best tool for the current situation.",
4609
4817
  permission: "auto",
4610
4818
  mutating: false,
4611
4819
  timeoutMs: 1e3,
@@ -4678,8 +4886,8 @@ var toolSearchTool = {
4678
4886
  var toolUseTool = {
4679
4887
  name: "tool_use",
4680
4888
  category: "Meta",
4681
- description: "Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.",
4682
- usageHint: "Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.",
4889
+ description: "Directly execute any registered tool by its exact name, bypassing normal discovery. This is a powerful meta-tool intended for cases where the agent has a clear plan and knows precisely which tool to invoke.",
4890
+ usageHint: "ADVANCED META TOOL \u2014 USE WITH CARE:\n\n- Only use when you are certain of the exact tool name and its expected input shape.\n- Prefer using the normal tool calling mechanism when possible.\n- Very useful in batch-tool-use or when orchestrating complex workflows programmatically.\n- The call still goes through full permission checks and capability validation.",
4683
4891
  permission: "confirm",
4684
4892
  mutating: true,
4685
4893
  timeoutMs: 6e4,
@@ -4688,11 +4896,11 @@ var toolUseTool = {
4688
4896
  properties: {
4689
4897
  tool: {
4690
4898
  type: "string",
4691
- description: "Exact name of the tool to execute"
4899
+ description: 'The exact registered name of the tool to invoke (e.g. "bash", "read", "codebase-search").'
4692
4900
  },
4693
4901
  input: {
4694
4902
  type: "object",
4695
- description: "Input parameters for the tool"
4903
+ description: "The input object matching the target tool's inputSchema."
4696
4904
  }
4697
4905
  },
4698
4906
  required: ["tool"]
@@ -4747,8 +4955,8 @@ var toolUseTool = {
4747
4955
  var batchToolUseTool = {
4748
4956
  name: "batch_tool_use",
4749
4957
  category: "Meta",
4750
- description: "Execute multiple tool calls in sequence or parallel. Returns all results.",
4751
- usageHint: "Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).",
4958
+ description: "Execute a batch of tool calls either sequentially or in parallel. Returns structured results for every call.",
4959
+ usageHint: "ADVANCED / POWER USER TOOL:\n\n- Useful when you have a clear list of independent operations to perform.\n- `parallel: true` (default) runs them concurrently for speed.\n- `stop_on_error: true` makes it fail fast on the first error.\nUse with care \u2014 batching many mutating operations can be risky. Prefer explicit sequential steps for important work.",
4752
4960
  permission: "confirm",
4753
4961
  mutating: true,
4754
4962
  timeoutMs: 12e4,
@@ -4851,8 +5059,8 @@ async function executeSingle(call, ctx, opts) {
4851
5059
  var toolHelpTool = {
4852
5060
  name: "tool_help",
4853
5061
  category: "Meta",
4854
- description: "Get help and usage information for a specific tool or list all available tools.",
4855
- usageHint: "Set `tool` for specific help. Omit to list all tools. `format`: short (one-liner), full (schema), markdown (formatted).",
5062
+ description: "Get detailed help for one or more tools, including their full schema and usage guidance. This is the best way to understand exactly how to call a specific tool.",
5063
+ usageHint: "USE WHEN YOU NEED PRECISE TOOL INFORMATION:\n\n- Call with a specific `tool` name when you want the full schema and current usageHint.\n- Omit `tool` (or use a broad query) to get an overview of available tools.\n- Different `format` options give you different levels of detail.\nThis tool is extremely valuable for self-correction when you are unsure about a tool's interface.",
4856
5064
  permission: "auto",
4857
5065
  mutating: false,
4858
5066
  timeoutMs: 5e3,
@@ -4861,16 +5069,16 @@ var toolHelpTool = {
4861
5069
  properties: {
4862
5070
  tool: {
4863
5071
  type: "string",
4864
- description: "Tool name to get help for (omit for all tools)"
5072
+ description: "Specific tool name to get detailed help for. Omit to get a list of all tools."
4865
5073
  },
4866
5074
  format: {
4867
5075
  type: "string",
4868
5076
  enum: ["short", "full", "markdown"],
4869
- description: "Output format (default: short)"
5077
+ description: 'Level of detail: "short" (summary), "full" (with full schema), "markdown" (human readable).'
4870
5078
  },
4871
5079
  include_examples: {
4872
5080
  type: "boolean",
4873
- description: "Include usage examples in output (default: false)"
5081
+ description: "Whether to include example usage in the response."
4874
5082
  }
4875
5083
  }
4876
5084
  },
@@ -4974,16 +5182,23 @@ function rememberTool(memory) {
4974
5182
  return {
4975
5183
  name: "remember",
4976
5184
  category: "Session",
4977
- description: "Persist a short note to project or user memory.",
4978
- usageHint: "Use sparingly. Only for facts that should outlive the session (project conventions, user preferences). Transient state belongs in `todo`. Scope defaults to project-memory.",
5185
+ description: "Persist important long-term facts into project or user memory. These memories survive conversation restarts and are available to future sessions.",
5186
+ usageHint: 'USE VERY SPARINGLY \u2014 ONLY FOR HIGH-VALUE RECURRING KNOWLEDGE:\n\n- Good: coding standards, project conventions, user preferences, recurring architecture decisions, important facts.\n- Bad: temporary state, current task progress, one-off notes \u2192 use `todo` or `plan` instead.\n- `scope: "project"` \u2192 visible to all agents on this codebase.\n- `scope: "user"` \u2192 personal to you.\n\nPolluting memory with noise hurts future context quality. Be extremely deliberate.',
4979
5187
  permission: "auto",
4980
5188
  mutating: true,
4981
5189
  timeoutMs: 2e3,
4982
5190
  inputSchema: {
4983
5191
  type: "object",
4984
5192
  properties: {
4985
- text: { type: "string" },
4986
- scope: { type: "string", enum: ["project-agents", "project-memory", "user-memory"] }
5193
+ text: {
5194
+ type: "string",
5195
+ description: "The fact or note to remember. Keep it concise and factual."
5196
+ },
5197
+ scope: {
5198
+ type: "string",
5199
+ enum: ["project-agents", "project-memory", "user-memory"],
5200
+ description: "Where to store it: project-memory (shared), user-memory (personal), or project-agents."
5201
+ }
4987
5202
  },
4988
5203
  required: ["text"]
4989
5204
  },
@@ -4999,8 +5214,8 @@ function forgetTool(memory) {
4999
5214
  return {
5000
5215
  name: "forget",
5001
5216
  category: "Session",
5002
- description: "Remove memory entries matching a substring (case-insensitive).",
5003
- usageHint: "Removes ALL matching bullet lines in the given scope. Use a unique substring.",
5217
+ description: "Remove memory entries that contain the given substring (case-insensitive). Use with caution.",
5218
+ usageHint: "This permanently deletes matching memories in the chosen scope.\n- Provide a reasonably specific `query` to avoid deleting unrelated memories.\n- Always double-check before calling with broad queries.\n- Use `remember` + `forget` together to maintain clean long-term memory.",
5004
5219
  permission: "confirm",
5005
5220
  mutating: true,
5006
5221
  timeoutMs: 2e3,
@@ -5026,8 +5241,8 @@ function createModeTool(modeStore) {
5026
5241
  return {
5027
5242
  name: "mode",
5028
5243
  category: "Session",
5029
- description: "Get, list, or switch the agent mode. Modes inject role-specific prompts into the system prompt.",
5030
- usageHint: "Set `action`: `get` (current mode), `list` (all modes), `set <modeId>` (switch), `clear` (reset to default).",
5244
+ description: "Manage agent operating modes. Modes change the agent's behavior, personality, and system prompt for different workflows (e.g. coding, security review, planning).",
5245
+ usageHint: "POWERFUL BEHAVIOR CONTROL TOOL:\n\n- Use `list` to see available modes.\n- Use `set <modeId>` to switch the agent into a specific role/mode.\n- Use `get` to check current mode.\n- Use `clear` to return to default behavior.\nSwitching modes is very effective for specialized tasks. The mode change affects how the agent reasons and which guidelines it follows.",
5031
5246
  permission: "confirm",
5032
5247
  mutating: true,
5033
5248
  timeoutMs: 5e3,
@@ -5037,11 +5252,11 @@ function createModeTool(modeStore) {
5037
5252
  action: {
5038
5253
  type: "string",
5039
5254
  enum: ["get", "list", "set", "clear"],
5040
- description: "Action: get current, list all, set mode, or clear"
5255
+ description: "The mode operation to perform."
5041
5256
  },
5042
5257
  mode: {
5043
5258
  type: "string",
5044
- description: "Mode ID to switch to (required for action=set)"
5259
+ description: "The mode identifier to activate (only required when action=set)."
5045
5260
  }
5046
5261
  },
5047
5262
  required: ["action"]
@@ -5141,8 +5356,14 @@ function lspKindToInternalKind(k) {
5141
5356
  }
5142
5357
 
5143
5358
  // src/codebase-index/writer.ts
5144
- var INDEX_DIR = ".codebase-index";
5145
5359
  var DB_FILE = "index.db";
5360
+ function resolveIndexDir(projectRoot, override) {
5361
+ return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
5362
+ }
5363
+ function codebaseIndexDirOverride(ctx) {
5364
+ const v = ctx.meta?.["codebaseIndexDir"];
5365
+ return typeof v === "string" ? v : void 0;
5366
+ }
5146
5367
  var warningSilenced = false;
5147
5368
  function silenceSqliteExperimentalWarning() {
5148
5369
  if (warningSilenced) return;
@@ -5170,16 +5391,16 @@ function loadDatabaseSync() {
5170
5391
  return DatabaseSyncCtor;
5171
5392
  }
5172
5393
  var IndexStore = class {
5173
- constructor(projectRoot) {
5174
- this.projectRoot = projectRoot;
5175
- const dir = path.join(projectRoot, INDEX_DIR);
5176
- fs13.mkdirSync(dir, { recursive: true });
5394
+ db;
5395
+ /** Absolute path to this project's index directory. */
5396
+ indexDir;
5397
+ constructor(projectRoot, opts = {}) {
5398
+ this.indexDir = resolveIndexDir(projectRoot, opts.indexDir);
5399
+ fs13.mkdirSync(this.indexDir, { recursive: true });
5177
5400
  const Database = loadDatabaseSync();
5178
- this.db = new Database(path.join(dir, DB_FILE));
5401
+ this.db = new Database(path.join(this.indexDir, DB_FILE));
5179
5402
  this.initSchema();
5180
5403
  }
5181
- projectRoot;
5182
- db;
5183
5404
  initSchema() {
5184
5405
  this.db.exec(`
5185
5406
  CREATE TABLE IF NOT EXISTS metadata (
@@ -5370,7 +5591,7 @@ var IndexStore = class {
5370
5591
  totalFiles,
5371
5592
  byLang,
5372
5593
  byKind,
5373
- indexPath: path.join(this.projectRoot, INDEX_DIR),
5594
+ indexPath: this.indexDir,
5374
5595
  lastIndexed,
5375
5596
  sizeBytes,
5376
5597
  version: SCHEMA_VERSION
@@ -5463,7 +5684,7 @@ var IndexStore = class {
5463
5684
  }));
5464
5685
  }
5465
5686
  sizeBytes() {
5466
- const dbPath = path.join(this.projectRoot, INDEX_DIR, DB_FILE);
5687
+ const dbPath = path.join(this.indexDir, DB_FILE);
5467
5688
  try {
5468
5689
  return fs13.statSync(dbPath).size;
5469
5690
  } catch {
@@ -6739,8 +6960,8 @@ async function parseFile(file, content, lang) {
6739
6960
  }
6740
6961
  }
6741
6962
  async function runIndexer(_ctx, opts) {
6742
- const { projectRoot, force = false, langs, ignore = [] } = opts;
6743
- const store = new IndexStore(projectRoot);
6963
+ const { projectRoot, force = false, langs, ignore = [], indexDir } = opts;
6964
+ const store = new IndexStore(projectRoot, { indexDir });
6744
6965
  const startMs = Date.now();
6745
6966
  const errors = [];
6746
6967
  const langStats = {};
@@ -6857,8 +7078,8 @@ async function runIndexer(_ctx, opts) {
6857
7078
  var codebaseIndexTool = {
6858
7079
  name: "codebase-index",
6859
7080
  category: "Project",
6860
- description: "Build or update the symbol index for the project. Runs incrementally by default \u2014 only re-indexes files that changed since the last run.",
6861
- usageHint: "Call with `force: true` to wipe and rebuild the index from scratch. Use `langs` to limit to specific languages. First call without arguments to do an incremental index.",
7081
+ description: "Build or incrementally update the project-wide symbol index. This powers fast codebase search and understanding. By default it only processes files that have changed since the last indexing run.",
7082
+ usageHint: "IMPORTANT FOR LARGE CODEBASES:\n\n- First run (or after major changes): consider `force: true` for a clean rebuild.\n- Normal usage: call without arguments for fast incremental updates.\n- Use `langs` to restrict to specific languages if you only care about certain parts of the project.\nThis tool is relatively expensive \u2014 do not call it on every turn. Use it when the index is stale or before heavy codebase-search sessions.",
6862
7083
  permission: "auto",
6863
7084
  mutating: true,
6864
7085
  timeoutMs: 12e4,
@@ -6880,7 +7101,8 @@ var codebaseIndexTool = {
6880
7101
  const result = await runIndexer(ctx, {
6881
7102
  projectRoot: ctx.projectRoot,
6882
7103
  force: input.force ?? false,
6883
- langs: input.langs
7104
+ langs: input.langs,
7105
+ indexDir: codebaseIndexDirOverride(ctx)
6884
7106
  });
6885
7107
  return result;
6886
7108
  }
@@ -6978,10 +7200,11 @@ var Bm25Index = class {
6978
7200
  var codebaseSearchTool = {
6979
7201
  name: "codebase-search",
6980
7202
  category: "Project",
6981
- description: "Search indexed code symbols by name, signature, or doc comment. Uses BM25 ranking for relevance.",
6982
- usageHint: "Pass `query` for keyword search. Filter with `kind` (class/function/interface/etc), `lang` (ts/js/etc), `file` (substring). `limit` caps results (default 20).",
7203
+ description: "Semantic/keyword search over the indexed codebase symbols (functions, classes, interfaces, etc.). Uses BM25 ranking. Much more powerful and structured than raw `grep` for finding code by name or concept.",
7204
+ usageHint: "PREFERRED FOR CODE UNDERSTANDING:\n\n- Use when you need to find where something is defined or used by name.\n- `kind` filter is very useful (e.g. only functions or only interfaces).\n- Combine with `file` filter to scope to a specific directory or module.\nThis is generally better than `grep` when you are looking for symbols rather than arbitrary text patterns.",
6983
7205
  permission: "auto",
6984
7206
  mutating: false,
7207
+ capabilities: ["fs.read"],
6985
7208
  timeoutMs: 1e4,
6986
7209
  inputSchema: {
6987
7210
  type: "object",
@@ -7016,7 +7239,7 @@ var codebaseSearchTool = {
7016
7239
  required: ["query"]
7017
7240
  },
7018
7241
  async execute(input, ctx) {
7019
- const store = new IndexStore(ctx.projectRoot);
7242
+ const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
7020
7243
  try {
7021
7244
  const limit = Math.min(input.limit ?? 20, 100);
7022
7245
  const candidates = store.search(input.query, {
@@ -7061,10 +7284,11 @@ var codebaseSearchTool = {
7061
7284
  var codebaseStatsTool = {
7062
7285
  name: "codebase-stats",
7063
7286
  category: "Project",
7064
- description: "Return statistics about the symbol index: total symbols, files, breakdown by language and kind, index size, and last update time.",
7065
- usageHint: "No arguments needed. Use to check if the index is stale or healthy before running a search.",
7287
+ description: "Return health and statistics about the current symbol index (total symbols, files, language/kind breakdown, size, last update). Useful to decide whether to re-index.",
7288
+ usageHint: "CALL BEFORE HEAVY CODEBASE-SEARCH WORK:\n\n- Use to see if the index is up-to-date or needs a refresh.\n- No arguments required.\n- Helps avoid wasting tokens on searches against a stale index.\nLightweight and safe to call frequently.",
7066
7289
  permission: "auto",
7067
7290
  mutating: false,
7291
+ capabilities: ["fs.read"],
7068
7292
  timeoutMs: 5e3,
7069
7293
  inputSchema: {
7070
7294
  type: "object",
@@ -7072,7 +7296,7 @@ var codebaseStatsTool = {
7072
7296
  additionalProperties: false
7073
7297
  },
7074
7298
  async execute(_input, ctx) {
7075
- const store = new IndexStore(ctx.projectRoot);
7299
+ const store = new IndexStore(ctx.projectRoot, { indexDir: codebaseIndexDirOverride(ctx) });
7076
7300
  try {
7077
7301
  const stats = store.getStats();
7078
7302
  return {
@@ -7132,7 +7356,7 @@ var builtinTools = [
7132
7356
  // src/pack.ts
7133
7357
  var builtinToolsPack = {
7134
7358
  name: "builtin-tools",
7135
- description: "WrongStack built-in filesystem, execution, network, lifecycle, and agent-control tools.",
7359
+ description: "The complete set of built-in tools that ship with WrongStack. Covers filesystem (read/write/edit/replace/glob/grep/tree), execution (bash/exec/git/install), networking (fetch/search), code quality (lint/test/typecheck/format), planning (todo/plan/memory), and meta tools (tool-search/tool-help/batch-tool-use/codebase-*).",
7136
7360
  tools: builtinTools
7137
7361
  };
7138
7362