@oh-my-pi/pi-coding-agent 14.9.2 → 14.9.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 (97) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/package.json +7 -7
  3. package/scripts/format-prompts.ts +3 -3
  4. package/src/async/job-manager.ts +66 -9
  5. package/src/capability/rule.ts +20 -0
  6. package/src/config/model-registry.ts +13 -0
  7. package/src/config/model-resolver.ts +8 -2
  8. package/src/config/prompt-templates.ts +0 -5
  9. package/src/config/settings-schema.ts +39 -1
  10. package/src/edit/index.ts +8 -0
  11. package/src/edit/renderer.ts +6 -1
  12. package/src/edit/streaming.ts +53 -2
  13. package/src/eval/eval.lark +10 -31
  14. package/src/eval/index.ts +1 -0
  15. package/src/eval/js/context-manager.ts +1 -38
  16. package/src/eval/js/prelude.txt +0 -2
  17. package/src/eval/parse.ts +156 -255
  18. package/src/eval/py/executor.ts +24 -8
  19. package/src/eval/py/index.ts +1 -0
  20. package/src/eval/py/prelude.py +11 -80
  21. package/src/eval/sniff.ts +28 -0
  22. package/src/export/html/template.css +50 -0
  23. package/src/export/html/template.generated.ts +1 -1
  24. package/src/export/html/template.js +229 -17
  25. package/src/extensibility/plugins/loader.ts +31 -6
  26. package/src/extensibility/skills.ts +20 -0
  27. package/src/hashline/constants.ts +20 -0
  28. package/src/hashline/grammar.lark +16 -23
  29. package/src/hashline/hash.ts +4 -34
  30. package/src/hashline/input.ts +16 -2
  31. package/src/hashline/parser.ts +12 -1
  32. package/src/internal-urls/agent-protocol.ts +64 -52
  33. package/src/internal-urls/artifact-protocol.ts +52 -51
  34. package/src/internal-urls/docs-index.generated.ts +34 -1
  35. package/src/internal-urls/index.ts +6 -19
  36. package/src/internal-urls/local-protocol.ts +50 -7
  37. package/src/internal-urls/mcp-protocol.ts +3 -8
  38. package/src/internal-urls/memory-protocol.ts +90 -59
  39. package/src/internal-urls/pi-protocol.ts +1 -0
  40. package/src/internal-urls/router.ts +40 -23
  41. package/src/internal-urls/rule-protocol.ts +3 -20
  42. package/src/internal-urls/skill-protocol.ts +5 -27
  43. package/src/internal-urls/types.ts +18 -2
  44. package/src/main.ts +1 -1
  45. package/src/mcp/manager.ts +17 -0
  46. package/src/modes/components/session-observer-overlay.ts +2 -2
  47. package/src/modes/components/tool-execution.ts +6 -0
  48. package/src/modes/components/tree-selector.ts +4 -0
  49. package/src/modes/controllers/event-controller.ts +23 -2
  50. package/src/modes/controllers/mcp-command-controller.ts +7 -10
  51. package/src/modes/interactive-mode.ts +2 -2
  52. package/src/modes/theme/theme.ts +27 -27
  53. package/src/modes/types.ts +1 -1
  54. package/src/modes/utils/ui-helpers.ts +14 -9
  55. package/src/prompts/commands/orchestrate.md +1 -0
  56. package/src/prompts/system/custom-system-prompt.md +0 -2
  57. package/src/prompts/system/project-prompt.md +10 -0
  58. package/src/prompts/system/subagent-system-prompt.md +18 -9
  59. package/src/prompts/system/subagent-user-prompt.md +1 -10
  60. package/src/prompts/system/system-prompt.md +159 -232
  61. package/src/prompts/tools/ask.md +0 -1
  62. package/src/prompts/tools/bash.md +0 -34
  63. package/src/prompts/tools/eval.md +27 -16
  64. package/src/prompts/tools/github.md +6 -5
  65. package/src/prompts/tools/hashline.md +1 -0
  66. package/src/prompts/tools/job.md +14 -6
  67. package/src/prompts/tools/task.md +20 -3
  68. package/src/registry/agent-registry.ts +2 -1
  69. package/src/sdk.ts +87 -89
  70. package/src/session/agent-session.ts +107 -37
  71. package/src/session/artifacts.ts +7 -4
  72. package/src/session/session-manager.ts +30 -1
  73. package/src/ssh/connection-manager.ts +32 -16
  74. package/src/ssh/sshfs-mount.ts +10 -7
  75. package/src/system-prompt.ts +3 -9
  76. package/src/task/executor.ts +23 -7
  77. package/src/task/index.ts +57 -36
  78. package/src/tool-discovery/tool-index.ts +21 -8
  79. package/src/tools/ast-edit.ts +3 -2
  80. package/src/tools/ast-grep.ts +3 -2
  81. package/src/tools/bash.ts +30 -50
  82. package/src/tools/browser/tab-supervisor.ts +12 -2
  83. package/src/tools/eval.ts +59 -44
  84. package/src/tools/fetch.ts +1 -1
  85. package/src/tools/gh.ts +140 -4
  86. package/src/tools/index.ts +12 -11
  87. package/src/tools/job.ts +48 -12
  88. package/src/tools/path-utils.ts +21 -1
  89. package/src/tools/read.ts +74 -31
  90. package/src/tools/search.ts +16 -3
  91. package/src/tools/todo-write.ts +1 -1
  92. package/src/utils/file-display-mode.ts +11 -5
  93. package/src/web/scrapers/mastodon.ts +1 -1
  94. package/src/web/scrapers/repology.ts +7 -7
  95. package/src/internal-urls/jobs-protocol.ts +0 -119
  96. package/src/task/template.ts +0 -47
  97. package/src/tools/bash-normalize.ts +0 -107
