@oh-my-pi/pi-coding-agent 8.5.0 → 8.8.8

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 (71) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +2 -2
  3. package/docs/rpc.md +32 -15
  4. package/docs/session.md +3 -5
  5. package/docs/tui.md +3 -3
  6. package/package.json +6 -8
  7. package/src/cli/args.ts +4 -0
  8. package/src/commit/agentic/prompts/analyze-file.md +1 -1
  9. package/src/commit/agentic/tools/analyze-file.ts +1 -1
  10. package/src/config/prompt-templates.ts +3 -0
  11. package/src/config/settings-manager.ts +19 -1
  12. package/src/export/html/template.css +9 -0
  13. package/src/export/html/template.generated.ts +1 -1
  14. package/src/export/html/template.js +6 -4
  15. package/src/extensibility/extensions/index.ts +2 -0
  16. package/src/extensibility/extensions/runner.ts +13 -2
  17. package/src/extensibility/extensions/types.ts +17 -2
  18. package/src/extensibility/hooks/runner.ts +13 -2
  19. package/src/extensibility/hooks/types.ts +22 -3
  20. package/src/ipy/executor.ts +2 -3
  21. package/src/ipy/kernel.ts +0 -42
  22. package/src/main.ts +3 -1
  23. package/src/mcp/json-rpc.ts +2 -5
  24. package/src/mcp/transports/http.ts +24 -11
  25. package/src/mcp/transports/stdio.ts +17 -12
  26. package/src/migrations.ts +15 -6
  27. package/src/modes/components/compaction-summary-message.ts +3 -0
  28. package/src/modes/components/model-selector.ts +24 -5
  29. package/src/modes/components/user-message.ts +7 -3
  30. package/src/modes/controllers/command-controller.ts +35 -1
  31. package/src/modes/controllers/event-controller.ts +5 -2
  32. package/src/modes/controllers/input-controller.ts +6 -1
  33. package/src/modes/interactive-mode.ts +6 -0
  34. package/src/modes/rpc/rpc-client.ts +51 -10
  35. package/src/modes/rpc/rpc-mode.ts +25 -19
  36. package/src/modes/types.ts +1 -0
  37. package/src/modes/utils/ui-helpers.ts +2 -2
  38. package/src/prompts/agents/explore.md +1 -1
  39. package/src/prompts/agents/plan.md +1 -1
  40. package/src/prompts/agents/reviewer.md +5 -3
  41. package/src/prompts/compaction/branch-summary-context.md +5 -0
  42. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  43. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  44. package/src/prompts/compaction/compaction-summary.md +13 -7
  45. package/src/prompts/compaction/compaction-update-summary.md +6 -2
  46. package/src/prompts/review-request.md +1 -1
  47. package/src/prompts/system/subagent-system-prompt.md +24 -0
  48. package/src/prompts/system/system-prompt.md +1 -0
  49. package/src/prompts/tools/task-summary.md +35 -0
  50. package/src/prompts/tools/task.md +28 -30
  51. package/src/prompts/tools/todo-write.md +3 -4
  52. package/src/sdk.ts +5 -4
  53. package/src/session/agent-session.ts +168 -8
  54. package/src/session/compaction/branch-summarization.ts +1 -1
  55. package/src/session/compaction/compaction.ts +154 -0
  56. package/src/session/compaction/pruning.ts +91 -0
  57. package/src/session/messages.ts +32 -17
  58. package/src/session/session-manager.ts +134 -80
  59. package/src/task/executor.ts +559 -495
  60. package/src/task/index.ts +39 -18
  61. package/src/task/render.ts +101 -12
  62. package/src/task/types.ts +3 -3
  63. package/src/tools/gemini-image.ts +22 -23
  64. package/src/tools/grep.ts +58 -26
  65. package/src/tools/index.ts +12 -14
  66. package/src/tools/jtd-to-typescript.ts +198 -0
  67. package/src/tools/review.ts +3 -3
  68. package/src/tools/{complete.ts → submit-result.ts} +10 -10
  69. package/src/tools/todo-write.ts +7 -7
  70. package/src/task/worker-protocol.ts +0 -131
  71. package/src/task/worker.ts +0 -921
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [8.8.8] - 2026-01-28
6
+
7
+ ### Added
8
+ - Added `/fork` command to create a new session with the exact same state (entries and artifacts) as the current session
9
+
10
+ ### Changed
11
+ - Renamed the `complete` tool to `submit_result` for subagent result submission
12
+
13
+ ## [8.6.0] - 2026-01-27
14
+
15
+ ### Added
16
+ - Added `plan` model role for specifying the model used by the plan agent
17
+ - Added `--plan` CLI flag and `OMP_PLAN_MODEL` environment variable for ephemeral plan model override
18
+ - Added plan model selection in model selector UI with PLAN badge
19
+
20
+ ### Changed
21
+ - Task tool subagents now execute in-process instead of using worker threads
22
+
23
+ ### Fixed
24
+ - Queued skill commands as follow-ups when the agent is already streaming to avoid load failures
25
+ - Deduplicated repeated review findings in subagent progress rendering
26
+ - Restored MCP proxy tool timeout handling to prevent subagent hangs
27
+
5
28
  ## [8.5.0] - 2026-01-27
