@oh-my-pi/pi-coding-agent 14.9.3 → 14.9.7

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 (108) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/package.json +7 -7
  3. package/src/async/job-manager.ts +66 -9
  4. package/src/capability/rule.ts +20 -0
  5. package/src/cli/setup-cli.ts +14 -161
  6. package/src/cli/stats-cli.ts +56 -2
  7. package/src/cli.ts +0 -1
  8. package/src/config/model-registry.ts +13 -0
  9. package/src/config/model-resolver.ts +8 -2
  10. package/src/config/settings-schema.ts +1 -11
  11. package/src/edit/index.ts +8 -0
  12. package/src/edit/renderer.ts +6 -1
  13. package/src/edit/streaming.ts +53 -2
  14. package/src/eval/eval.lark +30 -10
  15. package/src/eval/js/context-manager.ts +334 -601
  16. package/src/eval/js/shared/helpers.ts +237 -0
  17. package/src/eval/js/shared/indirect-eval.ts +30 -0
  18. package/src/eval/js/{prelude.txt → shared/prelude.txt} +0 -2
  19. package/src/eval/js/shared/rewrite-imports.ts +211 -0
  20. package/src/eval/js/shared/runtime.ts +168 -0
  21. package/src/eval/js/shared/types.ts +18 -0
  22. package/src/eval/js/tool-bridge.ts +2 -4
  23. package/src/eval/js/worker-core.ts +146 -0
  24. package/src/eval/js/worker-entry.ts +24 -0
  25. package/src/eval/js/worker-protocol.ts +41 -0
  26. package/src/eval/parse.ts +218 -49
  27. package/src/eval/py/display.ts +71 -0
  28. package/src/eval/py/executor.ts +97 -96
  29. package/src/eval/py/index.ts +2 -2
  30. package/src/eval/py/kernel.ts +472 -900
  31. package/src/eval/py/prelude.py +106 -87
  32. package/src/eval/py/runner.py +879 -0
  33. package/src/eval/py/runtime.ts +3 -16
  34. package/src/eval/py/tool-bridge.ts +137 -0
  35. package/src/export/html/template.css +12 -0
  36. package/src/export/html/template.generated.ts +1 -1
  37. package/src/export/html/template.js +113 -7
  38. package/src/extensibility/plugins/loader.ts +31 -6
  39. package/src/extensibility/skills.ts +20 -0
  40. package/src/internal-urls/agent-protocol.ts +63 -52
  41. package/src/internal-urls/artifact-protocol.ts +51 -51
  42. package/src/internal-urls/docs-index.generated.ts +35 -3
  43. package/src/internal-urls/index.ts +6 -19
  44. package/src/internal-urls/local-protocol.ts +49 -7
  45. package/src/internal-urls/mcp-protocol.ts +2 -8
  46. package/src/internal-urls/memory-protocol.ts +89 -59
  47. package/src/internal-urls/router.ts +38 -22
  48. package/src/internal-urls/rule-protocol.ts +2 -20
  49. package/src/internal-urls/skill-protocol.ts +4 -27
  50. package/src/main.ts +1 -1
  51. package/src/mcp/manager.ts +17 -0
  52. package/src/modes/components/session-observer-overlay.ts +2 -2
  53. package/src/modes/components/tool-execution.ts +6 -0
  54. package/src/modes/components/tree-selector.ts +4 -0
  55. package/src/modes/controllers/command-controller.ts +0 -23
  56. package/src/modes/controllers/event-controller.ts +23 -2
  57. package/src/modes/controllers/mcp-command-controller.ts +7 -10
  58. package/src/modes/interactive-mode.ts +2 -2
  59. package/src/modes/theme/theme.ts +27 -27
  60. package/src/modes/types.ts +1 -1
  61. package/src/modes/utils/ui-helpers.ts +14 -9
  62. package/src/prompts/commands/orchestrate.md +1 -0
  63. package/src/prompts/system/project-prompt.md +10 -2
  64. package/src/prompts/system/subagent-system-prompt.md +8 -8
  65. package/src/prompts/system/system-prompt.md +13 -7
  66. package/src/prompts/tools/ask.md +0 -1
  67. package/src/prompts/tools/bash.md +0 -10
  68. package/src/prompts/tools/eval.md +15 -30
  69. package/src/prompts/tools/github.md +6 -5
  70. package/src/prompts/tools/hashline.md +1 -0
  71. package/src/prompts/tools/job.md +14 -6
  72. package/src/prompts/tools/task.md +20 -3
  73. package/src/registry/agent-registry.ts +2 -1
  74. package/src/sdk.ts +87 -89
  75. package/src/session/agent-session.ts +58 -21
  76. package/src/session/artifacts.ts +7 -4
  77. package/src/session/history-storage.ts +77 -19
  78. package/src/session/session-manager.ts +30 -1
  79. package/src/ssh/connection-manager.ts +32 -16
  80. package/src/ssh/sshfs-mount.ts +10 -7
  81. package/src/system-prompt.ts +0 -5
  82. package/src/task/executor.ts +14 -2
  83. package/src/task/index.ts +19 -5
  84. package/src/tool-discovery/tool-index.ts +21 -8
  85. package/src/tools/ast-edit.ts +3 -2
  86. package/src/tools/ast-grep.ts +3 -2
  87. package/src/tools/bash.ts +15 -9
  88. package/src/tools/browser/tab-protocol.ts +4 -0
  89. package/src/tools/browser/tab-supervisor.ts +98 -7
  90. package/src/tools/browser/tab-worker.ts +104 -58
  91. package/src/tools/eval.ts +49 -11
  92. package/src/tools/fetch.ts +1 -1
  93. package/src/tools/gh.ts +140 -4
  94. package/src/tools/index.ts +12 -11
  95. package/src/tools/job.ts +48 -12
  96. package/src/tools/read.ts +5 -4
  97. package/src/tools/search.ts +3 -2
  98. package/src/tools/todo-write.ts +1 -1
  99. package/src/web/scrapers/mastodon.ts +1 -1
  100. package/src/web/scrapers/repology.ts +7 -7
  101. package/src/web/search/index.ts +6 -4
  102. package/src/cli/jupyter-cli.ts +0 -106
  103. package/src/commands/jupyter.ts +0 -32
  104. package/src/eval/py/cancellation.ts +0 -28
  105. package/src/eval/py/gateway-coordinator.ts +0 -424
  106. package/src/internal-urls/jobs-protocol.ts +0 -120
  107. package/src/prompts/system/now-prompt.md +0 -7
  108. /package/src/eval/js/{prelude.ts → shared/prelude.ts} +0 -0
