@itsaplan/runner 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +19 -17
  2. package/dist/cli.js +66 -4
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -8,7 +8,8 @@ This package runs such agents on your own machine — one, or several at once.
8
8
  - Polls your instance for the agent's queued runs. Runs each one with a coding agent CLI.
9
9
  Reports the result.
10
10
  - Has presets for Claude Code, Codex, opencode, Antigravity CLI, GitHub Copilot CLI,
11
- Pi, and Oh My Pi (`omp`). Each preset sets the unattended flags and the session resume.
11
+ Pi, Oh My Pi (`omp`), and Mastra Code. Each preset sets the unattended flags and the
12
+ session resume.
12
13
  - Runs your own `command` instead, if you prefer. The command takes the task on stdin.
13
14
  - Answers the agent's chat. Sends the text and the tool calls while the CLI prints them.
14
15
  - Sends the messages and the tool calls of a queued run to the issue while the run works.
@@ -23,9 +24,9 @@ of kind External. The key appears one time only, at creation.
23
24
  **2. Enable MCP** for the project, in Settings, MCP Server. MCP is off by default. The
24
25
  agent reads the issue and writes its result through it.
25
26
 
26
- **3. Give the coding agent the address of that server.** Claude Code, Oh My Pi, and Pi
27
- (through `pi-mcp-adapter`) read `.mcp.json` from the folder they run in. For the other
28
- CLIs, see
27
+ **3. Give the coding agent the address of that server.** Claude Code, Oh My Pi, Mastra
28
+ Code, and Pi (through `pi-mcp-adapter`) read `.mcp.json` from the folder they run in. For
29
+ the other CLIs, see
29
30
  [Coding agent setup](https://github.com/croffasia/itsaplan/blob/main/docs/runner.md).
30
31
 
31
32
  ```json
@@ -67,15 +68,16 @@ or mention it in a comment. The task then starts in your terminal.
67
68
  `agent` names a CLI that the runner knows. The runner then builds the command itself: the
68
69
  flags for an unattended run, and the session resume. Every preset keeps a chat session.
69
70
 
70
- | Agent | Runs |
71
- | ------------- | ------------------ |
72
- | `claude` | Claude Code |
73
- | `codex` | Codex CLI |
74
- | `opencode` | opencode |
75
- | `antigravity` | Antigravity CLI |
76
- | `copilot` | GitHub Copilot CLI |
77
- | `pi` | Pi |
78
- | `omp` | Oh My Pi |
71
+ | Agent | Runs |
72
+ | ------------- | ---------------------------------------------------------------------------- |
73
+ | `claude` | [Claude Code](https://code.claude.com/docs/en/overview) |
74
+ | `codex` | [Codex CLI](https://github.com/openai/codex) |
75
+ | `opencode` | [opencode](https://opencode.ai) |
76
+ | `antigravity` | [Antigravity CLI](https://antigravity.google/docs/getting-started?tab=cli) |
77
+ | `copilot` | [GitHub Copilot CLI](https://github.com/github/copilot-cli) |
78
+ | `pi` | [Pi](https://pi.dev) |
79
+ | `omp` | [Oh My Pi](https://omp.sh) |
80
+ | `mastracode` | [Mastra Code](https://code.mastra.ai) |
79
81
 
80
82
  Each preset needs the CLI's own MCP config.
81
83
  [Coding agent setup](https://github.com/croffasia/itsaplan/blob/main/docs/runner.md) holds
@@ -273,8 +275,8 @@ npx -y @itsaplan/runner --agent claude
273
275
 
274
276
  The server writes the task text. The text states what occurred, what to do, and to post
275
277
  the result as a comment on the issue. Your own `command` receives the text on **stdin**. A
276
- preset gives it to its CLI in the way that CLI accepts it: on stdin for `claude`, `codex`
277
- and `pi`, as an argument for `antigravity`, `copilot`, `opencode` and `omp`.
278
+ preset gives it to its CLI in the way that CLI accepts it: on stdin for `claude`, `codex`,
279
+ `pi` and `mastracode`, as an argument for `antigravity`, `copilot`, `opencode` and `omp`.
278
280
 
279
281
  The agent reads all other data through the MCP server at `$ITSAPLAN_URL/mcp`. The agent
280
282
  sends `$ITSAPLAN_API_KEY` as a bearer token, and acts as its own user with its role.
@@ -323,8 +325,8 @@ error.
323
325
 
324
326
  - `text` — all of stdout is the answer.
325
327
  - `claude-stream-json`, `codex-jsonl`, `opencode-json`, `antigravity-stream-json`,
326
- `copilot-json`, `pi-json` — the event stream of that CLI. `pi` and `omp` both use
327
- `pi-json`. The runner reads the text and the tool calls from it.
328
+ `copilot-json`, `pi-json`, `mastracode-jsonl` — the event stream of that CLI. `pi` and
329
+ `omp` both use `pi-json`. The runner reads the text and the tool calls from it.
328
330
 
329
331
  A preset sets `outputFormat` for you. Set it yourself only with your own `command`. If the
330
332
  output does not agree with the format, the runner uses the output as plain text.
package/dist/cli.js CHANGED
@@ -177,6 +177,19 @@ var PRESETS = {
177
177
  ...sessionId ? ["--resume", sessionId] : []
178
178
  ],
179
179
  tail: ["--"]
180
+ },
181
+ mastracode: {
182
+ bin: "mastracode",
183
+ outputFormat: "mastracode-jsonl",
184
+ promptVia: "stdin",
185
+ head: (sessionId) => [
186
+ "--output",
187
+ "jsonl",
188
+ "--permission-mode",
189
+ "auto",
190
+ ...sessionId ? ["--thread", sessionId] : []
191
+ ],
192
+ tail: ["--prompt", "-"]
180
193
  }
181
194
  };
182
195
  var PRESET_NAMES = Object.keys(PRESETS);
@@ -208,7 +221,8 @@ var OUTPUT_FORMATS = [
208
221
  "opencode-json",
209
222
  "antigravity-stream-json",
210
223
  "copilot-json",
211
- "pi-json"
224
+ "pi-json",
225
+ "mastracode-jsonl"
212
226
  ];
213
227
  function presetOf(config) {
214
228
  if (config.command || !config.agent)
@@ -529,6 +543,7 @@ function startListener(port, agents, log) {
529
543
  var FLUSH_CHARS = 1500;
530
544
  var DELTA_LIMIT = 12000;
531
545
  var TOOL_TEXT_LIMIT = 32000;
546
+ var TERMINAL_CODES = /\x1b\[[0-?]*[ -/]*[@-~]/g;
532
547
 
533
548
  class AnswerStream {
534
549
  format;
@@ -544,6 +559,7 @@ class AnswerStream {
544
559
  openTextPart = "";
545
560
  openToolCalls = new Set;
546
561
  closedToolCalls = new Set;
562
+ mastraMessageId;
547
563
  sawPartialText = false;
548
564
  sawAnyText = false;
549
565
  segment = "";
@@ -654,7 +670,9 @@ class AnswerStream {
654
670
  try {
655
671
  parsed = JSON.parse(trimmed);
656
672
  } catch {
657
- this.appendText(`${trimmed}
673
+ const plain = trimmed.replace(TERMINAL_CODES, "").trim();
674
+ if (plain)
675
+ this.appendText(`${plain}
658
676
  `);
659
677
  return;
660
678
  }