6
29
 
7
30
  ### Added
package/README.md CHANGED
@@ -263,7 +263,7 @@ The agent reads, writes, and edits files, and executes commands via bash.
263
263
  | Ctrl+D | Exit (when editor is empty) |
264
264
  | Ctrl+Z | Suspend to background (use `fg` in shell to resume) |
265
265
  | Shift+Tab | Cycle thinking level |
266
- | Ctrl+P / Shift+Ctrl+P | Cycle role models (slow/default/smol) |
266
+ | Ctrl+P / Shift+Ctrl+P | Cycle role models (slow/default/smol/plan) |
267
267
  | Ctrl+L | Open model selector |
268
268
  | Ctrl+O | Toggle tool output expansion |
269
269
  | Ctrl+T | Toggle todo list expansion |
@@ -564,7 +564,7 @@ Global `~/.omp/agent/settings.json` stores persistent preferences:
564
564
  | Setting | Description | Default |
565
565
  | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------- |
566
566
  | `theme` | Color theme name | auto-detected |
567
- | `modelRoles` | Model assignments by role (e.g., `{"default": "anthropic/claude-sonnet-4-20250514", "slow": "...", "smol": "..."}`) | - |
567
+ | `modelRoles` | Model assignments by role (e.g., `{"default": "...", "slow": "...", "smol": "...", "plan": "..."}`) | - |
568
568
  | `defaultThinkingLevel` | Thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | - |
569
569
  | `enabledModels` | Model patterns for cycling. Supports glob patterns (`github-copilot/*`, `*sonnet*`) and fuzzy matching. Same as `--models` CLI flag | - |
570
570
  | `queueMode` | Message queue mode: `all` or `one-at-a-time` | `one-at-a-time` |
package/docs/rpc.md CHANGED
@@ -23,6 +23,8 @@ Common options:
23
23
  - **Responses**: JSON objects with `type: "response"` indicating command success/failure
24
24
  - **Events**: Agent events streamed to stdout as JSON lines
25
25
 
26
+ If you're consuming output in Bun, prefer `Bun.JSONL.parse(text)` for buffered JSONL or `Bun.JSONL.parseChunk()` for streaming output instead of splitting and `JSON.parse`.
27
+
26
28
  All commands support an optional `id` field for request/response correlation. If provided, the corresponding response will include the same `id`.
27
29
 
28
30
  ## Commands