@@ -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>
@@ -1,4 +1,4 @@
1
- <|START_PROJECT|>
1
+ [PROJECT]
2
2
  <workstation>
3
3
  {{#list environment prefix="- " join="\n"}}{{label}}: {{value}}{{/list}}
4
4
  </workstation>
@@ -32,7 +32,15 @@ Working directory layout (sorted by mtime, recent first; depth ≤ 3):
32
32
  </workspace-tree>
33
33
  {{/if}}
34
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
+
35
43
  {{#if appendPrompt}}
36
44
  {{appendPrompt}}
37
45
  {{/if}}
38
- <|END_PROJECT|>
46
+ [/PROJECT]
@@ -1,14 +1,14 @@
1
- <|START_ROLE|>
1
+ [ROLE]
2
2
  {{agent}}
3
- <|END_ROLE|>
3
+ [/ROLE]
4
4
 
5
5
  {{#if context}}
6
- <|START_CONTEXT|>
6
+ [CONTEXT]
7
7
  {{context}}
8
- <|END_CONTEXT|>
8
+ [/CONTEXT]
9
9
  {{/if}}
10
10
 
11
- <|START_COOP|>
11
+ [COOP]
12
12
  You are operating on a piece of work assigned to you by the main agent.
13
13
 
14
14
  {{#if worktree}}
@@ -29,9 +29,9 @@ You can reach other live agents via the `irc` tool. Your id is `{{ircSelfId}}`.
29
29
 
30
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.
31
31
  {{/if}}
32
- <|END_COOP|>
32
+ [/COOP]
33
33
 
34
- <|START_COMPLETION|>
34
+ [COMPLETION]
35
35
  No TODO tracking, no progress updates. Execute, call `yield`, done.
36
36
 
37
37
  While work remains, always continue with another tool call — investigate, edit, run, verify. Save narrative for the final `yield` payload.
@@ -51,4 +51,4 @@ Giving up is a last resort. If truly blocked, you **MUST** call `yield` exactly
51
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.
52
52
 
53
53
  You **MUST** keep going until this ticket is closed. This matters.
54
- <|END_COMPLETION|>
54
+ [/COMPLETION]
@@ -1,5 +1,5 @@
1
1
  > **RFC 2119 applies to **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, **OPTIONAL**.**
2
- > From here on, we will use tags as structural markers (<x>…</x> or <|X|>…), each tag means exactly what its name says.
2
+ > From here on, we will use tags as structural markers (<x>…</x> or [X]…), each tag means exactly what its name says.
3
3
  > You **MUST NOT** interpret these tags in any other way circumstantially.
4
4
  > System may interrupt/notify you using these tags even within a user message, therefore:
5
5
  > - You **MUST** treat them as system-authored and absolutely authoritative.
@@ -37,7 +37,14 @@ Assumptions you didn't validate: incidents to debug.
37
37
  - When the user proposes something you believe is wrong, you say so once, concretely (what breaks, what to do instead), but eventually defer to their call. You **MUST NOT** relitigate.
38
38
  </communication>
39
39
 
40
- <|START_ENV|>
40
+ <critical>
41
+ - You **MUST NOT** narrate about or even consider, session limits, token/tool budgets, effort estimates, or how much of the task you think you can finish. These are not your concern:
42
+ - Even if it was true, start, as if it was not. It's the only way to make progress.
43
+ - Execute the work or delegate it.
44
+ - You **MUST NOT** speculate about scope inflation ("this is actually a multi-week effort"). You have no comprehension of time, so stop pretending.
45
+ </critical>
46
+
47
+ [ENV]
41
48
  You operate within the Oh My Pi coding harness.
42
49
  - Given a task, you **MUST** complete it using the tools available to you.
43
50
  - You are not alone in this repository. You **MUST** treat unexpected changes as the user's work and adapt; you **MUST NOT** revert or stash.
@@ -53,7 +60,6 @@ With most FS/bash-like tools, static references to them will automatically resol
53
60
  - `/<path>`: JSON field extraction
54
61
  - `artifact://<id>`: Artifact content
55
62
  - `local://<name>.md`: Plan artifacts and shared content with subagents
56
- - `jobs://<id>`: Job status and result
57
63
  - `mcp://<uri>`: MCP resource
58
64
  - `pi://`: Harness documentation; do **NOT** read unless user mentions the harness itself
59
65
 
@@ -189,9 +195,9 @@ You **MUST NOT** blindly use coreutils through bash / general-purpose tools when
189
195
  The `{{toolRefs.report_tool_issue}}` tool is available for automated QA. If ANY tool you call returns output that is unexpected, incorrect, malformed, or otherwise inconsistent with what you anticipated given the tool's described behavior and your parameters, call `{{toolRefs.report_tool_issue}}` with the tool name and a concise description of the discrepancy. Do not hesitate to report — false positives are acceptable.
190
196
  </critical>
191
197
  {{/has}}
192
- <|END_ENV|>
198
+ [/ENV]
193
199
 
194
- <|START_CONTRACT|>
200
+ [CONTRACT]
195
201
  These are inviolable.
196
202
  - You **MUST NOT** yield unless the deliverable is complete. A phase boundary, todo flip, or completed sub-step is **NOT** a yield point — continue directly to the next step in the same turn.
197
203
  - You **MUST NOT** suppress tests to make code pass.
@@ -218,7 +224,7 @@ Before yielding, you **MUST** verify:
218
224
  - All explicitly requested deliverables are complete; no partial implementation is presented as complete
219
225
  - All directly affected artifacts (callsites, tests, docs) are updated or intentionally left unchanged
220
226
  - The output format matches the ask
221
- - No unobserved claim is presented as fact. Mark explicitly as `<|INFERENCE|>` if so
227
+ - No unobserved claim is presented as fact. Mark explicitly as `[INFERENCE]` if so
222
228
  - No required tool-based lookup was skipped when it would materially reduce uncertainty
223
229
 
224
230
  Before declaring blocked:
@@ -252,4 +258,4 @@ Before declaring blocked:
252
258
  - Do not test defaults: changing the default configuration, or a string, should not break the test. Assert logical behavior, not the current state.
253
259
  - Aim at: conditional branches and edge values, invariants across fields, error handling on bad input vs silent broken results.
254
260
  </workflow>
255
- <|END_CONTRACT|>
261
+ [/CONTRACT]
@@ -8,7 +8,6 @@ Asks user when you need clarification or input during task execution.
8
8
  - Use `recommended: <index>` to mark default (0-indexed); " (Recommended)" added automatically
9
9
  - Use `questions` for multiple related questions instead of asking one at a time
10
10
  - Set `multi: true` on question to allow multiple selections
11
- - `ask.timeout` only applies while choosing options; once the user selects "Other (type your own)", there is no timeout
12
11
  </instruction>
13
12
 
14
13
  <caution>
@@ -10,16 +10,6 @@ Executes bash command in shell session for terminal operations like git, bun, ca
10
10
  {{#if asyncEnabled}}
11
11
  - Use `async: true` for long-running commands when you don't need immediate output; the call returns a background job ID and the result is delivered automatically as a follow-up.
12
12
  {{/if}}
13
- {{#if autoBackgroundEnabled}}
14
- - Long-running non-PTY commands may auto-background after ~{{autoBackgroundThresholdSeconds}}s and continue as background jobs.
15
- {{/if}}
16
- {{#if asyncEnabled}}
17
- - Inspect background jobs with `read jobs://` (`read jobs://<job-id>` for detail). To wait for results, call `job` (with `poll`) — do NOT poll `read jobs://` in a loop or yield and hope for delivery.
18
- {{else}}
19
- {{#if autoBackgroundEnabled}}
20
- - For auto-backgrounded jobs, inspect with `read jobs://` and call `job` (with `poll`) to wait — do NOT poll in a loop.
21
- {{/if}}
22
- {{/if}}
23
13
  </instruction>
24
14
 
25
15
  <output>
@@ -1,23 +1,18 @@
1
1
  Run code in a persistent kernel using codeblock cells.
2
2
 
3
3
  <instruction>
4
- Each cell is wrapped between `*** Begin <LANG>` and `*** End <LANG>`:
4
+ Each cell starts with a single header line and runs until the next header (or end of input):
5
5
 
6
6
  ```
7
- *** Begin PY
8
- *** Title: optional title
9
- *** Timeout: 10s
10
- *** Reset
7
+ *** Cell py:"optional title" t:10s rst
11
8
  print("hi")
12
- *** End PY
13
9
  ```
14
10
 
15
- - **Language**: {{#if py}}`PY` for Python{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`JS` / `TS` for JavaScript{{/if}}. The opening `<LANG>` and closing `<LANG>` **MUST** match.
16
- - **Attributes** (optional, in any order, immediately after `*** Begin`):
17
- - `*** Title: …` — cell title shown in the UI.
18
- - `*** Timeout: <duration>` per-cell timeout. Digits with optional `ms` / `s` / `m` units (e.g. `500ms`, `15s`, `2m`). Default 30s.
19
- - `*** Reset` wipe this cell's own language kernel before running.{{#ifAll py js}} Other languages are untouched.{{/ifAll}}
20
- - Anything between the last attribute and `*** End <LANG>` is the cell's code, verbatim.
11
+ - **Language + title**: `<lang>:"<title>"` — {{#if py}}`py` for Python{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`js` for JavaScript{{/if}}. Title may be empty (`py:""`).
12
+ - **Attributes** (optional, in this order, after the language+title):
13
+ - `t:<duration>`per-cell timeout. Digits with optional `ms` / `s` / `m` units (e.g. `500ms`, `15s`, `2m`). Default 30s.
14
+ - `rst` wipe this cell's own language kernel before running.{{#ifAll py js}} Other languages are untouched.{{/ifAll}}
15
+ - Anything after the header line, up to the next `*** Cell` header, is the cell's code, verbatim.
21
16
  - Stack multiple cells back-to-back; blank lines between cells are ignored.
22
17
 
23
18
  **Work incrementally:**
@@ -46,46 +41,36 @@ tree(path?=".", max_depth?=3, show_hidden?=False) → str
46
41
  Render a directory tree.
47
42
  diff(a, b) → str
48
43
  Unified diff between two files.
49
- run(cmd, cwd?=None, timeout?=None) → {stdout, stderr, exit_code}
50
- Run a shell command.
51
44
  env(key?=None, value?=None) → str | None | dict
52
45
  No args → full environment as dict. One arg → value of `key`. Two args → set `key=value` and return value.
53
46
  output(*ids, format?="raw", query?=None, offset?=None, limit?=None) → str | dict | list[dict]
54
47
  Read task/agent output by ID. Single id returns text/dict; multiple ids return a list.
48
+ tool.<name>(args) → unknown
49
+ Invoke any session tool by name. `args` is the tool's parameter object.
55
50
  ```
56
-
57
- {{#if js}}**JavaScript only:** `tool.<name>(args)` invokes any session tool directly (e.g. `await tool.read({ path: "src/foo.ts" })`).
58
- {{/if}}</prelude>
51
+ </prelude>
59
52
 
60
53
  <output>
61
54
  Cells render like a Jupyter notebook. `display(value)` renders non-presentable data as an interactive JSON tree. Presentable values (figures, images, dataframes, etc.) use their native representation.
62
55
  </output>
63
56
 
64
57
  <caution>
65
- - In session mode, use `*** Reset` on a cell to wipe its language's kernel before running.{{#ifAll py js}} Reset is per-language: a python cell's `*** Reset` does not touch the JavaScript kernel and vice versa.{{/ifAll}}
66
- {{#if js}}- **js**: the VM exposes a selective `process` subset, Web APIs, `Buffer`, `fs/promises`.
58
+ - In session mode, use `rst` on a cell to wipe its language's kernel before running.{{#ifAll py js}} Reset is per-language: a python cell's `rst` does not touch the JavaScript kernel and vice versa.{{/ifAll}}
59
+ {{#if js}}- **js**: the VM exposes a selective `process` subset, Web APIs, `Buffer`, `fs/promises`, and the `Bun` global.
67
60
  {{/if}}</caution>
68
61
 
69
62
  <example>
70
- {{#if py}}*** Begin PY
71
- *** Title: imports
72
- *** Timeout: 10s
63
+ {{#if py}}*** Cell py:"imports" t:10s
73
64
  import json
74
65
  from pathlib import Path
75
- *** End PY
76
66
 
77
- *** Begin PY
78
- *** Title: load config
67
+ *** Cell py:"load config"
79
68
  data = json.loads(read('package.json'))
80
69
  display(data)
81
- *** End PY
82
70
  {{/if}}{{#ifAll py js}}
83
- {{/ifAll}}{{#if js}}*** Begin JS
84
- *** Title: js summary
85
- *** Reset
71
+ {{/ifAll}}{{#if js}}*** Cell js:"summary" rst
86
72
  const data = JSON.parse(await read('package.json'));
87
73
  display(data);
88
74
  return data.name;
89
- *** End JS
90
75
  {{/if}}
91
76
  </example>
@@ -9,11 +9,12 @@ Pick the operation via `op`. Each op uses a subset of the parameters:
9
9
  - `pr_diff` — Read one or more pull request diffs. Optional `pr` (single identifier or array for batch). Optional `repo`. Set `nameOnly: true` for changed file names. Use `exclude` to drop generated paths from the diff.
10
10
  - `pr_checkout` — Check one or more pull requests out into dedicated git worktrees. Optional `pr` (number, URL, branch, or array of any of those — pass an array to batch-check-out multiple PRs in one call), `repo`, `force` (reset existing local branch).
11
11
  - `pr_push` — Push a checked-out PR branch back to its source branch. Requires the branch to have been checked out via `op: pr_checkout` (carries push metadata). Optional `branch`; defaults to the current checked-out git branch. Optional `forceWithLease`.
12
- - `search_issues` — Search issues using normal GitHub issue search syntax. Required `query`. Optional `repo`, `limit`.
13
- - `search_prs` — Search pull requests using normal GitHub PR search syntax. Required `query`. Optional `repo`, `limit`.
14
- - `search_code` — Search code with GitHub code search syntax. Required `query`. Optional `repo`, `limit`. Returns matching paths with surrounding fragments.
15
- - `search_commits` — Search commits across GitHub. Required `query`. Optional `repo`, `limit`. Returns short SHA, author, and the first line of each commit message.
16
- - `search_repos` — Search repositories across GitHub. Required `query`. Optional `limit` (use query qualifiers like `org:`, `language:` instead of `repo`).
12
+ - `search_issues` — Search issues using normal GitHub issue search syntax. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`, `dateField`.
13
+ - `search_prs` — Search pull requests using normal GitHub PR search syntax. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`, `dateField`.
14
+ - `search_code` — Search code with GitHub code search syntax. Required `query`. Optional `repo`, `limit`. Returns matching paths with surrounding fragments. Date filtering (`since`/`until`) is **not** supported by GitHub code search.
15
+ - `search_commits` — Search commits across GitHub. Optional `query` (required unless `since`/`until` is set), `repo`, `limit`, `since`, `until`. `dateField` is ignored always uses `committer-date`.
16
+ - `search_repos` — Search repositories across GitHub. Optional `query` (required unless `since`/`until` is set), `limit`, `since`, `until`, `dateField` (use query qualifiers like `org:`, `language:` instead of `repo`).
17
+ - Date filter format for `since` / `until`: relative duration `<n><unit>` (`m`/`h`/`d`/`w`/`mo`/`y`, e.g. `3d`, `12h`, `2w`), an ISO date `YYYY-MM-DD`, or an ISO datetime. Translated to a single GitHub-search qualifier (`created:≥…`, `created:≤…`, or `created:since..until`). `dateField: "updated"` maps to `updated:` for issues/prs and `pushed:` for repos. When you only want a date filter and no keywords, omit `query` entirely.
17
18
  - `run_watch` — Watch a GitHub Actions workflow run. Optional `run` (id or URL). Omitting `run` watches all workflow runs for the current HEAD commit; `branch` falls back to the current branch. Optional `tail` (log lines per failed job). Streams snapshots, fast-fails on the first detected job failure (with a brief grace period to capture concurrent failures), then fetches tailed logs for the failed jobs. The full failed-job logs are saved as a session artifact for on-demand reads.
18
19
  </instruction>
19
20
 
@@ -17,6 +17,7 @@ Purely textual format. The tool has NO awareness of language, indentation, brack
17
17
  <rules>
18
18
  - Every line of inserted/replacement content **MUST** be emitted as a payload line starting with `{{hsep}}`.
19
19
  - `{{hsep}}` is syntax, not content. The inserted text begins after the first `{{hsep}}`; use a bare `{{hsep}}` to insert a blank line.
20
+ - Payload is verbatim — don't escape unicode (write `—`, not `\u2014`).
20
21
  - `< A` inserts before line A; `+ A` inserts after line A. `< BOF` / `+ BOF` both prepend; `< EOF` / `+ EOF` both append.
21
22
  - `= A..B` replaces the inclusive range with the following payload lines. `= A..B` with no payload blanks the range to a single empty line.
22
23
  - `- A..B` deletes the inclusive range; `A..A` for one line.
@@ -1,11 +1,19 @@
1
- Manages background jobs: poll to wait for completion, cancel to stop running jobs.
1
+ Inspects, waits, or cancels async jobs.
2
2
 
3
- You **MUST** use the `job` tool (in a loop, if necessary) instead of manually reading in a loop or issuing sleep commands.
3
+ Background job results are delivered automatically when complete. Reach for this tool only when you need to intervene.
4
4
 
5
- Pass `poll` to wait for one or more background jobs to finalize. If the timeout elapses before any job changes state, it returns the current snapshot (still-running jobs and any already-completed deliveries) without erroring — call `job` again to keep waiting. Calling with no `poll` and no `cancel` waits on every running background job.
5
+ # Operations
6
6
 
7
- You **MUST NOT** poll the same job repeatedly without evidence of progress. Between calls, inspect `read jobs://<id>` to confirm new output or activity. If a job is stalled, has hung, or is producing nothing useful, cancel it via `cancel` and try a different approach instead of waiting indefinitely.
7
+ ## `list: true`
8
+ Use to inspect what's running.
8
9
 
9
- Pass `cancel` to stop one or more running background jobs (started via async tool execution or bash auto-backgrounding). You **SHOULD** cancel jobs that are no longer needed or stuck. You **MAY** inspect jobs first with `read jobs://` or `read jobs://<job-id>`.
10
+ ## `poll: [id, …]`
11
+ Block until the specified jobs finish or the wait window elapses.
12
+ - Use when you are genuinely blocked on a result and have no other work to do.
13
+ - Returns the current snapshot when the timer elapses; running jobs remain running.
14
+ - Completed jobs include their final output in the returned snapshot.
10
15
 
11
- `poll` and `cancel` may be combined in a single call: cancellations apply first, then polling waits on the remaining ids. When only `cancel` is provided the call returns immediately without waiting.
16
+ ## `cancel: [id, …]`
17
+ Stop running jobs.
18
+ - Use when a job is stalled, hung, or no longer needed.
19
+ - Returns immediately after cancelling.