@oh-my-pi/pi-coding-agent 14.5.3 → 14.5.5

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 (68) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/examples/extensions/plan-mode.ts +1 -1
  3. package/examples/sdk/README.md +1 -1
  4. package/package.json +7 -7
  5. package/src/config/prompt-templates.ts +103 -8
  6. package/src/config/settings-schema.ts +14 -13
  7. package/src/config/settings.ts +1 -1
  8. package/src/cursor.ts +4 -4
  9. package/src/edit/index.ts +111 -109
  10. package/src/edit/line-hash.ts +33 -3
  11. package/src/edit/modes/apply-patch.ts +6 -4
  12. package/src/edit/modes/atom.lark +27 -0
  13. package/src/edit/modes/atom.ts +1057 -841
  14. package/src/edit/modes/hashline.ts +9 -10
  15. package/src/edit/modes/patch.ts +23 -19
  16. package/src/edit/modes/replace.ts +19 -15
  17. package/src/edit/renderer.ts +65 -8
  18. package/src/edit/streaming.ts +47 -77
  19. package/src/extensibility/extensions/types.ts +11 -11
  20. package/src/extensibility/hooks/types.ts +6 -6
  21. package/src/lsp/edits.ts +8 -5
  22. package/src/lsp/index.ts +4 -4
  23. package/src/lsp/utils.ts +7 -7
  24. package/src/mcp/discoverable-tool-metadata.ts +1 -1
  25. package/src/mcp/manager.ts +3 -3
  26. package/src/mcp/tool-bridge.ts +4 -4
  27. package/src/memories/index.ts +1 -1
  28. package/src/modes/acp/acp-event-mapper.ts +1 -1
  29. package/src/modes/components/session-observer-overlay.ts +1 -1
  30. package/src/modes/components/settings-defs.ts +3 -3
  31. package/src/modes/components/tree-selector.ts +2 -2
  32. package/src/modes/utils/ui-helpers.ts +31 -7
  33. package/src/prompts/agents/explore.md +1 -1
  34. package/src/prompts/agents/librarian.md +2 -2
  35. package/src/prompts/agents/plan.md +2 -2
  36. package/src/prompts/agents/reviewer.md +1 -1
  37. package/src/prompts/agents/task.md +2 -2
  38. package/src/prompts/system/plan-mode-active.md +1 -1
  39. package/src/prompts/system/system-prompt.md +34 -31
  40. package/src/prompts/tools/apply-patch.md +0 -2
  41. package/src/prompts/tools/atom.md +81 -63
  42. package/src/prompts/tools/bash.md +7 -4
  43. package/src/prompts/tools/checkpoint.md +1 -1
  44. package/src/prompts/tools/find.md +6 -1
  45. package/src/prompts/tools/hashline.md +10 -11
  46. package/src/prompts/tools/patch.md +13 -13
  47. package/src/prompts/tools/read.md +4 -4
  48. package/src/prompts/tools/replace.md +3 -3
  49. package/src/prompts/tools/{grep.md → search.md} +4 -4
  50. package/src/sdk.ts +19 -9
  51. package/src/session/agent-session.ts +65 -0
  52. package/src/system-prompt.ts +15 -5
  53. package/src/task/executor.ts +5 -0
  54. package/src/task/index.ts +10 -1
  55. package/src/tools/ast-edit.ts +4 -6
  56. package/src/tools/ast-grep.ts +4 -6
  57. package/src/tools/bash.ts +1 -1
  58. package/src/tools/file-recorder.ts +6 -6
  59. package/src/tools/find.ts +11 -13
  60. package/src/tools/index.ts +7 -7
  61. package/src/tools/path-utils.ts +31 -4
  62. package/src/tools/read.ts +12 -6
  63. package/src/tools/renderers.ts +2 -2
  64. package/src/tools/{grep.ts → search.ts} +32 -40
  65. package/src/tools/write.ts +8 -4
  66. package/src/web/search/index.ts +1 -1
  67. package/src/edit/block.ts +0 -308
  68. package/src/edit/indent.ts +0 -150
package/src/lsp/edits.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { formatPathRelativeToCwd } from "../tools/path-utils";
3
4
  import type { CreateFile, DeleteFile, RenameFile, TextDocumentEdit, TextEdit, WorkspaceEdit } from "./types";
4
5
  import { uriToFile } from "./utils";
5
6
 
@@ -67,7 +68,7 @@ export async function applyWorkspaceEdit(edit: WorkspaceEdit, cwd: string): Prom
67
68
  for (const [uri, textEdits] of Object.entries(edit.changes)) {
68
69
  const filePath = uriToFile(uri);
69
70
  await applyTextEdits(filePath, textEdits);
70
- applied.push(`Applied ${textEdits.length} edit(s) to ${path.relative(cwd, filePath)}`);
71
+ applied.push(`Applied ${textEdits.length} edit(s) to ${formatPathRelativeToCwd(filePath, cwd)}`);
71
72
  }