@@ -1034,6 +1036,7 @@ Created by the `bash` RPC command (not by LLM tool calls):
1034
1036
  ```python
1035
1037
  import subprocess
1036
1038
  import json
1039
+ import jsonlines
1037
1040
 
1038
1041
  proc = subprocess.Popen(
1039
1042
  ["omp", "--mode", "rpc", "--no-session"],
@@ -1047,8 +1050,9 @@ def send(cmd):
1047
1050
  proc.stdin.flush()
1048
1051
 
1049
1052
  def read_events():
1050
- for line in proc.stdout:
1051
- yield json.loads(line)
1053
+ with jsonlines.Reader(proc.stdout) as reader:
1054
+ for event in reader:
1055
+ yield event
1052
1056
 
1053
1057
  # Send prompt
1054
1058
  send({"type": "prompt", "message": "Hello!"})
@@ -1065,26 +1069,39 @@ for event in read_events():
1065
1069
  break
1066
1070
  ```
1067
1071
 
1068
- ## Example: Interactive Client (Node.js)
1072
+ ## Example: Interactive Client (Bun)
1069
1073
 
1070
1074
  See [`test/rpc-example.ts`](../test/rpc-example.ts) for a complete interactive example, or [`src/modes/rpc/rpc-client.ts`](../src/modes/rpc/rpc-client.ts) for a typed client implementation.
1071
1075
 
1072
1076
  ```javascript
1073
- const { spawn } = require("child_process");
1074
- const readline = require("readline");
1075
-
1076
- const agent = spawn("omp", ["--mode", "rpc", "--no-session"]);
1077
-
1078
- readline.createInterface({ input: agent.stdout }).on("line", (line) => {
1079
- const event = JSON.parse(line);
1077
+ const agent = Bun.spawn(["omp", "--mode", "rpc", "--no-session"], {
1078
+ stdin: "pipe",
1079
+ stdout: "pipe",
1080
+ });
1080
1081
 
1081
- if (event.type === "message_update") {
1082
- const { assistantMessageEvent } = event;
1083
- if (assistantMessageEvent.type === "text_delta") {
1084
- process.stdout.write(assistantMessageEvent.delta);
1082
+ const decoder = new TextDecoder();
1083
+ let buffer = "";
1084
+
1085
+ async function readEvents() {
1086
+ const reader = agent.stdout.getReader();
1087
+ while (true) {
1088
+ const { value, done } = await reader.read();
1089
+ if (done) break;
1090
+ buffer += decoder.decode(value, { stream: true });
1091
+ const result = Bun.JSONL.parseChunk(buffer);
1092
+ buffer = buffer.slice(result.read);
1093
+ for (const event of result.values) {
1094
+ if (event.type === "message_update") {
1095
+ const { assistantMessageEvent } = event;
1096
+ if (assistantMessageEvent.type === "text_delta") {
1097
+ process.stdout.write(assistantMessageEvent.delta);
1098
+ }
1099
+ }
1085
1100
  }
1086
1101
  }
1087
- });
1102
+ }
1103
+
1104
+ readEvents();
1088
1105
 
1089
1106
  // Send prompt
1090
1107
  agent.stdin.write(JSON.stringify({ type: "prompt", message: "Hello" }) + "\n");
package/docs/session.md CHANGED
@@ -227,12 +227,10 @@ Entries form a tree:
227
227
  ## Parsing Example
228
228
 
229
229
  ```typescript
230
- import * as fs from "node:fs";
230
+ const text = await Bun.file("session.jsonl").text();
231
+ const entries = Bun.JSONL.parse(text);
231
232
 
