@oh-my-pi/pi-coding-agent 14.3.0 → 14.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +84 -1
  2. package/package.json +7 -7
  3. package/src/autoresearch/prompt.md +1 -1
  4. package/src/commit/agentic/prompts/analyze-file.md +1 -1
  5. package/src/config/model-registry.ts +67 -15
  6. package/src/config/prompt-templates.ts +5 -5
  7. package/src/config/settings-schema.ts +4 -4
  8. package/src/cursor.ts +3 -8
  9. package/src/discovery/helpers.ts +3 -3
  10. package/src/edit/diff.ts +50 -47
  11. package/src/edit/index.ts +86 -57
  12. package/src/edit/line-hash.ts +735 -19
  13. package/src/edit/modes/apply-patch.ts +0 -9
  14. package/src/edit/modes/atom.ts +658 -0
  15. package/src/edit/modes/chunk.ts +14 -24
  16. package/src/edit/modes/hashline.ts +188 -136
  17. package/src/edit/modes/patch.ts +5 -9
  18. package/src/edit/modes/replace.ts +6 -11
  19. package/src/edit/renderer.ts +14 -10
  20. package/src/edit/streaming.ts +50 -16
  21. package/src/exec/bash-executor.ts +2 -4
  22. package/src/export/html/template.generated.ts +1 -1
  23. package/src/export/html/template.js +4 -12
  24. package/src/extensibility/custom-tools/types.ts +2 -0
  25. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  26. package/src/internal-urls/docs-index.generated.ts +2 -2
  27. package/src/lsp/index.ts +1 -1
  28. package/src/mcp/render.ts +1 -8
  29. package/src/modes/components/assistant-message.ts +4 -0
  30. package/src/modes/components/diff.ts +23 -14
  31. package/src/modes/components/footer.ts +21 -16
  32. package/src/modes/components/settings-defs.ts +6 -1
  33. package/src/modes/components/todo-reminder.ts +1 -8
  34. package/src/modes/components/tool-execution.ts +1 -4
  35. package/src/modes/controllers/selector-controller.ts +1 -1
  36. package/src/modes/print-mode.ts +8 -0
  37. package/src/prompts/agents/librarian.md +1 -1
  38. package/src/prompts/agents/reviewer.md +4 -4
  39. package/src/prompts/ci-green-request.md +1 -1
  40. package/src/prompts/review-request.md +1 -1
  41. package/src/prompts/system/subagent-system-prompt.md +3 -3
  42. package/src/prompts/system/subagent-yield-reminder.md +11 -0
  43. package/src/prompts/system/system-prompt.md +3 -0
  44. package/src/prompts/tools/ask.md +3 -2
  45. package/src/prompts/tools/ast-edit.md +15 -19
  46. package/src/prompts/tools/ast-grep.md +18 -24
  47. package/src/prompts/tools/atom.md +96 -0
  48. package/src/prompts/tools/chunk-edit.md +37 -161
  49. package/src/prompts/tools/debug.md +4 -5
  50. package/src/prompts/tools/exit-plan-mode.md +4 -5
  51. package/src/prompts/tools/find.md +4 -8
  52. package/src/prompts/tools/github.md +18 -0
  53. package/src/prompts/tools/grep.md +4 -5
  54. package/src/prompts/tools/hashline.md +22 -89
  55. package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
  56. package/src/prompts/tools/inspect-image.md +6 -6
  57. package/src/prompts/tools/lsp.md +1 -1
  58. package/src/prompts/tools/patch.md +12 -19
  59. package/src/prompts/tools/python.md +3 -2
  60. package/src/prompts/tools/read-chunk.md +2 -3
  61. package/src/prompts/tools/read.md +2 -2
  62. package/src/prompts/tools/ssh.md +8 -17
  63. package/src/prompts/tools/todo-write.md +54 -41
  64. package/src/sdk.ts +14 -9
  65. package/src/session/agent-session.ts +25 -2
  66. package/src/task/executor.ts +43 -48
  67. package/src/task/render.ts +11 -13
  68. package/src/tools/ask.ts +7 -7
  69. package/src/tools/ast-edit.ts +45 -41
  70. package/src/tools/ast-grep.ts +77 -85
  71. package/src/tools/bash.ts +8 -9
  72. package/src/tools/browser.ts +32 -30
  73. package/src/tools/calculator.ts +4 -4
  74. package/src/tools/cancel-job.ts +1 -1
  75. package/src/tools/checkpoint.ts +2 -2
  76. package/src/tools/debug.ts +41 -37
  77. package/src/tools/exit-plan-mode.ts +1 -1
  78. package/src/tools/find.ts +4 -4
  79. package/src/tools/gh-renderer.ts +12 -4
  80. package/src/tools/gh.ts +509 -697
  81. package/src/tools/grep.ts +115 -130
  82. package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
  83. package/src/tools/index.ts +14 -32
  84. package/src/tools/inspect-image.ts +3 -3
  85. package/src/tools/json-tree.ts +114 -114
  86. package/src/tools/match-line-format.ts +9 -8
  87. package/src/tools/notebook.ts +8 -7
  88. package/src/tools/poll-tool.ts +2 -1
  89. package/src/tools/python.ts +9 -23
  90. package/src/tools/read.ts +32 -21
  91. package/src/tools/render-mermaid.ts +1 -1
  92. package/src/tools/render-utils.ts +18 -0
  93. package/src/tools/renderers.ts +2 -2
  94. package/src/tools/report-tool-issue.ts +3 -2
  95. package/src/tools/resolve.ts +1 -1
  96. package/src/tools/review.ts +12 -10
  97. package/src/tools/search-tool-bm25.ts +2 -4
  98. package/src/tools/ssh.ts +4 -4
  99. package/src/tools/todo-write.ts +172 -147
  100. package/src/tools/vim.ts +14 -15
  101. package/src/tools/write.ts +4 -4
  102. package/src/tools/{submit-result.ts → yield.ts} +11 -13
  103. package/src/utils/edit-mode.ts +2 -1
  104. package/src/utils/file-display-mode.ts +10 -5
  105. package/src/utils/git.ts +9 -5
  106. package/src/utils/shell-snapshot.ts +2 -3
  107. package/src/vim/render.ts +4 -4
  108. package/src/prompts/system/subagent-submit-reminder.md +0 -11
  109. package/src/prompts/tools/gh-issue-view.md +0 -11
  110. package/src/prompts/tools/gh-pr-checkout.md +0 -12
  111. package/src/prompts/tools/gh-pr-diff.md +0 -12
  112. package/src/prompts/tools/gh-pr-push.md +0 -12
  113. package/src/prompts/tools/gh-pr-view.md +0 -11
  114. package/src/prompts/tools/gh-repo-view.md +0 -11
  115. package/src/prompts/tools/gh-run-watch.md +0 -12
  116. package/src/prompts/tools/gh-search-issues.md +0 -11
  117. package/src/prompts/tools/gh-search-prs.md +0 -11