72
73
  }
73
74
 
@@ -80,26 +81,28 @@ export async function applyWorkspaceEdit(edit: WorkspaceEdit, cwd: string): Prom
80
81
  const filePath = uriToFile(docChange.textDocument.uri);
81
82
  const textEdits = docChange.edits.filter((e): e is TextEdit => "range" in e && "newText" in e);
82
83
  await applyTextEdits(filePath, textEdits);
83
- applied.push(`Applied ${textEdits.length} edit(s) to ${path.relative(cwd, filePath)}`);
84
+ applied.push(`Applied ${textEdits.length} edit(s) to ${formatPathRelativeToCwd(filePath, cwd)}`);
84
85
  } else if ("kind" in change && change.kind) {
85
86
  // Resource operations
86
87
  if (change.kind === "create") {
87
88
  const createOp = change as CreateFile;
88
89
  const filePath = uriToFile(createOp.uri);
89
90
  await Bun.write(filePath, "");
90
- applied.push(`Created ${path.relative(cwd, filePath)}`);
91
+ applied.push(`Created ${formatPathRelativeToCwd(filePath, cwd)}`);
91
92
  } else if (change.kind === "rename") {
92
93
  const renameOp = change as RenameFile;
93
94
  const oldPath = uriToFile(renameOp.oldUri);
94
95
  const newPath = uriToFile(renameOp.newUri);
95
96
  await fs.mkdir(path.dirname(newPath), { recursive: true });
96
97
  await fs.rename(oldPath, newPath);
97
- applied.push(`Renamed ${path.relative(cwd, oldPath)} → ${path.relative(cwd, newPath)}`);
98
+ applied.push(
99
+ `Renamed ${formatPathRelativeToCwd(oldPath, cwd)} → ${formatPathRelativeToCwd(newPath, cwd)}`,
100
+ );
98
101
  } else if (change.kind === "delete") {
99
102
  const deleteOp = change as DeleteFile;
100
103
  const filePath = uriToFile(deleteOp.uri);
101
104
  await fs.rm(filePath, { recursive: true });
102
- applied.push(`Deleted ${path.relative(cwd, filePath)}`);
105
+ applied.push(`Deleted ${formatPathRelativeToCwd(filePath, cwd)}`);
103
106
  }
104
107
  }
105
108
  }
package/src/lsp/index.ts CHANGED
@@ -6,7 +6,7 @@ import type { BunFile } from "bun";
6
6
  import { type Theme, theme } from "../modes/theme/theme";
7
7
  import lspDescription from "../prompts/tools/lsp.md" with { type: "text" };
8
8
  import type { ToolSession } from "../tools";
9
- import { resolveToCwd } from "../tools/path-utils";
9
+ import { formatPathRelativeToCwd, resolveToCwd } from "../tools/path-utils";
10
10
  import { ToolAbortError, throwIfAborted } from "../tools/tool-errors";
11
11
  import { clampTimeout } from "../tools/tool-timeouts";
12
12
  import {
@@ -562,7 +562,7 @@ async function getDiagnosticsForFile(
562
562
  }
563
563
 
564
564
  const uri = fileToUri(absolutePath);
565
- const relPath = path.relative(cwd, absolutePath);
565
+ const relPath = formatPathRelativeToCwd(absolutePath, cwd);
566
566
  const allDiagnostics: Diagnostic[] = [];
567
567
  const serverNames: string[] = [];
568
568
 
@@ -1229,7 +1229,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
1229
1229
  }
1230
1230
 
1231
1231
  const uri = fileToUri(resolved);
1232
- const relPath = path.relative(this.session.cwd, resolved);
1232
+ const relPath = formatPathRelativeToCwd(resolved, this.session.cwd);
1233
1233
  const allDiagnostics: Diagnostic[] = [];
1234
1234
 
1235
1235
  // Query all applicable servers for this file