232
- const lines = fs.readFileSync("session.jsonl", "utf8").trim().split("\n");
233
-
234
- for (const line of lines) {
235
- const entry = JSON.parse(line);
233
+ for (const entry of entries) {
236
234
 
237
235
  switch (entry.type) {
238
236
  case "session":
package/docs/tui.md CHANGED
@@ -153,7 +153,7 @@ handleInput(data: string) {
153
153
 
154
154
  ## Line Width
155
155
 
156
- **Critical:** Each line from `render()` must not exceed the `width` parameter.
156
+ **Critical:** Each line from `render()` must not exceed the `width` parameter. Width calculations and wrapping follow Bun’s built-ins (`Bun.stringWidth`, `Bun.wrapAnsi`).
157
157
 
158
158
  ```typescript
159
159
  import { visibleWidth, truncateToWidth } from "@oh-my-pi/pi-tui";
@@ -166,9 +166,9 @@ render(width: number): string[] {
166
166
 
167
167
  Utilities:
168
168
 
169
- - `visibleWidth(str)` - Get display width (ignores ANSI codes)
169
+ - `visibleWidth(str)` - Get display width (ANSI-safe, Unicode-width aware)
170
170
  - `truncateToWidth(str, width, ellipsis?)` - Truncate with optional ellipsis
171
- - `wrapTextWithAnsi(str, width)` - Word wrap preserving ANSI codes
171
+ - `wrapTextWithAnsi(str, width)` - Word wrap preserving ANSI codes (Bun.wrapAnsi)
172
172
 
173
173
  ## Creating Custom Components
174
174
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-coding-agent",
3
- "version": "8.5.0",
3
+ "version": "8.8.8",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "ompConfig": {
@@ -83,11 +83,11 @@
83
83
  "test": "bun test"
84
84
  },
85
85
  "dependencies": {
86
- "@oh-my-pi/omp-stats": "8.5.0",
87
- "@oh-my-pi/pi-agent-core": "8.5.0",
88
- "@oh-my-pi/pi-ai": "8.5.0",
89
- "@oh-my-pi/pi-tui": "8.5.0",
90
- "@oh-my-pi/pi-utils": "8.5.0",
86
+ "@oh-my-pi/omp-stats": "8.8.8",
87
+ "@oh-my-pi/pi-agent-core": "8.8.8",
88
+ "@oh-my-pi/pi-ai": "8.8.8",
89
+ "@oh-my-pi/pi-tui": "8.8.8",
90
+ "@oh-my-pi/pi-utils": "8.8.8",
91
91
  "@openai/agents": "^0.4.3",
92
92
  "@sinclair/typebox": "^0.34.46",
93
93
  "ajv": "^8.17.1",
@@ -100,7 +100,6 @@
100
100
  "highlight.js": "^11.11.1",
101
101
  "marked": "^17.0.1",
102
102
  "nanoid": "^5.1.6",
103
- "ndjson": "^2.0.0",
104
103
  "node-html-parser": "^7.0.2",
105
104
  "smol-toml": "^1.6.0",
106
105
  "strip-ansi": "^7.1.2",
@@ -109,7 +108,6 @@
109
108
  "devDependencies": {
110
109
  "@types/diff": "^8.0.0",
111
110
  "@types/ms": "^2.1.0",
112
- "@types/ndjson": "^2.0.4",
113
111
  "@types/node": "^25.0.10",
114
112
  "ms": "^2.1.3"
115
113
  },
package/src/cli/args.ts CHANGED
@@ -15,6 +15,7 @@ export interface Args {
15
15
  model?: string;
16
16
  smol?: string;
17
17
  slow?: string;
18
+ plan?: string;
18
19
  apiKey?: string;
19
20
  systemPrompt?: string;
20
21
  appendSystemPrompt?: string;
@@ -85,6 +86,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
85
86
  result.smol = args[++i];
86
87
  } else if (arg === "--slow" && i + 1 < args.length) {
87
88
  result.slow = args[++i];
89
+ } else if (arg === "--plan" && i + 1 < args.length) {
90
+ result.plan = args[++i];
88
91
  } else if (arg === "--api-key" && i + 1 < args.length) {
89
92
  result.apiKey = args[++i];
90
93
  } else if (arg === "--system-prompt" && i + 1 < args.length) {
@@ -193,6 +196,7 @@ ${chalk.bold("Options:")}
193
196
  --model <pattern> Model to use (fuzzy match: "opus", "gpt-5.2", or "p-openai/gpt-5.2")
194
197
  --smol <id> Smol/fast model for lightweight tasks (or OMP_SMOL_MODEL env)
195
198
  --slow <id> Slow/reasoning model for thorough analysis (or OMP_SLOW_MODEL env)
199
+ --plan <id> Plan model for architectural planning (or OMP_PLAN_MODEL env)
196
200
  --api-key <key> API key (defaults to env vars)
197
201
  --system-prompt <text> System prompt (default: coding assistant prompt)
198
202
  --append-system-prompt <text> Append text or file contents to the system prompt
@@ -19,4 +19,4 @@ Return a concise JSON object with:
19
19
  Consider how this file's changes relate to the above files.
20
20
  {{/if}}
21
21
 
22
- Call the complete tool with the JSON payload.
22
+ Call the submit_result tool with the JSON payload.
@@ -82,7 +82,7 @@ export function createAnalyzeFileTool(options: {
82
82
  const taskParams: TaskParams = {
83
83
  agent: "quick_task",
84
84
  context,
85
- output: analyzeFileOutputSchema,
85
+ schema: analyzeFileOutputSchema,
86
86
  tasks,
87
87
  };
88
88
  return taskTool.execute(toolCallId, taskParams, signal, onUpdate);
@@ -1,4 +1,5 @@
1
1
  import * as path from "node:path";
2
+ import { jtdToTypeScript } from "@oh-my-pi/pi-coding-agent/tools/jtd-to-typescript";
2
3
  import { logger } from "@oh-my-pi/pi-utils";
3
4
  import Handlebars from "handlebars";
4
5
  import { CONFIG_DIR_NAME, getPromptsDir } from "../config";
@@ -222,6 +223,8 @@ handlebars.registerHelper("includes", (collection: unknown, item: unknown): bool
222
223
  */
223
224
  handlebars.registerHelper("not", (value: unknown): boolean => !value);
224
225
 
226
+ handlebars.registerHelper("jtdToTypeScript", (schema: unknown): string => jtdToTypeScript(schema));
227
+
225
228
  export function renderPromptTemplate(template: string, context: TemplateContext = {}): string {
226
229
  const compiled = handlebars.compile(template, { noEscape: true, strict: false });
227
230
  const rendered = compiled(context ?? {});
@@ -13,6 +13,8 @@ export interface CompactionSettings {
13
13
  enabled?: boolean; // default: true
14
14
  reserveTokens?: number; // default: 16384
15
15
  keepRecentTokens?: number; // default: 20000
16
+ autoContinue?: boolean; // default: true
17
+ remoteEndpoint?: string;
16
18
  }
17
19
 
18
20
  export interface BranchSummarySettings {
@@ -838,11 +840,27 @@ export class SettingsManager {
838
840
  return this.settings.compaction?.keepRecentTokens ?? 20000;
839
841
  }
840
842
 
841
- getCompactionSettings(): { enabled: boolean; reserveTokens: number; keepRecentTokens: number } {
843
+ getCompactionAutoContinue(): boolean {
844
+ return this.settings.compaction?.autoContinue ?? true;
845
+ }
846
+
847
+ getCompactionRemoteEndpoint(): string | undefined {
848
+ return this.settings.compaction?.remoteEndpoint;
849
+ }
850
+
851
+ getCompactionSettings(): {
852
+ enabled: boolean;
853
+ reserveTokens: number;
854
+ keepRecentTokens: number;
855
+ autoContinue: boolean;
856
+ remoteEndpoint?: string;
857
+ } {
842
858
  return {
843
859
  enabled: this.getCompactionEnabled(),
844
860
  reserveTokens: this.getCompactionReserveTokens(),
845
861
  keepRecentTokens: this.getCompactionKeepRecentTokens(),
862
+ autoContinue: this.getCompactionAutoContinue(),
863
+ remoteEndpoint: this.getCompactionRemoteEndpoint(),
846
864
  };
847
865
  }
848
866
 
@@ -137,6 +137,15 @@
137
137
  }
138
138
 
139
139
  .tree-node.in-path {
140
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
141
+ }
142
+
143
+ .tree-node:not(.in-path) {
144
+ opacity: 0.5;
145
+ }
146
+
147
+ .tree-node:not(.in-path):hover {
148
+ opacity: 1;
140
149
  }
141
150
 
142
151
  .tree-prefix {