@@ -539,6 +539,10 @@ class TreeList implements Component {
539
539
  const msgWithContent = msg as { content?: unknown };
540
540
  const content = normalize(this.#extractContent(msgWithContent.content));
541
541
  result = theme.fg("accent", "user: ") + content;
542
+ } else if (role === "developer") {
543
+ const msgWithContent = msg as { content?: unknown };
544
+ const content = normalize(this.#extractContent(msgWithContent.content));
545
+ result = theme.fg("dim", "developer: ") + theme.fg("muted", content);
542
546
  } else if (role === "assistant") {
543
547
  const msgWithContent = msg as { content?: unknown; stopReason?: string; errorMessage?: string };
544
548
  const textContent = normalize(this.#extractContent(msgWithContent.content));
@@ -1,6 +1,6 @@
1
1
  import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
3
- import { Loader, TERMINAL, Text } from "@oh-my-pi/pi-tui";
3
+ import { type Component, Loader, TERMINAL, Text } from "@oh-my-pi/pi-tui";
4
4
  import { settings } from "../../config/settings";
5
5
  import { AssistantMessageComponent } from "../../modes/components/assistant-message";
6
6
  import { ReadToolGroupComponent } from "../../modes/components/read-tool-group";
@@ -15,6 +15,8 @@ import type { ExitPlanModeDetails } from "../../tools";
15
15
 
16
16
  type AgentSessionEventKind = AgentSessionEvent["type"];
17
17
 
18
+ const IRC_MESSAGE_VISIBLE_TTL_MS = 10_000;
19
+
18
20
  type AgentSessionEventHandlers = {
19
21
  [E in AgentSessionEventKind]: (event: Extract<AgentSessionEvent, { type: E }>) => Promise<void>;
20
22
  };
@@ -29,6 +31,7 @@ export class EventController {
29
31
  #readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
30
32
  #lastAssistantComponent: AssistantMessageComponent | undefined = undefined;
31
33
  #idleCompactionTimer?: NodeJS.Timeout;
34
+ #ircExpiryTimers = new Map<string, NodeJS.Timeout>();
32
35
  #handlers: AgentSessionEventHandlers;
33
36
 
34
37
  constructor(private ctx: InteractiveModeContext) {
@@ -59,6 +62,10 @@ export class EventController {
59
62
 
60
63
  dispose(): void {
61
64
  this.#cancelIdleCompaction();
65
+ for (const timer of this.#ircExpiryTimers.values()) {
66
+ clearTimeout(timer);
67
+ }
68
+ this.#ircExpiryTimers.clear();
62
69
  }
63
70
 
64
71
  #resetReadGroup(): void {
@@ -222,10 +229,24 @@ export class EventController {
222
229
  }
223
230
  this.#renderedCustomMessages.add(signature);
224
231
  this.#resetReadGroup();
225
- this.ctx.addMessageToChat(event.message);
232
+ const components = this.ctx.addMessageToChat(event.message);
233
+ this.#scheduleIrcExpiry(signature, components);
226
234
  this.ctx.ui.requestRender();
227
235
  }
228
236
 
237
+ #scheduleIrcExpiry(signature: string, components: Component[]): void {
238
+ if (components.length === 0 || this.#ircExpiryTimers.has(signature)) return;
239
+ const timer = setTimeout(() => {
240
+ this.#ircExpiryTimers.delete(signature);
241
+ for (const component of components) {
242
+ this.ctx.chatContainer.removeChild(component);
243
+ }
244
+ this.ctx.ui.requestRender();
245
+ }, IRC_MESSAGE_VISIBLE_TTL_MS);
246
+ timer.unref?.();
247
+ this.#ircExpiryTimers.set(signature, timer);
248
+ }
249
+
229
250
  async #handleNotice(event: Extract<AgentSessionEvent, { type: "notice" }>): Promise<void> {
230
251
  const message = event.source ? `${event.source}: ${event.message}` : event.message;
231
252
  if (event.level === "error") {
@@ -1238,12 +1238,12 @@ export class MCPCommandController {
1238
1238
  ? theme.fg("muted", "Connecting")
1239
1239
  : theme.fg("warning", "Not connected yet");
1240
1240
  this.#showMessage(
1241
- ["", theme.fg("success", `\u2713 Enabled "${name}"`), "", ` Status: ${status}`, ""].join("\n"),
1241
+ ["", theme.fg("success", `✓ Enabled "${name}"`), "", ` Status: ${status}`, ""].join("\n"),
1242
1242
  );
1243
1243
  } else {
1244
1244
  await this.ctx.mcpManager?.disconnectServer(name);
1245
1245
  await this.ctx.session.refreshMCPTools(this.ctx.mcpManager?.getTools() ?? []);
1246
- this.#showMessage(["", theme.fg("success", `\u2713 Disabled "${name}"`), ""].join("\n"));
1246
+ this.#showMessage(["", theme.fg("success", `✓ Disabled "${name}"`), ""].join("\n"));
1247
1247
  }
1248
1248
  return;
1249
1249
  }
@@ -1429,12 +1429,9 @@ export class MCPCommandController {
1429
1429
  await this.ctx.session.refreshMCPTools(this.ctx.mcpManager.getTools());
1430
1430
  const serverTools = this.ctx.mcpManager.getTools().filter(t => t.mcpServerName === name);
1431
1431
  this.#showMessage(
1432
- [
1433
- "\n",
1434
- theme.fg("success", `\u2713 Reconnected to "${name}"`),
1435
- ` Tools: ${serverTools.length}`,
1432
+ ["\n", theme.fg("success", `✓ Reconnected to "${name}"`), ` Tools: ${serverTools.length}`, "\n"].join(
1436
1433
  "\n",
1437
- ].join("\n"),
1434
+ ),
1438
1435
  );
1439
1436
  } else {
1440
1437
  this.ctx.showError(`Failed to reconnect to "${name}". Check server status and logs.`);
@@ -1589,8 +1586,8 @@ export class MCPCommandController {
1589
1586
  hasAny = true;
1590
1587
 
1591
1588
  lines.push(`${theme.fg("accent", name)}:`);
1592
- const check = theme.fg("success", "\u2713");
1593
- const cross = theme.fg("dim", "\u2717");
1589
+ const check = theme.fg("success", "");
1590
+ const cross = theme.fg("dim", "");
1594
1591
  if (supportsToolsChanged) lines.push(` ${check} tools/list_changed`);
1595
1592
  if (supportsResourcesChanged) lines.push(` ${check} resources/list_changed`);
1596
1593
  if (supportsPromptsChanged) lines.push(` ${check} prompts/list_changed`);
@@ -1607,7 +1604,7 @@ export class MCPCommandController {
1607
1604
  lines.push(` ${check} resources/subscribe ${subStatus}`);
1608
1605
  if (enabled && subscribedUris && subscribedUris.size > 0) {
1609
1606
  for (const uri of subscribedUris) {
1610
- lines.push(` ${theme.fg("success", "\u2713")} ${theme.fg("dim", uri)}`);
1607
+ lines.push(` ${theme.fg("success", "")} ${theme.fg("dim", uri)}`);
1611
1608
  }
1612
1609
  }
1613
1610
  } else if (supportsResources) {
@@ -1502,8 +1502,8 @@ export class InteractiveMode implements InteractiveModeContext {
1502
1502
  return this.#uiHelpers.isKnownSlashCommand(text);
1503
1503
  }
1504
1504
 
1505
- addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void {
1506
- this.#uiHelpers.addMessageToChat(message, options);
1505
+ addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[] {
1506
+ return this.#uiHelpers.addMessageToChat(message, options);
1507
1507
  }
1508
1508
 
1509
1509
  renderSessionContext(
@@ -387,51 +387,51 @@ const NERD_SYMBOLS: SymbolMap = {
387
387
  "nav.back": "\uf060",
388
388
  // Tree Connectors (same as unicode)
389
389
  // pick: ├─ | alt: ├╴ ├╌ ╠═ ┣━
390
- "tree.branch": "\u251c\u2500",
390
+ "tree.branch": "├─",
391
391
  // pick: └─ | alt: └╴ └╌ ╚═ ┗━
392
- "tree.last": "\u2514\u2500",
392
+ "tree.last": "└─",
393
393
  // pick: │ | alt: ┃ ║ ▏ ▕
394
- "tree.vertical": "\u2502",
394
+ "tree.vertical": "",
395
395
  // pick: ─ | alt: ━ ═ ╌ ┄
396
- "tree.horizontal": "\u2500",
396
+ "tree.horizontal": "",
397
397
  // pick: └ | alt: ╰ ⎿ ↳
398
- "tree.hook": "\u2514",
398
+ "tree.hook": "",
399
399
  // Box Drawing - Rounded (same as unicode)
400
400
  // pick: ╭ | alt: ┌ ┏ ╔
401
- "boxRound.topLeft": "\u256d",
401
+ "boxRound.topLeft": "",
402
402
  // pick: ╮ | alt: ┐ ┓ ╗
403
- "boxRound.topRight": "\u256e",
403
+ "boxRound.topRight": "",
404
404
  // pick: ╰ | alt: └ ┗ ╚
405
- "boxRound.bottomLeft": "\u2570",
405
+ "boxRound.bottomLeft": "",
406
406
  // pick: ╯ | alt: ┘ ┛ ╝
407
- "boxRound.bottomRight": "\u256f",
407
+ "boxRound.bottomRight": "",
408
408
  // pick: ─ | alt: ━ ═ ╌
409
- "boxRound.horizontal": "\u2500",
409
+ "boxRound.horizontal": "",
410
410
  // pick: │ | alt: ┃ ║ ▏
411
- "boxRound.vertical": "\u2502",
411
+ "boxRound.vertical": "",
412
412
  // Box Drawing - Sharp (same as unicode)
413
413
  // pick: ┌ | alt: ┏ ╭ ╔
414
- "boxSharp.topLeft": "\u250c",
414
+ "boxSharp.topLeft": "",
415
415
  // pick: ┐ | alt: ┓ ╮ ╗
416
- "boxSharp.topRight": "\u2510",
416
+ "boxSharp.topRight": "",
417
417
  // pick: └ | alt: ┗ ╰ ╚
418
- "boxSharp.bottomLeft": "\u2514",
418
+ "boxSharp.bottomLeft": "",
419
419
  // pick: ┘ | alt: ┛ ╯ ╝
420
- "boxSharp.bottomRight": "\u2518",
420
+ "boxSharp.bottomRight": "",
421
421
  // pick: ─ | alt: ━ ═ ╌
422
- "boxSharp.horizontal": "\u2500",
422
+ "boxSharp.horizontal": "",
423
423
  // pick: │ | alt: ┃ ║ ▏
424
- "boxSharp.vertical": "\u2502",
424
+ "boxSharp.vertical": "",
425
425
  // pick: ┼ | alt: ╋ ╬ ┿
426
- "boxSharp.cross": "\u253c",
426
+ "boxSharp.cross": "",
427
427
  // pick: ┬ | alt: ╦ ┯ ┳
428
- "boxSharp.teeDown": "\u252c",
428
+ "boxSharp.teeDown": "",
429
429
  // pick: ┴ | alt: ╩ ┷ ┻
430
- "boxSharp.teeUp": "\u2534",
430
+ "boxSharp.teeUp": "",
431
431
  // pick: ├ | alt: ╠ ┝ ┣
432
- "boxSharp.teeRight": "\u251c",
432
+ "boxSharp.teeRight": "",
433
433
  // pick: ┤ | alt: ╣ ┥ ┫
434
- "boxSharp.teeLeft": "\u2524",
434
+ "boxSharp.teeLeft": "",
435
435
  // Separators - Nerd Font specific
436
436
  // pick:  | alt:   
437
437
  "sep.powerline": "\ue0b0",
@@ -446,7 +446,7 @@ const NERD_SYMBOLS: SymbolMap = {
446
446
  // pick:  | alt: 
447
447
  "sep.powerlineThinRight": "\ue0b3",
448
448
  // pick: █ | alt: ▓ ▒ ░ ▉ ▌
449
- "sep.block": "\u2588",
449
+ "sep.block": "",
450
450
  // pick: space | alt: ␠ ·
451
451
  "sep.space": " ",
452
452
  // pick: > | alt: › » ▸
@@ -454,7 +454,7 @@ const NERD_SYMBOLS: SymbolMap = {
454
454
  // pick: < | alt: ‹ « ◂
455
455
  "sep.asciiRight": "<",
456
456
  // pick: · | alt: • ⋅
457
- "sep.dot": " \u00b7 ",
457
+ "sep.dot": " · ",
458
458
  // pick:  | alt: / ∕ ⁄
459
459
  "sep.slash": "\ue0bb",
460
460
  // pick:  | alt: │ ┃ |
@@ -545,16 +545,16 @@ const NERD_SYMBOLS: SymbolMap = {
545
545
  // pick:  | alt:   •
546
546
  "format.bullet": "\uf111",
547
547
  // pick: – | alt: — ― -
548
- "format.dash": "\u2013",
548
+ "format.dash": "",
549
549
  // pick: ⟨ | alt: [ ⟦
550
550
  "format.bracketLeft": "⟨",
551
551
  // pick: ⟩ | alt: ] ⟧
552
552
  "format.bracketRight": "⟩",
553
553
  // Markdown-specific
554
554
  // pick: │ | alt: ┃ ║
555
- "md.quoteBorder": "\u2502",
555
+ "md.quoteBorder": "",
556
556
  // pick: ─ | alt: ━ ═
557
- "md.hrChar": "\u2500",
557
+ "md.hrChar": "",
558
558
  // pick:  | alt:  •
559
559
  "md.bullet": "\uf111",
560
560
  // Language icons (nerd font devicons)
@@ -170,7 +170,7 @@ export interface InteractiveModeContext {
170
170
  */
171
171
  withLocalSubmission<T>(text: string, fn: () => Promise<T>, options?: { imageCount?: number }): Promise<T>;
172
172
  isKnownSlashCommand(text: string): boolean;
173
- addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void;
173
+ addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[];
174
174
  renderSessionContext(
175
175
  sessionContext: SessionContext,
176
176
  options?: { updateFooter?: boolean; populateHistory?: boolean },
@@ -1,6 +1,6 @@
1
1
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
2
  import type { AssistantMessage, ImageContent, Message } from "@oh-my-pi/pi-ai";
3
- import { Spacer, Text, TruncatedText } from "@oh-my-pi/pi-tui";
3
+ import { type Component, Spacer, Text, TruncatedText } from "@oh-my-pi/pi-tui";
4
4
  import { settings } from "../../config/settings";
5
5
  import { AssistantMessageComponent } from "../../modes/components/assistant-message";
6
6
  import { BashExecutionComponent } from "../../modes/components/bash-execution";
@@ -70,7 +70,7 @@ export class UiHelpers {
70
70
  this.ctx.ui.requestRender();
71
71
  }
72
72
 
73
- addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void {
73
+ addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): Component[] {
74
74
  switch (message.role) {
75
75
  case "bashExecution": {
76
76
  const component = new BashExecutionComponent(message.command, this.ctx.ui, message.excludeFromContext);
@@ -147,26 +147,30 @@ export class UiHelpers {
147
147
  if (message.customType === "irc:incoming") {
148
148
  const peer = details?.from ?? "?";
149
149
  body = details?.message ?? "";
150
- arrow = `\u21e6 ${peer}`;
150
+ arrow = `⇦ ${peer}`;
151
151
  } else if (message.customType === "irc:autoreply") {
152
152
  const peer = details?.to ?? "?";
153
153
  body = details?.reply ?? "";
154
- arrow = `\u21e8 ${peer} (auto)`;
154
+ arrow = `⇨ ${peer}`;
155
155
  } else {
156
156
  const from = details?.from ?? "?";
157
157
  const to = details?.to ?? "?";
158
158
  body = details?.body ?? "";
159
- const suffix = details?.kind === "reply" ? " (auto)" : "";
160
- arrow = `${from} \u21e8 ${to}${suffix}`;
159
+ arrow = `${from} ${to}`;
161
160
  }
161
+ const components: Component[] = [];
162
162
  const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
163
- this.ctx.chatContainer.addChild(new Text(header, 1, 0));
163
+ const headerComponent = new Text(header, 1, 0);
164
+ this.ctx.chatContainer.addChild(headerComponent);
165
+ components.push(headerComponent);
164
166
  if (body) {
165
167
  for (const line of body.split("\n")) {
166
- this.ctx.chatContainer.addChild(new Text(theme.fg("muted", ` ${line}`), 0, 0));
168
+ const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
169
+ this.ctx.chatContainer.addChild(lineComponent);
170
+ components.push(lineComponent);
167
171
  }
168
172
  }
169
- break;
173
+ return components;
170
174
  }
171
175
  const renderer = this.ctx.session.extensionRunner?.getMessageRenderer(message.customType);
172
176
  // Both HookMessage and CustomMessage have the same structure, cast for compatibility
@@ -240,6 +244,7 @@ export class UiHelpers {
240
244
  const _exhaustive: never = message;
241
245
  }
242
246
  }
247
+ return [];
243
248
  }
244
249
 
245
250
  /**
@@ -26,6 +26,7 @@ You decompose, dispatch, verify, and iterate. You do **not** edit code. Every fi
26
26
  6. **Commit policy.** If the task asks for commits or the repo workflow expects them, commit after each green phase with a focused message. Never commit a red tree. Never commit work the user did not ask to commit.
27
27
  7. **Respawn, do not absorb.** If a subagent returns incomplete or wrong work, spawn a corrective subagent with the specific gap — do not silently fix it yourself.
28
28
  8. **No scope creep, no scope shrink.** Do not add work the user did not ask for. Do not relabel unfinished items as "follow-up", "v1", or "MVP" to imply completion.
29
+ 9. **Subagents do not verify, lint, or format.** Every `task` assignment **MUST** instruct the subagent to skip all gates and formatters. Their job is the edit only. You — the orchestrator — run verification and formatting **once** at the end of the phase across the union of changed files. Avoids redundant runs and racing formatter passes.
29
30
  </rules>
30
31
 
31
32
  <workflow>
@@ -57,8 +57,6 @@ Rules are local constraints. You **MUST** read `rule://<name>` when working in t
57
57
  {{/list}}
58
58
  </rules>
59
59
  {{/if}}
60
- Current date: {{date}}
61
- Current working directory: {{cwd}}
62
60
  {{#if secretsEnabled}}
63
61
  <redacted-content>
64
62
  Some values in tool output are redacted for security. They appear as `#XXXX#` tokens (4 uppercase-alphanumeric characters wrapped in `#`). These are **not errors** — they are intentional placeholders for sensitive values (API keys, passwords, tokens). Treat them as opaque strings. Do not attempt to decode, fix, or report them as problems.
@@ -1,3 +1,4 @@
1
+ [PROJECT]
1
2
  <workstation>
2
3
  {{#list environment prefix="- " join="\n"}}{{label}}: {{value}}{{/list}}
3
4
  </workstation>
@@ -31,6 +32,15 @@ Working directory layout (sorted by mtime, recent first; depth ≤ 3):
31
32
  </workspace-tree>
32
33
  {{/if}}
33
34
 
35
+ Today is {{date}}, and the current working directory is '{{cwd}}'.
36
+
37
+ <critical>
38
+ - Each response **MUST** advance the task. There is no stopping condition other than completion.
39
+ - You **MUST** default to informed action; do not ask for confirmation when tools or repo context can answer.
40
+ - You **MUST** verify the effect of significant behavioral changes before yielding: run the specific test, command, or scenario that covers your change.
41
+ </critical>
42
+
34
43
  {{#if appendPrompt}}
35
44
  {{appendPrompt}}
36
45
  {{/if}}
46
+ [/PROJECT]
@@ -1,31 +1,40 @@
1
- {{base}}
2
-
3
- {{SECTION_SEPARATOR "Acting as"}}
1
+ [ROLE]
4
2
  {{agent}}
3
+ [/ROLE]
4
+
5
+ {{#if context}}
6
+ [CONTEXT]
7
+ {{context}}
8
+ [/CONTEXT]
9
+ {{/if}}
10
+
11
+ [COOP]
12
+ You are operating on a piece of work assigned to you by the main agent.
5
13
 
6
- {{SECTION_SEPARATOR "Job"}}
7
- You are operating on a delegated sub-task.
8
14
  {{#if worktree}}
15
+ # Working Tree
9
16
  You are working in an isolated working tree at `{{worktree}}` for this sub-task.
10
17
  You **MUST NOT** modify files outside this tree or in the original repository.
11
18
  {{/if}}
12
19
 
13
20
  {{#if contextFile}}
21
+ # Conversation Context
14
22
  If you need additional information, you can find your conversation with the user in {{contextFile}} (`tail` or `grep` relevant terms).
15
23
  {{/if}}
16
24
 
17
25
  {{#if ircPeers}}
18
- {{SECTION_SEPARATOR "IRC Peers"}}
26
+ # IRC Peers
19
27
  You can reach other live agents via the `irc` tool. Your id is `{{ircSelfId}}`. Currently visible peers:
20
28
  {{ircPeers}}
21
29
 
22
30
  Use `irc` only when you need a quick answer from a peer; do not use it for long-form content. Address peers by id or use `"all"` to broadcast.
23
31
  {{/if}}
32
+ [/COOP]
24
33
 
25
- {{SECTION_SEPARATOR "Closure"}}
34
+ [COMPLETION]
26
35
  No TODO tracking, no progress updates. Execute, call `yield`, done.
27
36
 
28
- Every turn **MUST** end with a tool call. A turn whose final block is plain text (or thinking only) is treated as a stop and you will be reminded to yield. While work remains, always continue with another tool call — investigate, edit, run, verify. Save narrative for the final `yield` payload.
37
+ While work remains, always continue with another tool call — investigate, edit, run, verify. Save narrative for the final `yield` payload.
29
38
 
30
39
  When finished, you **MUST** call `yield` exactly once. This is like writing to a ticket: provide what is required and close it.
31
40
 
@@ -38,8 +47,8 @@ Your result **MUST** match this TypeScript interface:
38
47
  ```
39
48
  {{/if}}
40
49
 
41
- {{SECTION_SEPARATOR "Giving Up"}}
42
50
  Giving up is a last resort. If truly blocked, you **MUST** call `yield` exactly once with `result.error` describing what you tried and the exact blocker.
43
51
  You **MUST NOT** give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
44
52
 
45
53
  You **MUST** keep going until this ticket is closed. This matters.
54
+ [/COMPLETION]
@@ -1,12 +1,3 @@
1
- {{#if context}}
2
- {{SECTION_SEPARATOR "Background"}}
3
- <context>
4
- {{context}}
5
- </context>
6
- {{/if}}
1
+ Complete the assignment below, thoroughly:
7
2
 
8
- {{SECTION_SEPARATOR "Task"}}
9
- Your assignment is below. Your work begins now.
10
- <goal>
11
3
  {{assignment}}
12
- </goal>