@@ -15,7 +15,6 @@ import { createFileRecorder, formatResultPath } from "./file-recorder";
15
15
  import { formatMatchLine } from "./match-line-format";
16
16
  import type { OutputMeta } from "./output-meta";
17
17
  import {
18
- combineSearchGlobs,
19
18
  hasGlobPathChars,
20
19
  normalizePathLikeInput,
21
20
  parseSearchPath,
@@ -24,6 +23,7 @@ import {
24
23
  } from "./path-utils";
25
24
  import {
26
25
  dedupeParseErrors,
26
+ formatCodeFrameLine,
27
27
  formatCount,
28
28
  formatEmptyMessage,
29
29
  formatErrorMessage,
@@ -35,14 +35,12 @@ import { ToolError } from "./tool-errors";
35
35
  import { toolResult } from "./tool-result";
36
36
 
37
37
  const astGrepSchema = Type.Object({
38
- pat: Type.Array(Type.String(), { minItems: 1, description: "AST patterns to match" }),
39
- lang: Type.Optional(Type.String({ description: "Language override" })),
40
- path: Type.Optional(Type.String({ description: "File, directory, or glob pattern to search (default: cwd)" })),
41
- glob: Type.Optional(Type.String({ description: "Optional glob filter relative to path" })),
42
- sel: Type.Optional(Type.String({ description: "Optional selector for contextual pattern mode" })),
43
- limit: Type.Optional(Type.Number({ description: "Max matches", default: 50 })),
44
- offset: Type.Optional(Type.Number({ description: "Skip first N matches", default: 0 })),
45
- context: Type.Optional(Type.Number({ description: "Context lines around each match" })),
38
+ pat: Type.String({ description: "ast pattern", examples: ["console.log($$$)"] }),
39
+ path: Type.String({
40
+ description: "file, directory, glob, or comma-separated paths to search",
41
+ examples: ["src/", "src/foo.ts", "src/**/*.ts"],
42
+ }),
43
+ skip: Type.Optional(Type.Number({ description: "matches to skip", default: 0 })),
46
44
  });
47
45
 
48
46
  export interface AstGrepToolDetails {
@@ -55,6 +53,9 @@ export interface AstGrepToolDetails {
55
53
  files?: string[];
56
54
  fileMatches?: Array<{ path: string; count: number }>;
57
55
  meta?: OutputMeta;
56
+ /** Pre-formatted text for the user-visible TUI render. Mirrors `result.text` lines but uses
57
+ * a `│` gutter and `*` to mark match lines. The TUI uses this directly so it never parses model-facing text. */
58
+ displayContent?: string;
58
59
  }
59
60
 
60
61
  export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolDetails> {
@@ -76,55 +77,48 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
76
77
  _context?: AgentToolContext,
77
78
  ): Promise<AgentToolResult<AstGrepToolDetails>> {
78
79
  return untilAborted(signal, async () => {
79
- const patterns = [...new Set(params.pat.map(pattern => pattern.trim()).filter(pattern => pattern.length > 0))];
80
- if (patterns.length === 0) {
81
- throw new ToolError("`pat` must include at least one non-empty pattern");
80
+ const pattern = params.pat.trim();
81
+ if (pattern.length === 0) {
82
+ throw new ToolError("`pat` must be a non-empty pattern");
82
83
  }
83
- const limit = params.limit === undefined ? 50 : Math.floor(params.limit);
84
- if (!Number.isFinite(limit) || limit < 1) {
85
- throw new ToolError("Limit must be a positive number");
84
+ const patterns = [pattern];
85
+ const skip = params.skip === undefined ? 0 : Math.floor(params.skip);
86
+ if (!Number.isFinite(skip) || skip < 0) {
87
+ throw new ToolError("skip must be a non-negative number");
86
88
  }
87
- const offset = params.offset === undefined ? 0 : Math.floor(params.offset);
88
- if (!Number.isFinite(offset) || offset < 0) {
89
- throw new ToolError("Offset must be a non-negative number");
90
- }
91
- const context = params.context === undefined ? undefined : Math.floor(params.context);
92
- if (context !== undefined && (!Number.isFinite(context) || context < 0)) {
93
- throw new ToolError("Context must be a non-negative number");
94
- }
95
-
96
89
  const formatScopePath = (targetPath: string): string => {
97
90
  const relative = path.relative(this.session.cwd, targetPath).replace(/\\/g, "/");
98
91
  return relative.length === 0 ? "." : relative;
99
92
  };
100
93
  let searchPath: string | undefined;
101
94
  let scopePath: string | undefined;
102
- let globFilter = params.glob ? normalizePathLikeInput(params.glob) || undefined : undefined;
103
- const rawPath = params.path ? normalizePathLikeInput(params.path) || undefined : undefined;
104
- if (rawPath) {
105
- const internalRouter = this.session.internalRouter;
106
- if (internalRouter?.canHandle(rawPath)) {
107
- if (hasGlobPathChars(rawPath)) {
108
- throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
109
- }
110
- const resource = await internalRouter.resolve(rawPath);
111
- if (!resource.sourcePath) {
112
- throw new ToolError(`Cannot search internal URL without backing file: ${rawPath}`);
113
- }
114
- searchPath = resource.sourcePath;
115
- scopePath = formatScopePath(searchPath);
95
+ let globFilter: string | undefined;
96
+ const rawPath = normalizePathLikeInput(params.path);
97
+ if (rawPath.length === 0) {
98
+ throw new ToolError("`path` must be a non-empty path or glob");
99
+ }
100
+ const internalRouter = this.session.internalRouter;
101
+ if (internalRouter?.canHandle(rawPath)) {
102
+ if (hasGlobPathChars(rawPath)) {
103
+ throw new ToolError(`Glob patterns are not supported for internal URLs: ${rawPath}`);
104
+ }
105
+ const resource = await internalRouter.resolve(rawPath);
106
+ if (!resource.sourcePath) {
107
+ throw new ToolError(`Cannot search internal URL without backing file: ${rawPath}`);
108
+ }
109
+ searchPath = resource.sourcePath;
110
+ scopePath = formatScopePath(searchPath);
111
+ } else {
112
+ const multiSearchPath = await resolveMultiSearchPath(rawPath, this.session.cwd, globFilter);
113
+ if (multiSearchPath) {
114
+ searchPath = multiSearchPath.basePath;
115
+ globFilter = multiSearchPath.glob;
116
+ scopePath = multiSearchPath.scopePath;
116
117
  } else {
117
- const multiSearchPath = await resolveMultiSearchPath(rawPath, this.session.cwd, globFilter);
118
- if (multiSearchPath) {
119
- searchPath = multiSearchPath.basePath;
120
- globFilter = multiSearchPath.glob;
121
- scopePath = multiSearchPath.scopePath;
122
- } else {
123
- const parsedPath = parseSearchPath(rawPath);
124
- searchPath = resolveToCwd(parsedPath.basePath, this.session.cwd);
125
- globFilter = combineSearchGlobs(parsedPath.glob, globFilter);
126
- scopePath = formatScopePath(searchPath);
127
- }
118
+ const parsedPath = parseSearchPath(rawPath);
119
+ searchPath = resolveToCwd(parsedPath.basePath, this.session.cwd);
120
+ globFilter = parsedPath.glob;
121
+ scopePath = formatScopePath(searchPath);
128
122
  }
129
123
  }
130
124
 
@@ -140,13 +134,9 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
140
134
 
141
135
  const result = await astGrep({
142
136
  patterns,
143
- lang: params.lang?.trim(),
144
137
  path: resolvedSearchPath,
145
138
  glob: globFilter,
146
- selector: params.sel?.trim(),
147
- limit,
148
- offset,
149
- context,
139
+ offset: skip,
150
140
  includeMeta: true,
151
141
  signal,
152
142
  });
@@ -183,7 +173,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
183
173
 
184
174
  if (result.matches.length === 0) {
185
175
  const noMatchMessage = dedupedParseErrors.length
186
- ? "No matches found. Parse issues mean the query may be mis-scoped; narrow `path`/`glob` or set `lang` before concluding absence."
176
+ ? "No matches found. Parse issues mean the query may be mis-scoped; narrow `path` before concluding absence."
187
177
  : "No matches found";
188
178
  const parseMessage = dedupedParseErrors.length
189
179
  ? `\n${formatParseErrors(dedupedParseErrors).join("\n")}`
@@ -193,16 +183,22 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
193
183
 
194
184
  const useHashLines = resolveFileDisplayMode(this.session).hashLines;
195
185
  const outputLines: string[] = [];
186
+ const displayLines: string[] = [];
196
187
  const renderMatchesForFile = (relativePath: string) => {
197
188
  const fileMatches = matchesByFile.get(relativePath) ?? [];
189
+ const lineNumberWidth = fileMatches.reduce((width, match) => {
190
+ const lineCount = match.text.split("\n").length;
191
+ const endLine = match.startLine + lineCount - 1;
192
+ return Math.max(width, String(match.startLine).length, String(endLine).length);
193
+ }, 0);
198
194
  for (const match of fileMatches) {
199
195
  const matchLines = match.text.split("\n");
200
- const lineNumbers = matchLines.map((_, index) => match.startLine + index);
201
- const lineWidth = Math.max(...lineNumbers.map(value => value.toString().length));
202
- const formatLine = (lineNumber: number, line: string, isMatch: boolean): string =>
203
- formatMatchLine(lineNumber, line, isMatch, { useHashLines, lineWidth });
204
196
  for (let index = 0; index < matchLines.length; index++) {
205
- outputLines.push(formatLine(match.startLine + index, matchLines[index], index === 0));
197
+ const lineNumber = match.startLine + index;
198
+ const isMatch = index === 0;
199
+ const line = matchLines[index] ?? "";
200
+ outputLines.push(formatMatchLine(lineNumber, line, isMatch, { useHashLines }));
201
+ displayLines.push(formatCodeFrameLine(isMatch ? "*" : " ", lineNumber, line, lineNumberWidth));
206
202
  }
207
203
  if (match.metaVariables && Object.keys(match.metaVariables).length > 0) {
208
204
  const serializedMeta = Object.entries(match.metaVariables)
@@ -210,6 +206,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
210
206
  .map(([key, value]) => `${key}=${value}`)
211
207
  .join(", ");
212
208
  outputLines.push(` meta: ${serializedMeta}`);
209
+ displayLines.push(` meta: ${serializedMeta}`);
213
210
  }
214
211
  fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
215
212
  }
@@ -229,18 +226,26 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
229
226
  for (const relativePath of directoryFiles) {
230
227
  if (outputLines.length > 0) {
231
228
  outputLines.push("");
229
+ displayLines.push("");
232
230
  }
233
- outputLines.push(`# ${path.basename(relativePath)}`);
231
+ const header = `# ${path.basename(relativePath)}`;
232
+ outputLines.push(header);
233
+ displayLines.push(header);
234
234
  renderMatchesForFile(relativePath);
235
235
  }
236
236
  continue;
237
237
  }
238
238
  if (outputLines.length > 0) {
239
239
  outputLines.push("");
240
+ displayLines.push("");
240
241
  }
241
- outputLines.push(`# ${directory}`);
242
+ const dirHeader = `# ${directory}`;
243
+ outputLines.push(dirHeader);
244
+ displayLines.push(dirHeader);
242
245
  for (const relativePath of directoryFiles) {
243
- outputLines.push(`## └─ ${path.basename(relativePath)}`);
246
+ const fileHeader = `## └─ ${path.basename(relativePath)}`;
247
+ outputLines.push(fileHeader);
248
+ displayLines.push(fileHeader);
244
249
  renderMatchesForFile(relativePath);
245
250
  }
246
251
  }
@@ -256,6 +261,7 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
256
261
  path: filePath,
257
262
  count: fileMatchCounts.get(filePath) ?? 0,
258
263
  })),
264
+ displayContent: displayLines.join("\n"),
259
265
  };
260
266
  if (result.limitReached) {
261
267
  outputLines.push("", "Result limit reached; narrow path pattern or increase limit.");
@@ -274,13 +280,9 @@ export class AstGrepTool implements AgentTool<typeof astGrepSchema, AstGrepToolD
274
280
  // =============================================================================
275
281
 
276
282
  interface AstGrepRenderArgs {
277
- pat?: string[];
278
- lang?: string;
283
+ pat?: string;
279
284
  path?: string;
280
- sel?: string;
281
- limit?: number;
282
- offset?: number;
283
- context?: number;
285
+ skip?: number;
284
286
  }
285
287
 
286
288
  const COLLAPSED_MATCH_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
@@ -289,15 +291,10 @@ export const astGrepToolRenderer = {
289
291
  inline: true,
290
292
  renderCall(args: AstGrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
291
293
  const meta: string[] = [];
292
- if (args.lang) meta.push(`lang:${args.lang}`);
293
294
  if (args.path) meta.push(`in ${args.path}`);
294
- if (args.sel) meta.push("selector");
295
- if (args.limit !== undefined && args.limit > 0) meta.push(`limit:${args.limit}`);
296
- if (args.offset !== undefined && args.offset > 0) meta.push(`offset:${args.offset}`);
297
- if (args.context !== undefined) meta.push(`context:${args.context}`);
298
- if (args.pat && args.pat.length > 1) meta.push(`${args.pat.length} patterns`);
295
+ if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
299
296
 
300
- const description = args.pat?.length === 1 ? args.pat[0] : args.pat ? `${args.pat.length} patterns` : "?";
297
+ const description = args.pat ?? "?";
301
298
  const text = renderStatusLine({ icon: "pending", title: "AST Grep", description, meta }, uiTheme);
302
299
  return new Text(text, 0, 0);
303
300
  },
@@ -321,19 +318,14 @@ export const astGrepToolRenderer = {
321
318
  const limitReached = details?.limitReached ?? false;
322
319
 
323
320
  if (matchCount === 0) {
324
- const description = args?.pat?.length === 1 ? args.pat[0] : undefined;
321
+ const description = args?.pat;
325
322
  const meta = ["0 matches"];
326
323
  if (details?.scopePath) meta.push(`in ${details.scopePath}`);
327
324
  if (filesSearched > 0) meta.push(`searched ${filesSearched}`);
328
325
  const header = renderStatusLine({ icon: "warning", title: "AST Grep", description, meta }, uiTheme);
329
326
  const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
330
327
  if (details?.parseErrors?.length) {
331
- lines.push(
332
- uiTheme.fg(
333
- "warning",
334
- "Query may be mis-scoped; narrow `path`/`glob` or set `lang` before concluding absence",
335
- ),
336
- );
328
+ lines.push(uiTheme.fg("warning", "Query may be mis-scoped; narrow `path` before concluding absence"));
337
329
  const capped = details.parseErrors.slice(0, PARSE_ERRORS_LIMIT);
338
330
  for (const err of capped) {
339
331
  lines.push(uiTheme.fg("warning", ` - ${err}`));
@@ -350,13 +342,13 @@ export const astGrepToolRenderer = {
350
342
  if (details?.scopePath) meta.push(`in ${details.scopePath}`);
351
343
  meta.push(`searched ${filesSearched}`);
352
344
  if (limitReached) meta.push(uiTheme.fg("warning", "limit reached"));
353
- const description = args?.pat?.length === 1 ? args.pat[0] : undefined;
345
+ const description = args?.pat;
354
346
  const header = renderStatusLine(
355
347
  { icon: limitReached ? "warning" : "success", title: "AST Grep", description, meta },
356
348
  uiTheme,
357
349
  );
358
350
 
359
- const textContent = result.content?.find(c => c.type === "text")?.text ?? "";
351
+ const textContent = result.details?.displayContent ?? result.content?.find(c => c.type === "text")?.text ?? "";
360
352
  const rawLines = textContent.split("\n");
361
353
  const hasSeparators = rawLines.some(line => line.trim().length === 0);
362
354
  const allGroups: string[][] = [];
package/src/tools/bash.ts CHANGED
@@ -42,20 +42,19 @@ async function saveBashOriginalArtifact(session: ToolSession, originalText: stri
42
42
  }
43
43
 
44
44
  const bashSchemaBase = Type.Object({
45
- command: Type.String({ description: "Command to execute" }),
45
+ command: Type.String({ description: "command to execute", examples: ["ls -la", "echo hi"] }),
46
46
  env: Type.Optional(
47
47
  Type.Record(Type.String({ pattern: BASH_ENV_NAME_PATTERN.source }), Type.String(), {
48
- description:
49
- "Additional environment variables passed to the command and rendered inline as shell assignments; prefer this for multiline or quote-heavy content",
48
+ description: "extra env vars",
50
49
  }),
51
50
  ),
52
- timeout: Type.Optional(Type.Number({ description: "Timeout in seconds", default: 300 })),
53
- cwd: Type.Optional(Type.String({ description: "Working directory (default: cwd)" })),
54
- head: Type.Optional(Type.Number({ description: "Return only first N lines of output" })),
55
- tail: Type.Optional(Type.Number({ description: "Return only last N lines of output" })),
51
+ timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 300 })),
52
+ cwd: Type.Optional(Type.String({ description: "working directory", examples: ["src/", "/tmp"] })),
53
+ head: Type.Optional(Type.Number({ description: "first n lines of output" })),
54
+ tail: Type.Optional(Type.Number({ description: "last n lines of output" })),
56
55
  pty: Type.Optional(
57
56
  Type.Boolean({
58
- description: "Run in PTY mode when command needs a real terminal (e.g. sudo/ssh/top/less); default: false",
57
+ description: "run in pty mode",
59
58
  }),
60
59
  ),
61
60
  });
@@ -64,7 +63,7 @@ const bashSchemaWithAsync = Type.Object({
64
63
  ...bashSchemaBase.properties,
65
64
  async: Type.Optional(
66
65
  Type.Boolean({
67
- description: "Run in background; returns immediately with a job ID. Result delivered as follow-up.",
66
+ description: "run in background",
68
67
  }),
69
68
  ),
70
69
  });
@@ -361,12 +361,12 @@ function resolvePageClient(page: Page): PuppeteerCdpClient | null {
361
361
  const puppeteerGetArgsSchema = Type.Array(
362
362
  Type.Object({
363
363
  selector: Type.String({
364
- description:
365
- "Selector for the target element (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
364
+ description: "target element selector",
365
+ examples: ["aria/Sign in", "text/Continue", "xpath/...", "pierce/..."],
366
366
  }),
367
- attribute: Type.Optional(Type.String({ description: "Attribute name (get_attribute)" })),
367
+ attribute: Type.Optional(Type.String({ description: "attribute name", examples: ["href", "data-id"] })),
368
368
  }),
369
- { description: "Batch arguments for get_* actions", minItems: 1 },
369
+ { description: "batch get_* args", minItems: 1 },
370
370
  );
371
371
 
372
372
  const browserSchema = Type.Object({
@@ -393,56 +393,58 @@ const browserSchema = Type.Object({
393
393
  "screenshot",
394
394
  "close",
395
395
  ],
396
- { description: "Action to perform" },
396
+ { description: "action to perform" },
397
397
  ),
398
- url: Type.Optional(Type.String({ description: "URL to navigate to (goto)" })),
398
+ url: Type.Optional(Type.String({ description: "url to navigate to", examples: ["https://example.com"] })),
399
399
  selector: Type.Optional(
400
400
  Type.String({
401
- description:
402
- "Selector for the target element (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
401
+ description: "target element selector",
402
+ examples: ["aria/Sign in", "text/Continue", "xpath/...", "pierce/..."],
403
403
  }),
404
404
  ),
405
- element_id: Type.Optional(Type.Number({ description: "Element ID from observe" })),
406
- include_all: Type.Optional(Type.Boolean({ description: "Include non-interactive nodes in observe" })),
407
- viewport_only: Type.Optional(Type.Boolean({ description: "Limit observe output to elements in the viewport" })),
405
+ element_id: Type.Optional(Type.Number({ description: "observed element id" })),
406
+ include_all: Type.Optional(Type.Boolean({ description: "include non-interactive nodes" })),
407
+ viewport_only: Type.Optional(Type.Boolean({ description: "limit to viewport" })),
408
408
  args: Type.Optional(puppeteerGetArgsSchema),
409
- script: Type.Optional(Type.String({ description: "JavaScript to evaluate (evaluate)" })),
410
- text: Type.Optional(Type.String({ description: "Text to type (type)" })),
411
- value: Type.Optional(Type.String({ description: "Value to set (fill)" })),
412
- attribute: Type.Optional(Type.String({ description: "Attribute name to read (get_attribute)" })),
413
- key: Type.Optional(Type.String({ description: "Keyboard key to press (press)" })),
414
- timeout: Type.Optional(Type.Number({ description: "Timeout in seconds", default: 30 })),
409
+ script: Type.Optional(
410
+ Type.String({ description: "javascript expression", examples: ["document.title", "window.location.href"] }),
411
+ ),
412
+ text: Type.Optional(Type.String({ description: "text to type", examples: ["hello world"] })),
413
+ value: Type.Optional(Type.String({ description: "value to set", examples: ["hello"] })),
414
+ attribute: Type.Optional(Type.String({ description: "attribute to read", examples: ["href", "data-id"] })),
415
+ key: Type.Optional(Type.String({ description: "keyboard key", examples: ["Enter", "Tab", "Escape"] })),
416
+ timeout: Type.Optional(Type.Number({ description: "timeout in seconds", default: 30 })),
415
417
  wait_until: Type.Optional(
416
418
  StringEnum(["load", "domcontentloaded", "networkidle0", "networkidle2"], {
417
- description: "Navigation wait condition (goto)",
419
+ description: "navigation wait condition",
418
420
  }),
419
421
  ),
420
- full_page: Type.Optional(Type.Boolean({ description: "Capture full page screenshot (screenshot)" })),
422
+ full_page: Type.Optional(Type.Boolean({ description: "full page screenshot" })),
421
423
  format: Type.Optional(
422
424
  StringEnum(["text", "markdown"], {
423
- description: "Output format for extract_readable (text/markdown)",
425
+ description: "output format",
424
426
  }),
425
427
  ),
426
- path: Type.Optional(Type.String({ description: "Optional path to save screenshot (relative to cwd)" })),
428
+ path: Type.Optional(Type.String({ description: "screenshot save path", examples: ["out.png"] })),
427
429
  viewport: Type.Optional(
428
430
  Type.Object({
429
- width: Type.Number({ description: "Viewport width in pixels" }),
430
- height: Type.Number({ description: "Viewport height in pixels" }),
431
- device_scale_factor: Type.Optional(Type.Number({ description: "Device scale factor" })),
431
+ width: Type.Number({ description: "viewport width" }),
432
+ height: Type.Number({ description: "viewport height" }),
433
+ device_scale_factor: Type.Optional(Type.Number({ description: "device scale factor" })),
432
434
  }),
433
435
  ),
434
- delta_x: Type.Optional(Type.Number({ description: "Scroll delta X (scroll)" })),
435
- delta_y: Type.Optional(Type.Number({ description: "Scroll delta Y (scroll)" })),
436
+ delta_x: Type.Optional(Type.Number({ description: "scroll delta x" })),
437
+ delta_y: Type.Optional(Type.Number({ description: "scroll delta y" })),
436
438
  from_selector: Type.Optional(
437
439
  Type.String({
438
- description:
439
- "Drag start selector (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
440
+ description: "drag start selector",
441
+ examples: ["aria/Drag handle"],
440
442
  }),
441
443
  ),
442
444
  to_selector: Type.Optional(
443
445
  Type.String({
444
- description:
445
- "Drag end selector (CSS, or puppeteer query handler like aria/, text/, xpath/, pierce/; also accepts legacy p- prefixes)",
446
+ description: "drag end selector",
447
+ examples: ["text/Drop zone"],
446
448
  }),
447
449
  ),
448
450
  });
@@ -31,11 +31,11 @@ type Token =
31
31
  const calculatorSchema = Type.Object({
32
32
  calculations: Type.Array(
33
33
  Type.Object({
34
- expression: Type.String({ description: "Math expression to evaluate" }),
35
- prefix: Type.String({ description: "Text to prepend to the result" }),
36
- suffix: Type.String({ description: "Text to append to the result" }),
34
+ expression: Type.String({ description: "math expression", examples: ["2 + 2", "sqrt(16)"] }),
35
+ prefix: Type.String({ description: "prefix text" }),
36
+ suffix: Type.String({ description: "suffix text" }),
37
37
  }),
38
- { description: "List of calculations to evaluate" },
38
+ { description: "calculations to evaluate" },
39
39
  ),
40
40
  });
41
41
 
@@ -6,7 +6,7 @@ import cancelJobDescription from "../prompts/tools/cancel-job.md" with { type: "
6
6
  import type { ToolSession } from "./index";
7
7
 
8
8
  const cancelJobSchema = Type.Object({
9
- job_id: Type.String({ description: "Background job ID" }),
9
+ job_id: Type.String({ description: "background job id", examples: ["job-1234"] }),
10
10
  });
11
11
 
12
12
  type CancelJobParams = Static<typeof cancelJobSchema>;
@@ -18,13 +18,13 @@ export interface CheckpointState {
18
18
  }
19
19
 
20
20
  const checkpointSchema = Type.Object({
21
- goal: Type.String({ description: "What you are investigating and why" }),
21
+ goal: Type.String({ description: "investigation goal", examples: ["investigate retry logic"] }),
22
22
  });
23
23
 
24
24
  type CheckpointParams = Static<typeof checkpointSchema>;
25
25
 
26
26
  const rewindSchema = Type.Object({
27
- report: Type.String({ description: "Concise investigation findings to retain after rewind" }),
27
+ report: Type.String({ description: "investigation findings" }),
28
28
  });
29
29
 
30
30
  type RewindParams = Static<typeof rewindSchema>;
@@ -83,51 +83,55 @@ const debugSchema = Type.Object({
83
83
  "terminate",
84
84
  "sessions",
85
85
  ],
86
- { description: "DAP debugger action" },
86
+ { description: "dap debugger action" },
87
87
  ),
88
- program: Type.Optional(Type.String({ description: "Program or script path for launch" })),
89
- args: Type.Optional(Type.Array(Type.String(), { description: "Program arguments for launch" })),
90
- adapter: Type.Optional(Type.String({ description: "Debugger adapter override (gdb, lldb-dap, debugpy, dlv)" })),
91
- cwd: Type.Optional(Type.String({ description: "Working directory for launch or attach" })),
92
- file: Type.Optional(Type.String({ description: "Source file for source breakpoints" })),
93
- line: Type.Optional(Type.Number({ description: "1-indexed source line for source breakpoints" })),
94
- function: Type.Optional(Type.String({ description: "Function name for function breakpoints" })),
95
- name: Type.Optional(Type.String({ description: "Variable or data name for data breakpoint info" })),
96
- condition: Type.Optional(Type.String({ description: "Breakpoint condition expression" })),
97
- hit_condition: Type.Optional(Type.String({ description: "Breakpoint hit condition expression" })),
98
- expression: Type.Optional(Type.String({ description: "Expression to evaluate in debugger context" })),
99
- context: Type.Optional(Type.String({ description: "Evaluate context (watch, repl, hover, variables, clipboard)" })),
100
- frame_id: Type.Optional(Type.Number({ description: "Stack frame ID for scopes/evaluate" })),
101
- scope_id: Type.Optional(Type.Number({ description: "Scope variablesReference for variables requests" })),
102
- variable_ref: Type.Optional(Type.Number({ description: "Variable reference for variables requests" })),
103
- pid: Type.Optional(Type.Number({ description: "Process ID for attach" })),
104
- port: Type.Optional(Type.Number({ description: "Port for remote attach when adapter supports it" })),
105
- host: Type.Optional(Type.String({ description: "Host for remote attach when adapter supports it" })),
106
- levels: Type.Optional(Type.Number({ description: "Maximum stack frames to fetch" })),
107
- memory_reference: Type.Optional(Type.String({ description: "Memory reference or address" })),
108
- instruction_reference: Type.Optional(
109
- Type.String({ description: "Instruction address/reference for instruction breakpoints" }),
88
+ program: Type.Optional(Type.String({ description: "program path", examples: ["./my_app", "src/main.py"] })),
89
+ args: Type.Optional(Type.Array(Type.String(), { description: "program arguments", examples: [["--verbose"]] })),
90
+ adapter: Type.Optional(
91
+ Type.String({ description: "debugger adapter", examples: ["gdb", "lldb-dap", "debugpy", "dlv"] }),
110
92
  ),
111
- instruction_count: Type.Optional(Type.Number({ description: "Number of instructions to disassemble" })),
112
- instruction_offset: Type.Optional(Type.Number({ description: "Instruction offset for disassembly" })),
113
- count: Type.Optional(Type.Number({ description: "Number of bytes to read from memory" })),
114
- data: Type.Optional(Type.String({ description: "Base64-encoded memory payload for write_memory" })),
115
- data_id: Type.Optional(Type.String({ description: "DAP data breakpoint identifier" })),
93
+ cwd: Type.Optional(Type.String({ description: "working directory", examples: ["src/"] })),
94
+ file: Type.Optional(Type.String({ description: "source file", examples: ["src/main.c"] })),
95
+ line: Type.Optional(Type.Number({ description: "source line", examples: [42] })),
96
+ function: Type.Optional(Type.String({ description: "function name", examples: ["main", "handle_request"] })),
97
+ name: Type.Optional(Type.String({ description: "variable or data name", examples: ["counter", "buffer"] })),
98
+ condition: Type.Optional(Type.String({ description: "breakpoint condition", examples: ["i == 10", "x > 0"] })),
99
+ hit_condition: Type.Optional(Type.String({ description: "hit condition" })),
100
+ expression: Type.Optional(Type.String({ description: "expression to evaluate", examples: ["x + 1", "obj.field"] })),
101
+ context: Type.Optional(
102
+ Type.String({ description: "evaluate context", examples: ["watch", "repl", "hover", "variables", "clipboard"] }),
103
+ ),
104
+ frame_id: Type.Optional(Type.Number({ description: "stack frame id" })),
105
+ scope_id: Type.Optional(Type.Number({ description: "scope variables reference" })),
106
+ variable_ref: Type.Optional(Type.Number({ description: "variable reference" })),
107
+ pid: Type.Optional(Type.Number({ description: "process id for attach", examples: [12345] })),
108
+ port: Type.Optional(Type.Number({ description: "remote attach port", examples: [4711] })),
109
+ host: Type.Optional(Type.String({ description: "remote attach host", examples: ["127.0.0.1"] })),
110
+ levels: Type.Optional(Type.Number({ description: "max stack frames" })),
111
+ memory_reference: Type.Optional(
112
+ Type.String({ description: "memory reference or address", examples: ["0x7ffd1234"] }),
113
+ ),
114
+ instruction_reference: Type.Optional(Type.String({ description: "instruction address or reference" })),
115
+ instruction_count: Type.Optional(Type.Number({ description: "instructions to disassemble" })),
116
+ instruction_offset: Type.Optional(Type.Number({ description: "instruction offset" })),
117
+ count: Type.Optional(Type.Number({ description: "bytes to read" })),
118
+ data: Type.Optional(Type.String({ description: "base64 memory payload" })),
119
+ data_id: Type.Optional(Type.String({ description: "data breakpoint id" })),
116
120
  access_type: Type.Optional(
117
- StringEnum(["read", "write", "readWrite"], { description: "Data breakpoint access type" }),
121
+ StringEnum(["read", "write", "readWrite"], { description: "data breakpoint access type" }),
118
122
  ),
119
- command: Type.Optional(Type.String({ description: "Custom DAP request command" })),
123
+ command: Type.Optional(Type.String({ description: "custom dap request command" })),
120
124
  arguments: Type.Optional(
121
125
  Type.Record(Type.String(), Type.Any(), {
122
- description: "Arguments object for custom_request",
126
+ description: "custom request arguments",
123
127
  }),
124
128
  ),
125
- offset: Type.Optional(Type.Number({ description: "Memory or instruction offset" })),
126
- resolve_symbols: Type.Optional(Type.Boolean({ description: "Resolve symbols during disassembly" })),
127
- allow_partial: Type.Optional(Type.Boolean({ description: "Allow partial writes for write_memory" })),
128
- start_module: Type.Optional(Type.Number({ description: "Modules request start index" })),
129
- module_count: Type.Optional(Type.Number({ description: "Maximum modules to fetch" })),
130
- timeout: Type.Optional(Type.Number({ description: "Per-request timeout in seconds" })),
129
+ offset: Type.Optional(Type.Number({ description: "memory or instruction offset" })),
130
+ resolve_symbols: Type.Optional(Type.Boolean({ description: "resolve symbols during disassembly" })),
131
+ allow_partial: Type.Optional(Type.Boolean({ description: "allow partial writes" })),
132
+ start_module: Type.Optional(Type.Number({ description: "modules start index" })),
133
+ module_count: Type.Optional(Type.Number({ description: "max modules to fetch" })),
134
+ timeout: Type.Optional(Type.Number({ description: "per-request timeout seconds" })),
131
135
  });
132
136
 
133
137
  export type DebugParams = Static<typeof debugSchema>;
@@ -8,7 +8,7 @@ import { resolvePlanPath } from "./plan-mode-guard";
8
8
  import { ToolError } from "./tool-errors";
9
9
 
10
10
  const exitPlanModeSchema = Type.Object({
11
- title: Type.String({ description: "Final plan title, e.g. WP_MIGRATION_PLAN" }),
11
+ title: Type.String({ description: "final plan title", examples: ["WP_MIGRATION_PLAN"] }),
12
12
  });
13
13
 
14
14
  type ExitPlanModeParams = Static<typeof exitPlanModeSchema>;
package/src/tools/find.ts CHANGED
@@ -30,11 +30,11 @@ import { toolResult } from "./tool-result";
30
30
 
31
31
  const findSchema = Type.Object({
32
32
  pattern: Type.String({
33
- description:
34
- "Glob pattern including the search path (no separate path param), e.g. 'src/**/*.ts', 'lib/*.json'. Supports comma-separated lists like 'apps/,packages/,phases/'. Simple patterns like '*.ts' recurse from cwd.",
33
+ description: "glob including search path",
34
+ examples: ["src/**/*.ts", "lib/*.json", "apps/,packages/", "*.ts"],
35
35
  }),
36
- hidden: Type.Optional(Type.Boolean({ description: "Include hidden files and directories", default: true })),
37
- limit: Type.Optional(Type.Number({ description: "Max results", default: 1000 })),
36
+ hidden: Type.Optional(Type.Boolean({ description: "include hidden files", default: true })),
37
+ limit: Type.Optional(Type.Number({ description: "max results", default: 1000 })),
38
38
  });
39
39
 
40
40
  export type FindToolInput = Static<typeof findSchema>;