@itsaplan/runner 0.7.0 → 0.8.1

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 +74 -13
  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;
@@ -537,6 +552,7 @@ class AnswerStream {
537
552
  send;
538
553
  messageId;
539
554
  queued = [];
555
+ unsent = [];
540
556
  text = "";
541
557
  started = false;
542
558
  line = "";
@@ -544,6 +560,7 @@ class AnswerStream {
544
560
  openTextPart = "";
545
561
  openToolCalls = new Set;
546
562
  closedToolCalls = new Set;
563
+ mastraMessageId;
547
564
  sawPartialText = false;
548
565
  sawAnyText = false;
549
566
  segment = "";
@@ -587,15 +604,13 @@ class AnswerStream {
587
604
  }
588
605
  async sendQueued() {
589
606
  this.drainText();
590
- if (this.queued.length === 0)
591
- return;
592
- const batch = this.queued;
593
- this.queued = [];
594
- try {
595
- await this.send(batch);
596
- } catch (err) {
597
- this.queued.unshift(...batch);
598
- throw err;
607
+ while (this.unsent.length > 0 || this.queued.length > 0) {
608
+ if (this.unsent.length === 0) {
609
+ this.unsent = this.queued;
610
+ this.queued = [];
611
+ }
612
+ await this.send(this.unsent);
613
+ this.unsent = [];
599
614
  }
600
615
  }
601
616
  async finish(fallback) {
@@ -654,7 +669,9 @@ class AnswerStream {
654
669
  try {
655
670
  parsed = JSON.parse(trimmed);
656
671
  } catch {
657
- this.appendText(`${trimmed}
672
+ const plain = trimmed.replace(TERMINAL_CODES, "").trim();
673
+ if (plain)
674
+ this.appendText(`${plain}
658
675
  `);
659
676
  return;
660
677
  }
@@ -680,6 +697,39 @@ class AnswerStream {
680
697
  case "pi-json":
681
698
  this.readPiLine(parsed);
682
699
  break;
700
+ case "mastracode-jsonl":
701
+ this.readMastraLine(parsed);
702
+ break;
703
+ }
704
+ }
705
+ readMastraLine(message) {
706
+ switch (message.type) {
707
+ case "thread_created":
708
+ if (message.thread?.id)
709
+ this.sessionId ??= message.thread.id;
710
+ return;
711
+ case "message_start":
712
+ if (message.message?.role === "assistant")
713
+ this.mastraMessageId = message.message.id;
714
+ return;
715
+ case "message_update":
716
+ if (message.id === this.mastraMessageId && message.event?.type === "text-delta") {
717
+ this.appendText(message.event.delta ?? "");
718
+ }
719
+ return;
720
+ case "tool_start":
721
+ if (message.toolCallId) {
722
+ this.pushToolCall(message.toolCallId, message.toolName ?? "tool", JSON.stringify(message.args ?? {}));
723
+ }
724
+ return;
725
+ case "tool_end":
726
+ if (message.toolCallId)
727
+ this.pushToolResult(message.toolCallId, toolResultText(message.result));
728
+ return;
729
+ case "result":
730
+ if (!this.sawAnyText)
731
+ this.appendText(message.text || message.error?.message || "");
732
+ return;
683
733
  }
684
734
  }
685
735
  readPiLine(message) {
@@ -704,7 +754,7 @@ class AnswerStream {
704
754
  if (!message.toolCallId || this.closedToolCalls.has(message.toolCallId))
705
755
  return;
706
756
  this.closedToolCalls.add(message.toolCallId);
707
- this.pushToolResult(message.toolCallId, piResultText(message.result));
757
+ this.pushToolResult(message.toolCallId, toolResultText(message.result));
708
758
  return;
709
759
  }
710
760
  case "message_end":
@@ -970,8 +1020,19 @@ class UsageReader {
970
1020
  case "pi-json":
971
1021
  this.readPi(parsed);
972
1022
  return;
1023
+ case "mastracode-jsonl":
1024
+ this.readMastra(parsed);
1025
+ return;
973
1026
  }
974
1027
  }
1028
+ readMastra(message) {
1029
+ if (message.type !== "usage_update" || !message.usage)
1030
+ return;
1031
+ this.last = {
1032
+ inputTokens: message.usage.promptTokens ?? 0,
1033
+ outputTokens: message.usage.completionTokens ?? 0
1034
+ };
1035
+ }
975
1036
  readClaude(message) {
976
1037
  const event = message.type === "stream_event" ? message.event : undefined;
977
1038
  if (!event)
@@ -1042,7 +1103,7 @@ function textOfResult(content) {
1042
1103
  }
1043
1104
  return "";
1044
1105
  }
1045
- function piResultText(result) {
1106
+ function toolResultText(result) {
1046
1107
  if (result && typeof result === "object" && !Array.isArray(result) && "content" in result) {
1047
1108
  return textOfResult(result.content);
1048
1109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itsaplan/runner",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
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": {