@@ -680,6 +698,39 @@ class AnswerStream {
680
698
  case "pi-json":
681
699
  this.readPiLine(parsed);
682
700
  break;
701
+ case "mastracode-jsonl":
702
+ this.readMastraLine(parsed);
703
+ break;
704
+ }
705
+ }
706
+ readMastraLine(message) {
707
+ switch (message.type) {
708
+ case "thread_created":
709
+ if (message.thread?.id)
710
+ this.sessionId ??= message.thread.id;
711
+ return;
712
+ case "message_start":
713
+ if (message.message?.role === "assistant")
714
+ this.mastraMessageId = message.message.id;
715
+ return;
716
+ case "message_update":
717
+ if (message.id === this.mastraMessageId && message.event?.type === "text-delta") {
718
+ this.appendText(message.event.delta ?? "");
719
+ }
720
+ return;
721
+ case "tool_start":
722
+ if (message.toolCallId) {
723
+ this.pushToolCall(message.toolCallId, message.toolName ?? "tool", JSON.stringify(message.args ?? {}));
724
+ }
725
+ return;
726
+ case "tool_end":
727
+ if (message.toolCallId)
728
+ this.pushToolResult(message.toolCallId, toolResultText(message.result));
729
+ return;
730
+ case "result":
731
+ if (!this.sawAnyText)
732
+ this.appendText(message.text || message.error?.message || "");
733
+ return;
683
734
  }
684
735
  }
685
736
  readPiLine(message) {
@@ -704,7 +755,7 @@ class AnswerStream {
704
755
  if (!message.toolCallId || this.closedToolCalls.has(message.toolCallId))
705
756
  return;
706
757
  this.closedToolCalls.add(message.toolCallId);
707
- this.pushToolResult(message.toolCallId, piResultText(message.result));
758
+ this.pushToolResult(message.toolCallId, toolResultText(message.result));
708
759
  return;
709
760
  }
710
761
  case "message_end":
@@ -970,8 +1021,19 @@ class UsageReader {
970
1021
  case "pi-json":
971
1022
  this.readPi(parsed);
972
1023
  return;
1024
+ case "mastracode-jsonl":
1025
+ this.readMastra(parsed);
1026
+ return;
973
1027
  }
974
1028
  }
1029
+ readMastra(message) {
1030
+ if (message.type !== "usage_update" || !message.usage)
1031
+ return;
1032
+ this.last = {
1033
+ inputTokens: message.usage.promptTokens ?? 0,
1034
+ outputTokens: message.usage.completionTokens ?? 0
1035
+ };
1036
+ }
975
1037
  readClaude(message) {
976
1038
  const event = message.type === "stream_event" ? message.event : undefined;
977
1039
  if (!event)
@@ -1042,7 +1104,7 @@ function textOfResult(content) {
1042
1104
  }
1043
1105
  return "";
1044
1106
  }
1045
- function piResultText(result) {
1107
+ function toolResultText(result) {
1046
1108
  if (result && typeof result === "object" && !Array.isArray(result) && "content" in result) {
1047
1109
  return textOfResult(result.content);
1048
1110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itsaplan/runner",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Runs an Itsaplan external agent's queued tasks and answers its chat on your own machine",
5
5
  "keywords": [
6
6
  "itsaplan",
@@ -20,7 +20,8 @@
20
20
  "antigravity",
21
21
  "opencode",
22
22
  "pi",
23
- "omp"
23
+ "omp",
24
+ "mastracode"
24
25
  ],
25
26
  "homepage": "https://github.com/croffasia/itsaplan/tree/main/packages/runner#readme",
26
27
  "repository": {