@@ -1707,7 +1707,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
1707
1707
  if (!result || result.length === 0) {
1708
1708
  output = "No symbols found";
1709
1709
  } else {
1710
- const relPath = path.relative(this.session.cwd, targetFile);
1710
+ const relPath = formatPathRelativeToCwd(targetFile, this.session.cwd);
1711
1711
  if ("selectionRange" in result[0]) {
1712
1712
  const lines = (result as DocumentSymbol[]).flatMap(s => formatDocumentSymbol(s));
1713
1713
  output = `Symbols in ${relPath}:\n${lines.join("\n")}`;
package/src/lsp/utils.ts CHANGED
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { isEnoent } from "@oh-my-pi/pi-utils";
6
6
  import { type Theme, theme } from "../modes/theme/theme";
7
7
  import { formatGroupedFiles } from "../tools/grouped-file-output";
8
- import { resolveToCwd } from "../tools/path-utils";
8
+ import { formatPathRelativeToCwd, resolveToCwd } from "../tools/path-utils";
9
9
  import type {
10
10
  CodeAction,
11
11
  Command,
@@ -229,7 +229,7 @@ export function formatDiagnosticsSummary(diagnostics: Diagnostic[]): string {
229
229
  * Format a location as file:line:col relative to cwd.
230
230
  */
231
231
  export function formatLocation(location: Location, cwd: string): string {
232
- const file = path.relative(cwd, uriToFile(location.uri));
232
+ const file = formatPathRelativeToCwd(uriToFile(location.uri), cwd);
233
233
  const line = location.range.start.line + 1;
234
234
  const col = location.range.start.character + 1;
235
235
  return `${file}:${line}:${col}`;
@@ -255,7 +255,7 @@ export function formatWorkspaceEdit(edit: WorkspaceEdit, cwd: string): string[]
255
255
  // Handle changes map (legacy format)
256
256
  if (edit.changes) {
257
257
  for (const [uri, textEdits] of Object.entries(edit.changes)) {
258
- const file = path.relative(cwd, uriToFile(uri));
258
+ const file = formatPathRelativeToCwd(uriToFile(uri), cwd);
259
259
  results.push(`${file}: ${textEdits.length} edit${textEdits.length > 1 ? "s" : ""}`);
260
260
  }
261
261
  }
@@ -264,20 +264,20 @@ export function formatWorkspaceEdit(edit: WorkspaceEdit, cwd: string): string[]
264
264
  if (edit.documentChanges) {
265
265
  for (const change of edit.documentChanges) {
266
266
  if ("edits" in change && change.textDocument) {
267
- const file = path.relative(cwd, uriToFile(change.textDocument.uri));
267
+ const file = formatPathRelativeToCwd(uriToFile(change.textDocument.uri), cwd);
268
268
  results.push(`${file}: ${change.edits.length} edit${change.edits.length > 1 ? "s" : ""}`);
269
269
  } else if ("kind" in change) {
270
270
  switch (change.kind) {
271
271
  case "create":
272
- results.push(`CREATE: ${path.relative(cwd, uriToFile(change.uri))}`);
272
+ results.push(`CREATE: ${formatPathRelativeToCwd(uriToFile(change.uri), cwd)}`);
273
273
  break;
274
274
  case "rename":
275
275
  results.push(
276
- `RENAME: ${path.relative(cwd, uriToFile(change.oldUri))} ${theme.nav.cursor} ${path.relative(cwd, uriToFile(change.newUri))}`,
276
+ `RENAME: ${formatPathRelativeToCwd(uriToFile(change.oldUri), cwd)} ${theme.nav.cursor} ${formatPathRelativeToCwd(uriToFile(change.newUri), cwd)}`,
277
277
  );
278
278
  break;
279
279
  case "delete":
280
- results.push(`DELETE: ${path.relative(cwd, uriToFile(change.uri))}`);
280
+ results.push(`DELETE: ${formatPathRelativeToCwd(uriToFile(change.uri), cwd)}`);
281
281
  break;
282
282
  }
283
283
  }
@@ -53,7 +53,7 @@ const FIELD_WEIGHTS = {
53
53
  } as const;
54
54
 
55
55
  export function isMCPToolName(name: string): boolean {
56
- return name.startsWith("mcp_");
56
+ return name.startsWith("mcp__");
57
57
  }
58
58
 
59
59
  function getSchemaPropertyKeys(parameters: unknown): string[] {
@@ -472,7 +472,7 @@ export class MCPManager {
472
472
  }
473
473
 
474
474
  #replaceServerTools(name: string, tools: CustomTool<TSchema, MCPToolDetails>[]): void {
475
- this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp_${name}_`));
475
+ this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp__${name}_`));
476
476
  this.#tools.push(...tools);
477
477
  }
478
478
 
@@ -644,8 +644,8 @@ export class MCPManager {
644
644
  }
645
645
 
646
646
  // Remove tools from this server and notify consumers
647
- const hadTools = this.#tools.some(t => t.name.startsWith(`mcp_${name}_`));
648
- this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp_${name}_`));
647
+ const hadTools = this.#tools.some(t => t.name.startsWith(`mcp__${name}_`));
648
+ this.#tools = this.#tools.filter(t => !t.name.startsWith(`mcp__${name}_`));
649
649
  if (hadTools) this.#onToolsChanged?.(this.#tools);
650
650
 
651
651
  // Notify prompt consumers so stale commands are cleared
@@ -159,7 +159,7 @@ async function reconnectWithAbort(reconnect: MCPReconnect, signal?: AbortSignal)
159
159
  * Prefixes with server name to avoid conflicts. If the tool name already
160
160
  * starts with the server name (e.g., server "puppeteer" with tool
161
161
  * "puppeteer_screenshot"), strips the redundant prefix to produce
162
- * "mcp_puppeteer_screenshot" instead of "mcp_puppeteer_puppeteer_screenshot".
162
+ * "mcp__puppeteer_screenshot" instead of "mcp__puppeteer_puppeteer_screenshot".
163
163
  */
164
164
  function sanitizeMCPToolNamePart(value: string, fallback: string): string {
165
165
  const sanitized = value
@@ -183,7 +183,7 @@ export function createMCPToolName(serverName: string, toolName: string): string
183
183
  normalizedToolName = sanitizedToolName.slice(prefixWithUnderscore.length);
184
184
  }
185
185
 
186
- return `mcp_${sanitizedServerName}_${normalizedToolName}`;
186
+ return `mcp__${sanitizedServerName}_${normalizedToolName}`;
187
187
  }
188
188
 
189
189
  /**
@@ -193,9 +193,9 @@ export function createMCPToolName(serverName: string, toolName: string): string
193
193
  * The original MCP tool name may have had the server name as a prefix.
194
194
  */
195
195
  export function parseMCPToolName(name: string): { serverName: string; toolName: string } | null {
196
- if (!name.startsWith("mcp_")) return null;
196
+ if (!name.startsWith("mcp__")) return null;
197
197
 
198
- const rest = name.slice(4);
198
+ const rest = name.slice(5);
199
199
  const underscoreIdx = rest.indexOf("_");
200
200
  if (underscoreIdx === -1) return null;
201
201
 
@@ -539,7 +539,7 @@ function shouldPersistResponseItemForMemories(message: AgentMessage): boolean {
539
539
  }
540
540
  if (role !== "toolResult") return false;
541
541
  const toolName = (message as { toolName?: string }).toolName;
542
- if (toolName === "bash" || toolName === "python" || toolName === "read" || toolName === "grep") {
542
+ if (toolName === "bash" || toolName === "python" || toolName === "read" || toolName === "search") {
543
543
  const text = extractMessageText(message);
544
544
  return text.length > 0 && text.length <= 32_000;
545
545
  }
@@ -106,7 +106,7 @@ export function mapToolKind(toolName: string): ToolKind {
106
106
  case "bash":
107
107
  case "python":
108
108
  return "execute";
109
- case "grep":
109
+ case "search":
110
110
  case "find":
111
111
  case "ast_grep":
112
112
  return "search";
@@ -518,7 +518,7 @@ export class SessionObserverOverlayComponent extends Container {
518
518
  case "write":
519
519
  case "edit":
520
520
  return args.path ? `path: ${args.path}` : "";
521
- case "grep":
521
+ case "search":
522
522
  return [args.pattern ? `pattern: ${args.pattern}` : "", args.path ? `path: ${args.path}` : ""]
523
523
  .filter(Boolean)
524
524
  .join(", ");
@@ -214,15 +214,15 @@ const OPTION_PROVIDERS: Partial<Record<SettingPath, OptionProvider>> = {
214
214
  { value: "3", label: "3 reminders" },
215
215
  { value: "5", label: "5 reminders" },
216
216
  ],
217
- // Grep context
218
- "grep.contextBefore": [
217
+ // Search context
218
+ "search.contextBefore": [
219
219
  { value: "0", label: "0 lines" },
220
220
  { value: "1", label: "1 line" },
221
221
  { value: "2", label: "2 lines" },
222
222
  { value: "3", label: "3 lines" },
223
223
  { value: "5", label: "5 lines" },
224
224
  ],
225
- "grep.contextAfter": [
225
+ "search.contextAfter": [
226
226
  { value: "0", label: "0 lines" },
227
227
  { value: "1", label: "1 line" },
228
228
  { value: "2", label: "2 lines" },
@@ -665,10 +665,10 @@ class TreeList implements Component {
665
665
  .slice(0, 50);
666
666
  return `[bash: ${cmd}${rawCmd.length > 50 ? "..." : ""}]`;
667
667
  }
668
- case "grep": {
668
+ case "search": {
669
669
  const pattern = String(args.pattern || "");
670
670
  const path = shortenPath(String(args.path || "."));
671
- return `[grep: /${pattern}/ in ${path}]`;
671
+ return `[search: /${pattern}/ in ${path}]`;
672
672
  }
673
673
  case "find": {
674
674
  const pattern = String(args.pattern || "");
@@ -127,14 +127,38 @@ export class UiHelpers {
127
127
  this.ctx.chatContainer.addChild(component);
128
128
  break;
129
129
  }
130
- if (message.customType === "irc:incoming" || message.customType === "irc:autoreply") {
130
+ if (
131
+ message.customType === "irc:incoming" ||
132
+ message.customType === "irc:autoreply" ||
133
+ message.customType === "irc:relay"
134
+ ) {
131
135
  const details = (
132
- message as CustomMessage<{ from?: string; to?: string; message?: string; reply?: string }>
136
+ message as CustomMessage<{
137
+ from?: string;
138
+ to?: string;
139
+ message?: string;
140
+ reply?: string;
141
+ body?: string;
142
+ kind?: "message" | "reply";
143
+ }>
133
144
  ).details;
134
- const isIncoming = message.customType === "irc:incoming";
135
- const peer = isIncoming ? (details?.from ?? "?") : (details?.to ?? "?");
136
- const body = isIncoming ? (details?.message ?? "") : (details?.reply ?? "");
137
- const arrow = isIncoming ? `\u21e6 ${peer}` : `\u21e8 ${peer} (auto)`;
145
+ let arrow: string;
146
+ let body: string;
147
+ if (message.customType === "irc:incoming") {
148
+ const peer = details?.from ?? "?";
149
+ body = details?.message ?? "";
150
+ arrow = `\u21e6 ${peer}`;
151
+ } else if (message.customType === "irc:autoreply") {
152
+ const peer = details?.to ?? "?";
153
+ body = details?.reply ?? "";
154
+ arrow = `\u21e8 ${peer} (auto)`;
155
+ } else {
156
+ const from = details?.from ?? "?";
157
+ const to = details?.to ?? "?";
158
+ body = details?.body ?? "";
159
+ const suffix = details?.kind === "reply" ? " (auto)" : "";
160
+ arrow = `${from} \u21e8 ${to}${suffix}`;
161
+ }
138
162
  const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
139
163
  this.ctx.chatContainer.addChild(new Text(header, 1, 0));
140
164
  if (body) {
@@ -226,7 +250,7 @@ export class UiHelpers {
226
250
  sessionContext: SessionContext,
227
251
  options: { updateFooter?: boolean; populateHistory?: boolean } = {},
228
252
  ): void {
229
- this.ctx.optimisticUserMessageSignature = undefined;
253
+ // Preserved: message_start handler owns this lifecycle (see #783)
230
254
  this.ctx.pendingTools.clear();
231
255
 
232
256
  if (options.updateFooter) {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: explore
3
3
  description: Fast read-only codebase scout returning compressed context for handoff
4
- tools: read, grep, find, web_search
4
+ tools: read, search, find, web_search
5
5
  model: pi/smol
6
6
  thinking-level: med
7
7
  output:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: librarian
3
3
  description: Researches external libraries and APIs by reading source code. Returns definitive, source-verified answers.
4
- tools: read, grep, find, bash, lsp, web_search, ast_grep
4
+ tools: read, search, find, bash, lsp, web_search, ast_grep
5
5
  model: pi/smol
6
6
  thinking-level: minimal
7
7
  output:
@@ -87,7 +87,7 @@ Before acting, determine what kind of question this is:
87
87
 
88
88
  ## 3. Investigate
89
89
  - Read `package.json`, `Cargo.toml`, or equivalent for version info and entry points.
90
- - Use `grep`, `find`, and `ast_grep` to locate relevant source, type definitions, and docs. Parallelize searches.
90
+ - Use `search`, `find`, and `ast_grep` to locate relevant source, type definitions, and docs. Parallelize searches.
91
91
  - Read the actual implementation — not just README examples. READMEs are aspirational; source code is truth.
92
92
  - For behavior questions: trace through the implementation. Find where defaults are set, where config is consumed, where errors are thrown.
93
93
  - Check tests for usage examples and edge case behavior — tests are the most honest documentation.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: plan
3
3
  description: Software architect for complex multi-file architectural decisions. NOT for simple tasks, single-file changes, or tasks completable in <5 tool calls.
4
- tools: read, grep, find, bash, lsp, web_search, ast_grep
4
+ tools: read, search, find, bash, lsp, web_search, ast_grep
5
5
  spawns: explore
6
6
  model: pi/plan, pi/slow
7
7
  thinking-level: high
@@ -14,7 +14,7 @@ You are an expert software architect analyzing the codebase and the user's reque
14
14
  2. Identify ambiguities; list assumptions
15
15
 
16
16
  ## Phase 2: Explore
17
- 1. Find existing patterns via grep/find
17
+ 1. Find existing patterns via `search`/`find`
18
18
  2. Read key files; understand architecture
19
19
  3. Trace data flow through relevant paths
20
20
  4. Identify types, interfaces, contracts
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: reviewer
3
3
  description: "Code review specialist for quality/security analysis"
4
- tools: read, grep, find, bash, lsp, web_search, ast_grep, report_finding
4
+ tools: read, search, find, bash, lsp, web_search, ast_grep, report_finding
5
5
  spawns: explore
6
6
  model: pi/slow
7
7
  thinking-level: high
@@ -1,6 +1,6 @@
1
1
  You are a worker agent for delegated tasks.
2
2
 
3
- You have FULL access to all tools (edit, write, bash, grep, read, etc.) and you **MUST** use them as needed to complete your task.
3
+ You have FULL access to all tools (edit, write, bash, search, read, etc.) and you **MUST** use them as needed to complete your task.
4
4
 
5
5
  You **MUST** maintain hyperfocus on the task at hand, do not deviate from what was assigned to you.
6
6
 
@@ -8,7 +8,7 @@ You **MUST** maintain hyperfocus on the task at hand, do not deviate from what w
8
8
  - You **MUST** finish only the assigned work and return the minimum useful result. Do not repeat what you have written to the filesystem.
9
9
  - You **MAY** make file edits, run commands, and create files when your task requires it—and **SHOULD** do so.
10
10
  - You **MUST** be concise. You **MUST NOT** include filler, repetition, or tool transcripts. User cannot even see you. Your result is just the notes you are leaving for yourself.
11
- - You **SHOULD** prefer narrow search (grep/find) then read only needed ranges. Do not bother yourself with anything beyond your current scope.
11
+ - You **SHOULD** prefer narrow lookups (`search`/`find`) then read only needed ranges. Do not bother yourself with anything beyond your current scope.
12
12
  - You **SHOULD NOT** do full-file reads unless necessary.
13
13
  - You **SHOULD** prefer edits to existing files over creating new ones.
14
14
  - You **MUST NOT** create documentation files (*.md) unless explicitly requested.
@@ -42,7 +42,7 @@ Plan execution runs in fresh context (session cleared). You **MUST** make the pl
42
42
 
43
43
  <procedure>
44
44
  ### 1. Explore
45
- You **MUST** use `find`, `grep`, `read`, `ls` to understand the codebase.
45
+ You **MUST** use `find`, `search`, `read` to understand the codebase.
46
46
  ### 2. Interview
47
47
  You **MUST** use `{{askToolName}}` to clarify:
48
48
  - Ambiguous requirements
@@ -78,10 +78,13 @@ If any check fails, continue or mark [blocked]. Do **NOT** reframe partial work
78
78
  - Prefer concise, information-dense writing.
79
79
  - Avoid repeating the user's request or narrating routine tool calls.
80
80
  - Do not give time estimates or predictions.
81
+ - Do not emit closing summaries, recap paragraphs, or "what I did" wrap-ups. Final messages state the result and any blockers; the trace already shows the work.
81
82
  </communication>
82
83
 
83
84
  <output-contract>
84
85
  - Brief preambles are allowed when they improve orientation, but they **MUST** stay short and **MUST NOT** be treated as completion.
86
+ - A phase boundary, todo flip, or completed sub-step is **NOT** a yield point. Continue directly to the next step in the same turn — do **NOT** stop to summarize, ask for acknowledgement, or wait for the user to say "go".
87
+ - Yield only when (a) the whole deliverable is complete, (b) you are [blocked], or (c) the user asked a question that requires their input.
85
88
  - Claims about code, tools, tests, docs, or external sources **MUST** be grounded in what was actually observed.
86
89
  - If a statement is an inference, label it as such.
87
90
  - Be brief in prose, not in evidence, verification, or blocking details.
@@ -175,44 +178,45 @@ Most tools have a `{{intentField}}` parameter. Fill it with a concise intent in
175
178
  {{#if mcpDiscoveryMode}}
176
179
  ### MCP tool discovery
177
180
  {{#if hasMCPDiscoveryServers}}Discoverable MCP servers in this session: {{#list mcpDiscoveryServerSummaries join=", "}}{{this}}{{/list}}.{{/if}}
178
- If the task may involve external systems, SaaS APIs, chat, tickets, databases, deployments, or other non-local integrations, you **SHOULD** call `search_tool_bm25` before concluding no such tool exists.
181
+ If the task may involve external systems, SaaS APIs, chat, tickets, databases, deployments, or other non-local integrations, you **SHOULD** call `{{toolRefs.search_tool_bm25}}` before concluding no such tool exists.
179
182
  {{/if}}
180
183
 
181
184
  {{#ifAny (includes tools "python") (includes tools "bash")}}
182
185
  ### Tool priority
183
- 1. Use specialized tools first{{#ifAny (includes tools "read") (includes tools "grep") (includes tools "find") (includes tools "edit") (includes tools "lsp")}}: {{#has tools "read"}}`read`, {{/has}}{{#has tools "grep"}}`grep`, {{/has}}{{#has tools "find"}}`find`, {{/has}}{{#has tools "edit"}}`edit`, {{/has}}{{#has tools "lsp"}}`lsp`{{/has}}{{/ifAny}}
186
+ 1. Use specialized tools first{{#ifAny (includes tools "read") (includes tools "search") (includes tools "find") (includes tools "edit") (includes tools "lsp")}}: {{#has tools "read"}}`{{toolRefs.read}}`, {{/has}}{{#has tools "search"}}`{{toolRefs.search}}`, {{/has}}{{#has tools "find"}}`{{toolRefs.find}}`, {{/has}}{{#has tools "edit"}}`{{toolRefs.edit}}`, {{/has}}{{#has tools "lsp"}}`{{toolRefs.lsp}}`{{/has}}{{/ifAny}}
184
187
  2. Python: logic, loops, processing, display
185
188
  3. Bash: simple one-liners only
186
189
  You **MUST NOT** use Python or Bash when a specialized tool exists.
187
190
  {{/ifAny}}
188
191
 
189
- {{#ifAny (includes tools "read") (includes tools "write") (includes tools "grep") (includes tools "find") (includes tools "edit")}}
190
- {{#has tools "read"}}- Use `read`, not `cat`.{{/has}}
191
- {{#has tools "write"}}- Use `write`, not shell redirection.{{/has}}
192
- {{#has tools "grep"}}- Use `grep`, not shell regex search.{{/has}}
193
- {{#has tools "find"}}- Use `find`, not shell file globbing.{{/has}}
194
- {{#has tools "edit"}}- Use `edit` for surgical text changes, not `sed`.{{/has}}
192
+ {{#ifAny (includes tools "read") (includes tools "write") (includes tools "search") (includes tools "find") (includes tools "edit")}}
193
+ {{#has tools "read"}}- Use `{{toolRefs.read}}`, not `cat` or `ls`. `{{toolRefs.read}}` on a directory path lists its entries.{{/has}}
194
+ {{#has tools "write"}}- Use `{{toolRefs.write}}`, not shell redirection.{{/has}}
195
+ {{#has tools "search"}}- Use `{{toolRefs.search}}`, not shell regex search.{{/has}}
196
+ {{#has tools "find"}}- Use `{{toolRefs.find}}`, not shell file globbing.{{/has}}
197
+ {{#has tools "edit"}}- Use `{{toolRefs.edit}}` for surgical text changes, not `sed`.{{/has}}
195
198
  {{/ifAny}}
196
199
 
197
200
  ### Paths
198
- - For tools that take a `path` (or path-like field), prefer cwd-relative paths for files inside the cwd. Use absolute paths only when targeting files outside the cwd or when expanding `~`.
201
+ - For tools that take a `path` or path-like field, you **MUST** use cwd-relative paths for files inside the current working directory.
202
+ - You **MUST** use absolute paths only when targeting files outside the current working directory or when expanding `~`.
199
203
 
200
204
  {{#has tools "lsp"}}
201
205
  ### LSP guidance
202
206
  Use semantic tools for semantic questions:
203
- - Definition → `lsp definition`
204
- - Type → `lsp type_definition`
205
- - Implementations → `lsp implementation`
206
- - References → `lsp references`
207
- - What is this? → `lsp hover`
208
- - Refactors/imports/fixes → `lsp code_actions` (list first, then apply with `apply: true` + `query`)
207
+ - Definition → `{{toolRefs.lsp}} definition`
208
+ - Type → `{{toolRefs.lsp}} type_definition`
209
+ - Implementations → `{{toolRefs.lsp}} implementation`
210
+ - References → `{{toolRefs.lsp}} references`
211
+ - What is this? → `{{toolRefs.lsp}} hover`
212
+ - Refactors/imports/fixes → `{{toolRefs.lsp}} code_actions` (list first, then apply with `apply: true` + `query`)
209
213
  {{/has}}
210
214
 
211
215
  {{#ifAny (includes tools "ast_grep") (includes tools "ast_edit")}}
212
216
  ### AST guidance
213
217
  Use syntax-aware tools before text hacks:
214
- {{#has tools "ast_grep"}}- `ast_grep` for structural discovery{{/has}}
215
- {{#has tools "ast_edit"}}- `ast_edit` for codemods{{/has}}
218
+ {{#has tools "ast_grep"}}- `{{toolRefs.ast_grep}}` for structural discovery{{/has}}
219
+ {{#has tools "ast_edit"}}- `{{toolRefs.ast_edit}}` for codemods{{/has}}
216
220
  - Use `grep` only for plain text lookup when structure is irrelevant
217
221
  {{/ifAny}}
218
222
 
@@ -233,10 +237,10 @@ Match commands to the host shell: linux/bash and macos/zsh use Unix commands; wi
233
237
  {{/has}}
234
238
 
235
239
  ### Search before you read
236
- {{#has tools "grep"}}- Use `grep` to locate targets.{{/has}}
237
- {{#has tools "find"}}- Use `find` to map structure.{{/has}}
238
- {{#has tools "read"}}- Use `read` with offset or limit rather than whole-file reads when practical.{{/has}}
239
- {{#has tools "task"}}- Use `task` for investigate+edit when available.{{/has}}
240
+ {{#has tools "grep"}}- Use `{{toolRefs.grep}}` to locate targets.{{/has}}
241
+ {{#has tools "find"}}- Use `{{toolRefs.find}}` to map structure.{{/has}}
242
+ {{#has tools "read"}}- Use `{{toolRefs.read}}` with offset or limit rather than whole-file reads when practical.{{/has}}
243
+ {{#has tools "task"}}- Use `{{toolRefs.task}}` for investigate+edit when available.{{/has}}
240
244
  - Do not read a file hoping to find the right thing.
241
245
 
242
246
  <tool-persistence>
@@ -250,8 +254,8 @@ Match commands to the host shell: linux/bash and macos/zsh use Unix commands; wi
250
254
 
251
255
  {{#if (includes tools "inspect_image")}}
252
256
  ### Image inspection
253
- - For image understanding tasks you **MUST** use `inspect_image` over `read` to avoid overloading session context.
254
- - Write a specific `question` for `inspect_image`: what to inspect, constraints, and desired output format.
257
+ - For image understanding tasks you **MUST** use `{{toolRefs.inspect_image}}` over `{{toolRefs.read}}` to avoid overloading session context.
258
+ - Write a specific `question` for `{{toolRefs.inspect_image}}`: what to inspect, constraints, and desired output format.
255
259
  {{/if}}
256
260
 
257
261
  {{SECTION_SEPARATOR "Rules"}}
@@ -285,7 +289,7 @@ These are inviolable.
285
289
  ## 1. Scope
286
290
  {{#if skills.length}}- You **MUST** read skills that match the task domain before starting.{{/if}}
287
291
  {{#if rules.length}}- You **MUST** read rules that match the file paths you are touching before starting.{{/if}}
288
- {{#has tools "task"}}- Determine whether the task can be parallelized with `task`.{{/has}}
292
+ {{#has tools "task"}}- Determine whether the task can be parallelized with `{{toolRefs.task}}`.{{/has}}
289
293
  - If the task is multi-file or imprecisely scoped, write a step-by-step plan before editing.
290
294
  - For new or unfamiliar work, think about architecture, review the codebase, consult authoritative docs when needed, then implement the best fit or surface tradeoffs.
291
295
  - If context is missing, use tools first; ask a minimal question only when necessary.
@@ -293,17 +297,18 @@ These are inviolable.
293
297
  ## 2. Before you edit
294
298
  - Read the relevant section of any file before editing.
295
299
  - You **MUST** search for existing examples before implementing a new pattern, utility, or abstraction. If the codebase already solves it, **MUST** reuse it; inventing a parallel convention is **PROHIBITED**.
296
- {{#has tools "lsp"}}- Before modifying a function, type, or exported symbol, run `lsp references` to find its consumers.{{/has}}
300
+ {{#has tools "lsp"}}- Before modifying a function, type, or exported symbol, run `{{toolRefs.lsp}} references` to find its consumers.{{/has}}
297
301
  - If a file changed since you last read it, re-read before editing.
298
302
 
299
303
  ## 3. Parallelization
300
304
  - Prefer parallel work whenever the pieces are independent.
301
305
  {{#has tools "task"}}- Use tasks or subagents when independent investigations or edits can be split safely.{{/has}}
302
306
  - If you cannot explain why one piece depends on another, they are probably independent.
303
-
307
+ {{#has tools "task"}}- When a plan feels too large for a single turn, parallelize aggressively — do **NOT** abandon phases, silently drop them, or narrate scope cuts. Scope pressure is a signal to delegate, not to shrink the work.{{/has}}
304
308
  ## 4. Task tracking
305
309
  - Update todos as you progress.
306
310
  - Skip task tracking only for trivial requests.
311
+ - Marking a todo done is a transition, not a stop: in the same turn, start the next pending todo. Acceptable inter-phase text is one short line ("phase 1 done, starting phase 2") — not a recap, not a question.
307
312
 
308
313
  ## 5. While working
309
314
  - Keep one job per level of abstraction.
@@ -318,11 +323,9 @@ These are inviolable.
318
323
  - If blocked, exhaust tools and context first.
319
324
 
320
325
  ## 6. Verification
321
- - Test rigorously. Prefer unit or end-to-end tests.
322
- - You **MUST NOT** rely on mocks for behavior the production system owns — they invent behaviors that never happen in production and hide real bugs. Use mocks or fakes only for genuinely external, unstable, slow, or costly boundaries.
326
+ - Test rigorously. Prefer unit or end-to-end tests, you **MUST NOT** rely on mocks.
323
327
  - Run only tests you added or modified unless asked otherwise.
324
- - You **MUST NOT** yield non-trivial work without proof: tests, linters, type checks, repro steps, or equivalent evidence.
325
- - High-impact actions **MUST** be verified or explicitly held for permission before yielding.
328
+ - You **MUST NOT** yield non-trivial work without proof: tests, e2e run, browsing and QA testing, etc.
326
329
 
327
330
  {{#if secretsEnabled}}
328
331
  <redacted-content>
@@ -332,7 +335,7 @@ Some values in tool output are intentionally redacted as `#XXXX#` tokens. Treat
332
335
 
333
336
  {{SECTION_SEPARATOR "Now"}}
334
337
 
335
- The current working directory is '{{cwd}}'.
338
+ The current working directory is '{{cwd}}'. Paths inside this directory **MUST** be passed to tools as relative paths.
336
339
  Today is '{{date}}'. Begin now.
337
340
 
338
341
  <critical>
@@ -1,5 +1,3 @@
1
- ## `apply_patch`
2
-
3
1
  Use the `apply_patch` shell command to edit files.
4
2
  Your patch language is a stripped‑down, file‑oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high‑level envelope:
5
3