@runtypelabs/cli 1.6.0 → 1.7.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.
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ var init_credential_store = __esm({
128
128
  import { Command as Command20 } from "commander";
129
129
  import chalk25 from "chalk";
130
130
  import { config as loadEnv } from "dotenv";
131
- import { readFileSync as readFileSync9 } from "fs";
131
+ import { readFileSync as readFileSync10 } from "fs";
132
132
  import { dirname as dirname4, join as join6 } from "path";
133
133
  import { fileURLToPath } from "url";
134
134
 
@@ -5262,11 +5262,11 @@ import { render as render10 } from "ink";
5262
5262
  import React12 from "react";
5263
5263
 
5264
5264
  // src/ink/marathon/MarathonApp.tsx
5265
- import { useState as useState20, useEffect as useEffect17, useRef as useRef8, useCallback as useCallback7, useMemo as useMemo6 } from "react";
5265
+ import { useState as useState20, useEffect as useEffect17, useRef as useRef8, useCallback as useCallback7, useMemo as useMemo7 } from "react";
5266
5266
  import { execSync } from "child_process";
5267
5267
  import open4 from "open";
5268
- import { Box as Box24, useApp as useApp5, useInput as useInput9, useStdout as useStdout3 } from "ink";
5269
- import { StreamOutput as StreamOutput3, StatusBar as StatusBar2, ErrorDisplay as ErrorDisplay4, theme as theme27 } from "@runtypelabs/ink-components";
5268
+ import { Box as Box25, useApp as useApp5, useInput as useInput9, useStdout as useStdout3 } from "ink";
5269
+ import { StreamOutput as StreamOutput3, StatusBar as StatusBar2, ErrorDisplay as ErrorDisplay4, theme as theme28 } from "@runtypelabs/ink-components";
5270
5270
  import { LoadingAnimation } from "@runtypelabs/terminal-animations";
5271
5271
 
5272
5272
  // src/ink/marathon/useMarathonStream.ts
@@ -5298,7 +5298,9 @@ var INITIAL_STATE2 = {
5298
5298
  totalCost: 0,
5299
5299
  currentIteration: 0,
5300
5300
  error: null,
5301
- rawEvents: []
5301
+ rawEvents: [],
5302
+ totalInputTokens: 0,
5303
+ totalOutputTokens: 0
5302
5304
  };
5303
5305
  var MAX_RAW_EVENTS = 500;
5304
5306
  function appendContentLine(content, line) {
@@ -5414,6 +5416,8 @@ function useMarathonStream() {
5414
5416
  setState((prev) => ({
5415
5417
  ...prev,
5416
5418
  totalCost: event.cost != null ? prev.totalCost + event.cost : prev.totalCost,
5419
+ totalInputTokens: prev.totalInputTokens + (event.tokens?.input ?? 0),
5420
+ totalOutputTokens: prev.totalOutputTokens + (event.tokens?.output ?? 0),
5417
5421
  sessionCount: prev.sessionCount + 1,
5418
5422
  rawEvents: pushRawEvent(prev, "agent_iteration_complete", { ...event })
5419
5423
  }));
@@ -5435,6 +5439,8 @@ function useMarathonStream() {
5435
5439
  phase: "complete",
5436
5440
  content: prev.content || event.finalOutput || "",
5437
5441
  totalCost: event.totalCost != null ? event.totalCost : prev.totalCost,
5442
+ totalInputTokens: event.totalTokens?.input ?? prev.totalInputTokens,
5443
+ totalOutputTokens: event.totalTokens?.output ?? prev.totalOutputTokens,
5438
5444
  rawEvents: pushRawEvent(prev, "agent_complete", { ...event })
5439
5445
  }));
5440
5446
  },
@@ -6090,16 +6096,258 @@ function EventStreamPanel({
6090
6096
  );
6091
6097
  }
6092
6098
 
6099
+ // src/ink/marathon/FilesPanel.tsx
6100
+ import { useMemo as useMemo6 } from "react";
6101
+ import { Box as Box21, Text as Text21 } from "ink";
6102
+ import { theme as theme24 } from "@runtypelabs/ink-components";
6103
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
6104
+ var TYPE_COLORS = {
6105
+ plan: theme24.accent,
6106
+ written: theme24.success,
6107
+ read: theme24.textMuted
6108
+ };
6109
+ var TYPE_LABELS = {
6110
+ plan: "plan",
6111
+ written: "write",
6112
+ read: "read"
6113
+ };
6114
+ function shortenPath(filePath, maxWidth) {
6115
+ if (filePath.length <= maxWidth) return filePath;
6116
+ const segments = filePath.split("/");
6117
+ for (let skip = 1; skip < segments.length - 1; skip++) {
6118
+ const shortened = ".../" + segments.slice(skip).join("/");
6119
+ if (shortened.length <= maxWidth) return shortened;
6120
+ }
6121
+ return ".../" + segments[segments.length - 1];
6122
+ }
6123
+ function FileContentView({
6124
+ file,
6125
+ content,
6126
+ totalLines,
6127
+ truncated,
6128
+ maxVisibleLines,
6129
+ scrollOffset = 0
6130
+ }) {
6131
+ const separator = theme24.separator ?? " \xB7 ";
6132
+ const { lines, displayTotalLines } = useMemo6(() => {
6133
+ const allLines = content.split("\n");
6134
+ const total = allLines.length;
6135
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, total - maxVisibleLines));
6136
+ const end = total - clampedOffset;
6137
+ const start = Math.max(0, end - maxVisibleLines);
6138
+ return { lines: allLines.slice(start, end), displayTotalLines: total };
6139
+ }, [content, maxVisibleLines, scrollOffset]);
6140
+ const filename = file.path.split("/").pop() || file.path;
6141
+ return /* @__PURE__ */ jsxs19(
6142
+ Box21,
6143
+ {
6144
+ flexDirection: "column",
6145
+ paddingX: theme24.panelPaddingX,
6146
+ paddingY: theme24.panelPaddingY,
6147
+ children: [
6148
+ /* @__PURE__ */ jsxs19(Box21, { marginBottom: theme24.sectionGap, children: [
6149
+ /* @__PURE__ */ jsx23(Text21, { bold: true, color: theme24.accent, children: filename }),
6150
+ /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6151
+ separator,
6152
+ totalLines,
6153
+ " lines"
6154
+ ] }),
6155
+ truncated && /* @__PURE__ */ jsxs19(Text21, { color: theme24.warning, children: [
6156
+ separator,
6157
+ "truncated"
6158
+ ] }),
6159
+ /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6160
+ separator,
6161
+ "Esc: back"
6162
+ ] }),
6163
+ displayTotalLines > maxVisibleLines && /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6164
+ separator,
6165
+ Math.max(0, displayTotalLines - maxVisibleLines - scrollOffset),
6166
+ " below"
6167
+ ] })
6168
+ ] }),
6169
+ lines.map((line, i) => /* @__PURE__ */ jsx23(Text21, { color: theme24.text, children: line }, i))
6170
+ ]
6171
+ }
6172
+ );
6173
+ }
6174
+ function FilesPanel({
6175
+ files,
6176
+ maxVisibleLines = 50,
6177
+ selectedIndex,
6178
+ detailFile,
6179
+ detailContent,
6180
+ detailTotalLines = 0,
6181
+ detailTruncated = false,
6182
+ detailScrollOffset = 0
6183
+ }) {
6184
+ const separator = theme24.separator ?? " \xB7 ";
6185
+ if (detailFile && detailContent !== null) {
6186
+ return /* @__PURE__ */ jsx23(
6187
+ FileContentView,
6188
+ {
6189
+ file: detailFile,
6190
+ content: detailContent,
6191
+ totalLines: detailTotalLines,
6192
+ truncated: detailTruncated,
6193
+ maxVisibleLines: maxVisibleLines - 2,
6194
+ scrollOffset: detailScrollOffset
6195
+ }
6196
+ );
6197
+ }
6198
+ const { rows, hiddenAbove, hiddenBelow } = useMemo6(() => {
6199
+ if (files.length === 0) return { rows: [], hiddenAbove: 0, hiddenBelow: 0 };
6200
+ const total = files.length;
6201
+ const half = Math.floor(maxVisibleLines / 2);
6202
+ let start = selectedIndex - half;
6203
+ let end = start + maxVisibleLines;
6204
+ if (start < 0) {
6205
+ start = 0;
6206
+ end = Math.min(total, maxVisibleLines);
6207
+ }
6208
+ if (end > total) {
6209
+ end = total;
6210
+ start = Math.max(0, end - maxVisibleLines);
6211
+ }
6212
+ const visible = files.slice(start, end);
6213
+ return {
6214
+ rows: visible.map((file, i) => ({
6215
+ globalIndex: start + i,
6216
+ key: `${start + i}`,
6217
+ file
6218
+ })),
6219
+ hiddenAbove: start,
6220
+ hiddenBelow: total - end
6221
+ };
6222
+ }, [files, maxVisibleLines, selectedIndex]);
6223
+ if (files.length === 0) {
6224
+ return /* @__PURE__ */ jsx23(
6225
+ Box21,
6226
+ {
6227
+ flexDirection: "column",
6228
+ paddingX: theme24.panelPaddingX,
6229
+ paddingY: theme24.panelPaddingY,
6230
+ children: /* @__PURE__ */ jsx23(Text21, { color: theme24.textSubtle, children: "No files tracked yet..." })
6231
+ }
6232
+ );
6233
+ }
6234
+ return /* @__PURE__ */ jsxs19(
6235
+ Box21,
6236
+ {
6237
+ flexDirection: "column",
6238
+ paddingX: theme24.panelPaddingX,
6239
+ paddingY: theme24.panelPaddingY,
6240
+ children: [
6241
+ /* @__PURE__ */ jsxs19(Box21, { marginBottom: 0, children: [
6242
+ /* @__PURE__ */ jsx23(Text21, { bold: true, color: theme24.accent, children: "Files" }),
6243
+ /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6244
+ separator,
6245
+ files.length,
6246
+ " files"
6247
+ ] }),
6248
+ hiddenAbove > 0 && /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6249
+ separator,
6250
+ hiddenAbove,
6251
+ " above"
6252
+ ] }),
6253
+ hiddenBelow > 0 && /* @__PURE__ */ jsxs19(Text21, { color: theme24.textSubtle, children: [
6254
+ separator,
6255
+ hiddenBelow,
6256
+ " below"
6257
+ ] })
6258
+ ] }),
6259
+ rows.map((row) => {
6260
+ const isSelected = row.globalIndex === selectedIndex;
6261
+ const typeColor = TYPE_COLORS[row.file.type] || theme24.text;
6262
+ const typeLabel = TYPE_LABELS[row.file.type] || row.file.type;
6263
+ return /* @__PURE__ */ jsxs19(Box21, { flexDirection: "row", flexWrap: "nowrap", children: [
6264
+ /* @__PURE__ */ jsx23(Box21, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx23(Text21, { color: isSelected ? theme24.accentActive : theme24.textSubtle, children: isSelected ? "\u203A" : " " }) }),
6265
+ /* @__PURE__ */ jsx23(Box21, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsxs19(Text21, { color: typeColor, children: [
6266
+ "[",
6267
+ typeLabel,
6268
+ "]"
6269
+ ] }) }),
6270
+ /* @__PURE__ */ jsx23(Box21, { flexShrink: 1, children: /* @__PURE__ */ jsx23(
6271
+ Text21,
6272
+ {
6273
+ color: isSelected ? theme24.accentActive : theme24.text,
6274
+ bold: isSelected,
6275
+ wrap: "truncate",
6276
+ children: shortenPath(row.file.path, 80)
6277
+ }
6278
+ ) })
6279
+ ] }, row.key);
6280
+ })
6281
+ ]
6282
+ }
6283
+ );
6284
+ }
6285
+
6286
+ // src/ink/marathon/file-utils.ts
6287
+ import * as fs4 from "fs";
6288
+ function extractTrackedFiles(snapshots, liveTools, planPath) {
6289
+ const fileMap = /* @__PURE__ */ new Map();
6290
+ function processTools(tools) {
6291
+ for (const tool of tools) {
6292
+ if (tool.name !== "write_file" && tool.name !== "read_file") continue;
6293
+ const filePath = tool.parameters?.path;
6294
+ if (!filePath) continue;
6295
+ const isWrite = tool.name === "write_file";
6296
+ const existing = fileMap.get(filePath);
6297
+ if (!existing || isWrite && existing.type === "read") {
6298
+ fileMap.set(filePath, {
6299
+ path: filePath,
6300
+ type: isWrite ? "written" : "read",
6301
+ lastTouchedAt: tool.startedAt
6302
+ });
6303
+ } else if (existing && tool.startedAt > (existing.lastTouchedAt ?? 0)) {
6304
+ existing.lastTouchedAt = tool.startedAt;
6305
+ }
6306
+ }
6307
+ }
6308
+ for (const snapshot of snapshots) {
6309
+ processTools(snapshot.tools);
6310
+ }
6311
+ processTools(liveTools);
6312
+ const result = [];
6313
+ if (planPath) {
6314
+ fileMap.delete(planPath);
6315
+ result.push({ path: planPath, type: "plan" });
6316
+ }
6317
+ const sorted = Array.from(fileMap.values()).sort(
6318
+ (a, b) => (b.lastTouchedAt ?? 0) - (a.lastTouchedAt ?? 0)
6319
+ );
6320
+ result.push(...sorted);
6321
+ return result;
6322
+ }
6323
+ function readFileContent(filePath, maxLines = 5e3) {
6324
+ try {
6325
+ const raw = fs4.readFileSync(filePath, "utf-8");
6326
+ const lines = raw.split("\n");
6327
+ const totalLines = lines.length;
6328
+ if (totalLines > maxLines) {
6329
+ return {
6330
+ content: lines.slice(0, maxLines).join("\n"),
6331
+ totalLines,
6332
+ truncated: true
6333
+ };
6334
+ }
6335
+ return { content: raw, totalLines, truncated: false };
6336
+ } catch {
6337
+ return { content: `Error: Could not read file at ${filePath}`, totalLines: 1, truncated: false };
6338
+ }
6339
+ }
6340
+
6093
6341
  // src/ink/marathon/SteeringPrompt.tsx
6094
6342
  import { useState as useState19, useEffect as useEffect16, useRef as useRef7 } from "react";
6095
- import { Box as Box22, Text as Text22 } from "ink";
6343
+ import { Box as Box23, Text as Text23 } from "ink";
6096
6344
  import TextInput4 from "ink-text-input";
6097
- import { theme as theme25 } from "@runtypelabs/ink-components";
6345
+ import { theme as theme26 } from "@runtypelabs/ink-components";
6098
6346
 
6099
6347
  // src/ink/marathon/SteeringRecap.tsx
6100
- import { Box as Box21, Text as Text21 } from "ink";
6101
- import { theme as theme24 } from "@runtypelabs/ink-components";
6102
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
6348
+ import { Box as Box22, Text as Text22 } from "ink";
6349
+ import { theme as theme25 } from "@runtypelabs/ink-components";
6350
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
6103
6351
  function SteeringRecap({
6104
6352
  sessionNumber,
6105
6353
  toolCallsMade,
@@ -6109,19 +6357,19 @@ function SteeringRecap({
6109
6357
  isTerminal
6110
6358
  }) {
6111
6359
  const label = isTerminal ? `\u2713 Session ${sessionNumber} complete` : `Session ${sessionNumber} complete`;
6112
- const separator = theme24.separator ?? " \xB7 ";
6113
- return /* @__PURE__ */ jsxs19(
6114
- Box21,
6360
+ const separator = theme25.separator ?? " \xB7 ";
6361
+ return /* @__PURE__ */ jsxs20(
6362
+ Box22,
6115
6363
  {
6116
6364
  borderStyle: "single",
6117
- borderColor: theme24.border,
6118
- backgroundColor: theme24.background,
6365
+ borderColor: theme25.border,
6366
+ backgroundColor: theme25.background,
6119
6367
  flexDirection: "column",
6120
- paddingX: theme24.panelPaddingX,
6121
- paddingY: theme24.panelPaddingY,
6368
+ paddingX: theme25.panelPaddingX,
6369
+ paddingY: theme25.panelPaddingY,
6122
6370
  children: [
6123
- /* @__PURE__ */ jsx23(Text21, { color: isTerminal ? theme24.accentActive : theme24.accent, children: label }),
6124
- /* @__PURE__ */ jsx23(Text21, { color: theme24.textMuted, children: `Tools: ${toolCallsMade}${separator}Tokens: ${tokensInput}+${tokensOutput}${separator}Cost: $${cost.toFixed(4)}` })
6371
+ /* @__PURE__ */ jsx24(Text22, { color: isTerminal ? theme25.accentActive : theme25.accent, children: label }),
6372
+ /* @__PURE__ */ jsx24(Text22, { color: theme25.textMuted, children: `Tools: ${toolCallsMade}${separator}Tokens: ${tokensInput}+${tokensOutput}${separator}Cost: $${cost.toFixed(4)}` })
6125
6373
  ]
6126
6374
  }
6127
6375
  );
@@ -6148,7 +6396,7 @@ var MARATHON_STEER_COMMANDS = [
6148
6396
  ];
6149
6397
 
6150
6398
  // src/ink/marathon/SteeringPrompt.tsx
6151
- import { Fragment as Fragment2, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
6399
+ import { Fragment as Fragment2, jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
6152
6400
  function SteeringPrompt({
6153
6401
  onSubmit,
6154
6402
  timeout,
@@ -6163,7 +6411,7 @@ function SteeringPrompt({
6163
6411
  const [showModelPicker, setShowModelPicker] = useState19(false);
6164
6412
  const [showHelp, setShowHelp] = useState19(false);
6165
6413
  const timerRef = useRef7(null);
6166
- const separator = theme25.separator ?? " \xB7 ";
6414
+ const separator = theme26.separator ?? " \xB7 ";
6167
6415
  useEffect16(() => {
6168
6416
  const title = "Marathon";
6169
6417
  const body = isTerminal ? "Marathon complete" : `Session ${recap?.sessionNumber ?? "?"} complete`;
@@ -6251,19 +6499,19 @@ function SteeringPrompt({
6251
6499
  if (isTyping) return "(paused)";
6252
6500
  return `(${remaining}s)`;
6253
6501
  })();
6254
- return /* @__PURE__ */ jsxs20(Box22, { flexDirection: "column", flexShrink: 0, children: [
6255
- /* @__PURE__ */ jsx24(SteeringRecap, { ...recap, isTerminal }),
6256
- showModelPicker ? /* @__PURE__ */ jsx24(
6502
+ return /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", flexShrink: 0, children: [
6503
+ /* @__PURE__ */ jsx25(SteeringRecap, { ...recap, isTerminal }),
6504
+ showModelPicker ? /* @__PURE__ */ jsx25(
6257
6505
  ModelPicker,
6258
6506
  {
6259
6507
  currentModel,
6260
6508
  onSelect: handleModelSelect,
6261
6509
  onCancel: handleModelCancel
6262
6510
  }
6263
- ) : /* @__PURE__ */ jsxs20(Fragment2, { children: [
6264
- /* @__PURE__ */ jsxs20(Box22, { children: [
6265
- /* @__PURE__ */ jsx24(Text22, { color: theme25.accentActive, children: "> " }),
6266
- /* @__PURE__ */ jsx24(Box22, { flexGrow: 1, children: /* @__PURE__ */ jsx24(
6511
+ ) : /* @__PURE__ */ jsxs21(Fragment2, { children: [
6512
+ /* @__PURE__ */ jsxs21(Box23, { children: [
6513
+ /* @__PURE__ */ jsx25(Text23, { color: theme26.accentActive, children: "> " }),
6514
+ /* @__PURE__ */ jsx25(Box23, { flexGrow: 1, children: /* @__PURE__ */ jsx25(
6267
6515
  TextInput4,
6268
6516
  {
6269
6517
  value,
@@ -6272,12 +6520,12 @@ function SteeringPrompt({
6272
6520
  placeholder: isTerminal ? "Send new instructions to continue marathon, or press enter to exit..." : "Steer next iteration..."
6273
6521
  }
6274
6522
  ) }),
6275
- countdownText && /* @__PURE__ */ jsxs20(Text22, { color: theme25.textSubtle, children: [
6523
+ countdownText && /* @__PURE__ */ jsxs21(Text23, { color: theme26.textSubtle, children: [
6276
6524
  " ",
6277
6525
  countdownText
6278
6526
  ] })
6279
6527
  ] }),
6280
- /* @__PURE__ */ jsx24(Text22, { color: theme25.textSubtle, children: [
6528
+ /* @__PURE__ */ jsx25(Text23, { color: theme26.textSubtle, children: [
6281
6529
  isTerminal ? isTyping ? "Enter: send" : "Enter: exit" : "Enter: continue",
6282
6530
  "/model",
6283
6531
  "/tools",
@@ -6285,7 +6533,7 @@ function SteeringPrompt({
6285
6533
  "/stop",
6286
6534
  "/help"
6287
6535
  ].join(separator) }),
6288
- showHelp && /* @__PURE__ */ jsx24(Box22, { flexDirection: "column", marginTop: theme25.sectionGap, children: MARATHON_STEER_COMMANDS.map((cmd) => /* @__PURE__ */ jsxs20(Text22, { color: theme25.textSubtle, children: [
6536
+ showHelp && /* @__PURE__ */ jsx25(Box23, { flexDirection: "column", marginTop: theme26.sectionGap, children: MARATHON_STEER_COMMANDS.map((cmd) => /* @__PURE__ */ jsxs21(Text23, { color: theme26.textSubtle, children: [
6289
6537
  cmd.name.padEnd(12),
6290
6538
  " ",
6291
6539
  cmd.description
@@ -6295,44 +6543,44 @@ function SteeringPrompt({
6295
6543
  }
6296
6544
 
6297
6545
  // src/ink/marathon/UpgradeModal.tsx
6298
- import { Box as Box23, Text as Text23 } from "ink";
6299
- import { theme as theme26 } from "@runtypelabs/ink-components";
6300
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
6546
+ import { Box as Box24, Text as Text24 } from "ink";
6547
+ import { theme as theme27 } from "@runtypelabs/ink-components";
6548
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
6301
6549
  function UpgradeModal({ prompt, width }) {
6302
- return /* @__PURE__ */ jsxs21(
6303
- Box23,
6550
+ return /* @__PURE__ */ jsxs22(
6551
+ Box24,
6304
6552
  {
6305
6553
  width,
6306
6554
  flexDirection: "column",
6307
6555
  borderStyle: "round",
6308
- borderColor: theme26.warning,
6309
- backgroundColor: theme26.surfaceElevated,
6556
+ borderColor: theme27.warning,
6557
+ backgroundColor: theme27.surfaceElevated,
6310
6558
  paddingX: 2,
6311
6559
  paddingY: 1,
6312
6560
  children: [
6313
- /* @__PURE__ */ jsx25(Text23, { color: theme26.warning, bold: true, children: "Upgrade available" }),
6314
- /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx25(Text23, { color: theme26.label, children: prompt.message }) }),
6315
- (prompt.code || prompt.limitType || prompt.retryAfter) && /* @__PURE__ */ jsxs21(Box23, { flexDirection: "column", marginTop: 1, children: [
6316
- prompt.code && /* @__PURE__ */ jsxs21(Text23, { color: theme26.muted, children: [
6561
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.warning, bold: true, children: "Upgrade available" }),
6562
+ /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text24, { color: theme27.label, children: prompt.message }) }),
6563
+ (prompt.code || prompt.limitType || prompt.retryAfter) && /* @__PURE__ */ jsxs22(Box24, { flexDirection: "column", marginTop: 1, children: [
6564
+ prompt.code && /* @__PURE__ */ jsxs22(Text24, { color: theme27.muted, children: [
6317
6565
  "Code: ",
6318
- /* @__PURE__ */ jsx25(Text23, { color: theme26.label, children: prompt.code })
6566
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.label, children: prompt.code })
6319
6567
  ] }),
6320
- prompt.limitType && /* @__PURE__ */ jsxs21(Text23, { color: theme26.muted, children: [
6568
+ prompt.limitType && /* @__PURE__ */ jsxs22(Text24, { color: theme27.muted, children: [
6321
6569
  "Limit: ",
6322
- /* @__PURE__ */ jsx25(Text23, { color: theme26.label, children: prompt.limitType })
6570
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.label, children: prompt.limitType })
6323
6571
  ] }),
6324
- prompt.retryAfter && /* @__PURE__ */ jsxs21(Text23, { color: theme26.muted, children: [
6572
+ prompt.retryAfter && /* @__PURE__ */ jsxs22(Text24, { color: theme27.muted, children: [
6325
6573
  "Retry after: ",
6326
- /* @__PURE__ */ jsx25(Text23, { color: theme26.label, children: prompt.retryAfter })
6574
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.label, children: prompt.retryAfter })
6327
6575
  ] })
6328
6576
  ] }),
6329
- /* @__PURE__ */ jsxs21(Box23, { marginTop: 1, children: [
6330
- /* @__PURE__ */ jsx25(Text23, { color: theme26.info, children: "Enter" }),
6331
- /* @__PURE__ */ jsx25(Text23, { color: theme26.muted, children: " open billing " }),
6332
- /* @__PURE__ */ jsx25(Text23, { color: theme26.info, children: "Esc" }),
6333
- /* @__PURE__ */ jsx25(Text23, { color: theme26.muted, children: " close" })
6577
+ /* @__PURE__ */ jsxs22(Box24, { marginTop: 1, children: [
6578
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.info, children: "Enter" }),
6579
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.muted, children: " open billing " }),
6580
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.info, children: "Esc" }),
6581
+ /* @__PURE__ */ jsx26(Text24, { color: theme27.muted, children: " close" })
6334
6582
  ] }),
6335
- /* @__PURE__ */ jsx25(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx25(Text23, { color: theme26.dimLabel, children: "Opens your dashboard billing page" }) })
6583
+ /* @__PURE__ */ jsx26(Box24, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text24, { color: theme27.dimLabel, children: "Opens your dashboard billing page" }) })
6336
6584
  ]
6337
6585
  }
6338
6586
  );
@@ -6485,7 +6733,7 @@ function getSessionTabKeyForShortcut(visibleTabs, input) {
6485
6733
  }
6486
6734
 
6487
6735
  // src/ink/marathon/MarathonApp.tsx
6488
- import { Fragment as Fragment3, jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
6736
+ import { Fragment as Fragment3, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
6489
6737
  var TOOL_PANEL_WIDE = 48;
6490
6738
  var TOOL_PANEL_NARROW = 36;
6491
6739
  var NARROW_THRESHOLD = 100;
@@ -6571,6 +6819,7 @@ function MarathonApp({
6571
6819
  showFinish,
6572
6820
  initialRunnerPosition,
6573
6821
  initialRunnerLaps,
6822
+ planPath,
6574
6823
  debug: _debug,
6575
6824
  plainText,
6576
6825
  noSteer: _noSteer,
@@ -6584,7 +6833,7 @@ function MarathonApp({
6584
6833
  const { state, callbacks, reset: _reset, setSteering, resetForNewSession, showError } = useMarathonStream();
6585
6834
  const { exit } = useApp5();
6586
6835
  const { stdout } = useStdout3();
6587
- const separator = theme27.separator ?? " \xB7 ";
6836
+ const separator = theme28.separator ?? " \xB7 ";
6588
6837
  const steeringResolveRef = useRef8(null);
6589
6838
  const [steeringRecap, setSteeringRecap] = useState20(null);
6590
6839
  const [currentModel, setCurrentModel] = useState20(model || "default");
@@ -6685,6 +6934,13 @@ function MarathonApp({
6685
6934
  const [ctrlCPressed, setCtrlCPressed] = useState20(false);
6686
6935
  const ctrlCTimeout = useRef8(null);
6687
6936
  const [showEventStream, setShowEventStream] = useState20(false);
6937
+ const [showFilesPanel, setShowFilesPanel] = useState20(false);
6938
+ const [fileCursor, setFileCursor] = useState20(0);
6939
+ const [detailFile, setDetailFile] = useState20(null);
6940
+ const [detailFileContent, setDetailFileContent] = useState20(null);
6941
+ const [detailFileTotalLines, setDetailFileTotalLines] = useState20(0);
6942
+ const [detailFileTruncated, setDetailFileTruncated] = useState20(false);
6943
+ const [fileDetailScrollOffset, setFileDetailScrollOffset] = useState20(0);
6688
6944
  const [scrollOffset, setScrollOffset] = useState20(0);
6689
6945
  const [reasoningCollapsed, setReasoningCollapsed] = useState20(false);
6690
6946
  const [eventCursor, setEventCursor] = useState20(-1);
@@ -6702,21 +6958,21 @@ function MarathonApp({
6702
6958
  }
6703
6959
  const latestCompletedSessionIndex = sessionSnapshots[sessionSnapshots.length - 1]?.sessionIndex ?? initialSessionCount;
6704
6960
  const shouldShowLiveTab = state.phase !== "idle" && state.phase !== "steering" && state.phase !== "complete" && (Boolean(state.content) || Boolean(state.reasoning) || state.tools.length > 0 || state.rawEvents.length > 0 || state.phase === "thinking" || state.phase === "error");
6705
- const liveSessionSnapshot = useMemo6(
6961
+ const liveSessionSnapshot = useMemo7(
6706
6962
  () => shouldShowLiveTab ? buildLiveSessionSnapshot(state, latestCompletedSessionIndex + 1, currentModel) : void 0,
6707
6963
  [currentModel, latestCompletedSessionIndex, shouldShowLiveTab, state]
6708
6964
  );
6709
6965
  const liveSessionKey = liveSessionSnapshot ? createSessionTabKey(liveSessionSnapshot.sessionIndex, "live") : void 0;
6710
6966
  const latestSessionKey = getLatestSessionTabKey(sessionSnapshots, liveSessionSnapshot);
6711
- const allTabs = useMemo6(
6967
+ const allTabs = useMemo7(
6712
6968
  () => buildSessionTabs(sessionSnapshots, selectedSessionKey, latestUnreadKey, liveSessionSnapshot),
6713
6969
  [latestUnreadKey, liveSessionSnapshot, selectedSessionKey, sessionSnapshots]
6714
6970
  );
6715
- const visibleTabs = useMemo6(
6971
+ const visibleTabs = useMemo7(
6716
6972
  () => getVisibleSessionTabs(allTabs, selectedSessionKey, terminalWidth),
6717
6973
  [allTabs, selectedSessionKey, terminalWidth]
6718
6974
  );
6719
- const displayedSessionSnapshot = useMemo6(() => {
6975
+ const displayedSessionSnapshot = useMemo7(() => {
6720
6976
  if (!selectedSessionKey) return liveSessionSnapshot ?? sessionSnapshots[sessionSnapshots.length - 1];
6721
6977
  if (selectedSessionKey === liveSessionKey) return liveSessionSnapshot;
6722
6978
  return sessionSnapshots.find(
@@ -6728,9 +6984,13 @@ function MarathonApp({
6728
6984
  const displayedReasoning = displayedSessionSnapshot?.reasoning ?? state.reasoning;
6729
6985
  const displayedTools = displayedSessionSnapshot?.tools ?? state.tools;
6730
6986
  const displayedEvents = displayedSessionSnapshot?.rawEvents ?? state.rawEvents;
6987
+ const trackedFiles = useMemo7(
6988
+ () => extractTrackedFiles(sessionSnapshots, displayedTools, planPath),
6989
+ [sessionSnapshots, displayedTools, planPath]
6990
+ );
6731
6991
  const latestLiveActivityRef = useRef8(void 0);
6732
- const upgradePrompt = useMemo6(() => parseMarathonUpgradePrompt(state.error), [state.error]);
6733
- const upgradePromptKey = useMemo6(
6992
+ const upgradePrompt = useMemo7(() => parseMarathonUpgradePrompt(state.error), [state.error]);
6993
+ const upgradePromptKey = useMemo7(
6734
6994
  () => upgradePrompt ? `${upgradePrompt.message}|${upgradePrompt.code ?? ""}|${upgradePrompt.limitType ?? ""}|${upgradePrompt.retryAfter ?? ""}` : void 0,
6735
6995
  [upgradePrompt]
6736
6996
  );
@@ -6898,7 +7158,22 @@ function MarathonApp({
6898
7158
  selectSessionTab(shortcutTabKey);
6899
7159
  return;
6900
7160
  }
7161
+ if (_input === "f" && !detailEvent && !detailFile) {
7162
+ setShowFilesPanel((prev) => {
7163
+ const next = !prev;
7164
+ if (next) setShowEventStream(false);
7165
+ return next;
7166
+ });
7167
+ setScrollOffset(0);
7168
+ return;
7169
+ }
6901
7170
  if (key.tab) {
7171
+ if (showFilesPanel) {
7172
+ setShowFilesPanel(false);
7173
+ setDetailFile(null);
7174
+ setDetailFileContent(null);
7175
+ setFileDetailScrollOffset(0);
7176
+ }
6902
7177
  setShowEventStream((prev) => !prev);
6903
7178
  setScrollOffset(0);
6904
7179
  setDetailEvent(null);
@@ -6906,7 +7181,13 @@ function MarathonApp({
6906
7181
  return;
6907
7182
  }
6908
7183
  if (key.escape) {
6909
- if (detailEvent) {
7184
+ if (detailFile) {
7185
+ setDetailFile(null);
7186
+ setDetailFileContent(null);
7187
+ setFileDetailScrollOffset(0);
7188
+ } else if (showFilesPanel) {
7189
+ setShowFilesPanel(false);
7190
+ } else if (detailEvent) {
6910
7191
  setDetailEvent(null);
6911
7192
  setDetailScrollOffset(0);
6912
7193
  } else if (goalExpanded) {
@@ -6922,6 +7203,19 @@ function MarathonApp({
6922
7203
  setReasoningCollapsed((prev) => !prev);
6923
7204
  return;
6924
7205
  }
7206
+ if (_input === "c" && showFilesPanel) {
7207
+ if (detailFile && detailFileContent) {
7208
+ const ok = copyToClipboard(detailFileContent);
7209
+ showFlash(ok ? "Copied content!" : "Copy failed");
7210
+ } else {
7211
+ const file = trackedFiles[fileCursor];
7212
+ if (file) {
7213
+ const ok = copyToClipboard(file.path);
7214
+ showFlash(ok ? "Copied path!" : "Copy failed");
7215
+ }
7216
+ }
7217
+ return;
7218
+ }
6925
7219
  if (_input === "c" && showEventStream) {
6926
7220
  const evt = detailEvent ?? displayedEvents[eventCursor];
6927
7221
  if (evt) {
@@ -6931,6 +7225,24 @@ function MarathonApp({
6931
7225
  }
6932
7226
  return;
6933
7227
  }
7228
+ if (key.return && showFilesPanel && !detailFile) {
7229
+ const file = trackedFiles[fileCursor];
7230
+ if (file) {
7231
+ const { content, totalLines, truncated } = readFileContent(file.path);
7232
+ setDetailFile(file);
7233
+ setDetailFileContent(content);
7234
+ setDetailFileTotalLines(totalLines);
7235
+ setDetailFileTruncated(truncated);
7236
+ setFileDetailScrollOffset(0);
7237
+ }
7238
+ return;
7239
+ }
7240
+ if (key.return && detailFile) {
7241
+ setDetailFile(null);
7242
+ setDetailFileContent(null);
7243
+ setFileDetailScrollOffset(0);
7244
+ return;
7245
+ }
6934
7246
  if (key.return && showEventStream && !detailEvent) {
6935
7247
  const evt = displayedEvents[eventCursor];
6936
7248
  if (evt) {
@@ -6945,7 +7257,11 @@ function MarathonApp({
6945
7257
  return;
6946
7258
  }
6947
7259
  if (key.upArrow) {
6948
- if (showEventStream && detailEvent) {
7260
+ if (showFilesPanel && detailFile) {
7261
+ setFileDetailScrollOffset((prev) => prev + SCROLL_STEP2);
7262
+ } else if (showFilesPanel) {
7263
+ setFileCursor((prev) => Math.max(0, prev - 1));
7264
+ } else if (showEventStream && detailEvent) {
6949
7265
  setDetailScrollOffset((prev) => prev + SCROLL_STEP2);
6950
7266
  } else if (showEventStream) {
6951
7267
  setEventCursor((prev) => Math.max(0, prev - 1));
@@ -6955,7 +7271,11 @@ function MarathonApp({
6955
7271
  return;
6956
7272
  }
6957
7273
  if (key.downArrow) {
6958
- if (showEventStream && detailEvent) {
7274
+ if (showFilesPanel && detailFile) {
7275
+ setFileDetailScrollOffset((prev) => Math.max(0, prev - SCROLL_STEP2));
7276
+ } else if (showFilesPanel) {
7277
+ setFileCursor((prev) => Math.min(trackedFiles.length - 1, prev + 1));
7278
+ } else if (showEventStream && detailEvent) {
6959
7279
  setDetailScrollOffset((prev) => Math.max(0, prev - SCROLL_STEP2));
6960
7280
  } else if (showEventStream) {
6961
7281
  setEventCursor((prev) => Math.min(displayedEvents.length - 1, prev + 1));
@@ -6964,6 +7284,34 @@ function MarathonApp({
6964
7284
  }
6965
7285
  return;
6966
7286
  }
7287
+ if (key.leftArrow && showFilesPanel && detailFile) {
7288
+ const newIndex = Math.max(0, fileCursor - 1);
7289
+ const file = trackedFiles[newIndex];
7290
+ if (file) {
7291
+ setFileCursor(newIndex);
7292
+ const { content, totalLines, truncated } = readFileContent(file.path);
7293
+ setDetailFile(file);
7294
+ setDetailFileContent(content);
7295
+ setDetailFileTotalLines(totalLines);
7296
+ setDetailFileTruncated(truncated);
7297
+ setFileDetailScrollOffset(0);
7298
+ }
7299
+ return;
7300
+ }
7301
+ if (key.rightArrow && showFilesPanel && detailFile) {
7302
+ const newIndex = Math.min(trackedFiles.length - 1, fileCursor + 1);
7303
+ const file = trackedFiles[newIndex];
7304
+ if (file) {
7305
+ setFileCursor(newIndex);
7306
+ const { content, totalLines, truncated } = readFileContent(file.path);
7307
+ setDetailFile(file);
7308
+ setDetailFileContent(content);
7309
+ setDetailFileTotalLines(totalLines);
7310
+ setDetailFileTruncated(truncated);
7311
+ setFileDetailScrollOffset(0);
7312
+ }
7313
+ return;
7314
+ }
6967
7315
  if (key.leftArrow && showEventStream && detailEvent) {
6968
7316
  navigateToEvent(eventCursor - 1);
6969
7317
  return;
@@ -7002,6 +7350,22 @@ function MarathonApp({
7002
7350
  const scrollHint2 = scrollOffset > 0 ? `+${scrollOffset} scroll` : void 0;
7003
7351
  return joinHints("\u2191\u2193: scroll", "Ctrl+C", scrollHint2);
7004
7352
  }
7353
+ if (showFilesPanel && detailFile) {
7354
+ return joinHints(
7355
+ "c: copy content",
7356
+ "\u2190\u2192: prev/next",
7357
+ "Esc: back",
7358
+ "Ctrl+C"
7359
+ );
7360
+ }
7361
+ if (showFilesPanel) {
7362
+ return joinHints(
7363
+ "Enter: view",
7364
+ "c: copy path",
7365
+ "f/Esc: close",
7366
+ "Ctrl+C"
7367
+ );
7368
+ }
7005
7369
  if (showEventStream && detailEvent) {
7006
7370
  return joinHints(
7007
7371
  "Shift+\u2190/\u2192: tabs",
@@ -7028,12 +7392,14 @@ function MarathonApp({
7028
7392
  "Shift+\u2190/\u2192: tabs",
7029
7393
  "1-9: jump",
7030
7394
  "0: latest",
7395
+ "f: files",
7031
7396
  "Tab: events",
7032
7397
  "Ctrl+C",
7033
7398
  scrollHint,
7034
7399
  upgradePrompt ? "u: upgrade" : void 0
7035
7400
  );
7036
7401
  })();
7402
+ const filesCenter = detailFile ? `${detailFile.path.split("/").pop()}${separator}${fileCursor + 1}/${trackedFiles.length}` : `${trackedFiles.length} files`;
7037
7403
  const detailCenter = detailEvent ? `Event detail${separator}${eventCursor + 1}/${displayedEvents.length}` : "Event stream";
7038
7404
  const goalText = goal || "";
7039
7405
  const goalExpandedRows = goalExpanded && goalText.length > terminalWidth - 4 ? Math.ceil(goalText.length / (terminalWidth - 4)) - 1 : 0;
@@ -7046,7 +7412,7 @@ function MarathonApp({
7046
7412
  adjustedContentHeight - reasoningHintRows - reasoningLiveRows - thinkingRows - 3
7047
7413
  );
7048
7414
  const targetReasoningRows = hasReasoning && !reasoningCollapsed ? Math.min(maxReasoningRows, Math.max(3, Math.min(8, Math.floor(adjustedContentHeight * 0.3)))) : 0;
7049
- const visibleReasoningLines = useMemo6(
7415
+ const visibleReasoningLines = useMemo7(
7050
7416
  () => getVisibleReasoningLines(displayedReasoning, targetReasoningRows),
7051
7417
  [displayedReasoning, targetReasoningRows]
7052
7418
  );
@@ -7057,14 +7423,14 @@ function MarathonApp({
7057
7423
  );
7058
7424
  const upgradeModalWidth = Math.max(24, Math.min(terminalWidth - 6, 88));
7059
7425
  const showUpgradeBrowseHint = canBrowseAfterUpgradeError && selectedIsLive && !displayedContent.trim() && !displayedReasoning.trim() && displayedTools.length === 0 && displayedEvents.length === 0;
7060
- return /* @__PURE__ */ jsxs22(
7061
- Box24,
7426
+ return /* @__PURE__ */ jsxs23(
7427
+ Box25,
7062
7428
  {
7063
7429
  flexDirection: "column",
7064
7430
  height: terminalRows,
7065
7431
  width: terminalWidth,
7066
7432
  children: [
7067
- /* @__PURE__ */ jsx26(
7433
+ /* @__PURE__ */ jsx27(
7068
7434
  SessionHeader,
7069
7435
  {
7070
7436
  sessionName: taskName,
@@ -7085,7 +7451,7 @@ function MarathonApp({
7085
7451
  previewUrl
7086
7452
  }
7087
7453
  ),
7088
- hasTabs && /* @__PURE__ */ jsx26(Box24, { width: terminalWidth, marginBottom: 1, children: /* @__PURE__ */ jsx26(
7454
+ hasTabs && /* @__PURE__ */ jsx27(Box25, { width: terminalWidth, marginBottom: 1, children: /* @__PURE__ */ jsx27(
7089
7455
  SessionTabs,
7090
7456
  {
7091
7457
  tabs: visibleTabs.tabs,
@@ -7093,13 +7459,13 @@ function MarathonApp({
7093
7459
  hiddenRight: visibleTabs.hiddenRight
7094
7460
  }
7095
7461
  ) }),
7096
- state.phase === "steering" && steeringRecap ? /* @__PURE__ */ jsxs22(
7097
- Box24,
7462
+ state.phase === "steering" && steeringRecap ? /* @__PURE__ */ jsxs23(
7463
+ Box25,
7098
7464
  {
7099
7465
  flexDirection: "column",
7100
7466
  height: adjustedContentHeight,
7101
7467
  children: [
7102
- /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ jsx26(
7468
+ /* @__PURE__ */ jsx27(Box25, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ jsx27(
7103
7469
  StreamOutput3,
7104
7470
  {
7105
7471
  content: displayedContent,
@@ -7109,7 +7475,7 @@ function MarathonApp({
7109
7475
  scrollOffset
7110
7476
  }
7111
7477
  ) }),
7112
- /* @__PURE__ */ jsx26(
7478
+ /* @__PURE__ */ jsx27(
7113
7479
  SteeringPrompt,
7114
7480
  {
7115
7481
  onSubmit: handleSteeringSubmit,
@@ -7122,13 +7488,33 @@ function MarathonApp({
7122
7488
  )
7123
7489
  ]
7124
7490
  }
7125
- ) : showEventStream ? /* @__PURE__ */ jsx26(
7126
- Box24,
7491
+ ) : showFilesPanel ? /* @__PURE__ */ jsx27(
7492
+ Box25,
7127
7493
  {
7128
7494
  flexDirection: "column",
7129
7495
  height: adjustedContentHeight,
7130
7496
  overflow: "hidden",
7131
- children: /* @__PURE__ */ jsx26(
7497
+ children: /* @__PURE__ */ jsx27(
7498
+ FilesPanel,
7499
+ {
7500
+ files: trackedFiles,
7501
+ maxVisibleLines: adjustedContentHeight - 1,
7502
+ selectedIndex: fileCursor,
7503
+ detailFile,
7504
+ detailContent: detailFileContent,
7505
+ detailTotalLines: detailFileTotalLines,
7506
+ detailTruncated: detailFileTruncated,
7507
+ detailScrollOffset: fileDetailScrollOffset
7508
+ }
7509
+ )
7510
+ }
7511
+ ) : showEventStream ? /* @__PURE__ */ jsx27(
7512
+ Box25,
7513
+ {
7514
+ flexDirection: "column",
7515
+ height: adjustedContentHeight,
7516
+ overflow: "hidden",
7517
+ children: /* @__PURE__ */ jsx27(
7132
7518
  EventStreamPanel,
7133
7519
  {
7134
7520
  events: displayedEvents,
@@ -7140,28 +7526,28 @@ function MarathonApp({
7140
7526
  }
7141
7527
  )
7142
7528
  }
7143
- ) : /* @__PURE__ */ jsxs22(
7144
- Box24,
7529
+ ) : /* @__PURE__ */ jsxs23(
7530
+ Box25,
7145
7531
  {
7146
7532
  flexDirection: isStacked ? "column" : "row",
7147
7533
  height: adjustedContentHeight,
7148
7534
  overflow: "hidden",
7149
7535
  children: [
7150
- /* @__PURE__ */ jsx26(
7151
- Box24,
7536
+ /* @__PURE__ */ jsx27(
7537
+ Box25,
7152
7538
  {
7153
7539
  flexDirection: "column",
7154
7540
  flexGrow: 1,
7155
7541
  flexShrink: 1,
7156
7542
  marginLeft: contentMarginLeft,
7157
7543
  marginRight: contentMarginRight,
7158
- children: showLoadingAnimation ? /* @__PURE__ */ jsx26(
7159
- Box24,
7544
+ children: showLoadingAnimation ? /* @__PURE__ */ jsx27(
7545
+ Box25,
7160
7546
  {
7161
7547
  flexGrow: 1,
7162
7548
  minHeight: adjustedContentHeight,
7163
7549
  overflow: "hidden",
7164
- children: /* @__PURE__ */ jsx26(
7550
+ children: /* @__PURE__ */ jsx27(
7165
7551
  LoadingAnimation,
7166
7552
  {
7167
7553
  width: transcriptPaneWidth,
@@ -7169,8 +7555,8 @@ function MarathonApp({
7169
7555
  }
7170
7556
  )
7171
7557
  }
7172
- ) : /* @__PURE__ */ jsxs22(Fragment3, { children: [
7173
- hasReasoning && /* @__PURE__ */ jsx26(Box24, { flexDirection: "column", marginBottom: reasoningCollapsed ? 0 : 1, children: /* @__PURE__ */ jsx26(
7558
+ ) : /* @__PURE__ */ jsxs23(Fragment3, { children: [
7559
+ hasReasoning && /* @__PURE__ */ jsx27(Box25, { flexDirection: "column", marginBottom: reasoningCollapsed ? 0 : 1, children: /* @__PURE__ */ jsx27(
7174
7560
  ReasoningBlock,
7175
7561
  {
7176
7562
  lines: visibleReasoningLines,
@@ -7179,8 +7565,8 @@ function MarathonApp({
7179
7565
  showToggleHint: true
7180
7566
  }
7181
7567
  ) }),
7182
- showThinkingIndicator && /* @__PURE__ */ jsx26(ThinkingIndicator, { startedAt: state.thinkingStartedAt }),
7183
- showUpgradeBrowseHint && /* @__PURE__ */ jsx26(Box24, { marginBottom: 1, children: /* @__PURE__ */ jsx26(
7568
+ showThinkingIndicator && /* @__PURE__ */ jsx27(ThinkingIndicator, { startedAt: state.thinkingStartedAt }),
7569
+ showUpgradeBrowseHint && /* @__PURE__ */ jsx27(Box25, { marginBottom: 1, children: /* @__PURE__ */ jsx27(
7184
7570
  ReasoningBlock,
7185
7571
  {
7186
7572
  lines: [
@@ -7190,7 +7576,7 @@ function MarathonApp({
7190
7576
  compact: true
7191
7577
  }
7192
7578
  ) }),
7193
- /* @__PURE__ */ jsx26(
7579
+ /* @__PURE__ */ jsx27(
7194
7580
  StreamOutput3,
7195
7581
  {
7196
7582
  content: displayedContent,
@@ -7200,22 +7586,22 @@ function MarathonApp({
7200
7586
  scrollOffset
7201
7587
  }
7202
7588
  ),
7203
- selectedIsLive && state.error && !upgradePrompt && /* @__PURE__ */ jsx26(ErrorDisplay4, { error: state.error })
7589
+ selectedIsLive && state.error && !upgradePrompt && /* @__PURE__ */ jsx27(ErrorDisplay4, { error: state.error })
7204
7590
  ] })
7205
7591
  }
7206
7592
  ),
7207
- (hasTools || hasReasoning) && /* @__PURE__ */ jsx26(
7208
- Box24,
7593
+ (hasTools || hasReasoning) && /* @__PURE__ */ jsx27(
7594
+ Box25,
7209
7595
  {
7210
7596
  flexDirection: "column",
7211
7597
  width: isStacked ? void 0 : toolPanelWidth,
7212
7598
  flexShrink: 0,
7213
7599
  borderStyle: "single",
7214
- borderColor: theme27.border,
7215
- backgroundColor: theme27.background,
7216
- paddingX: theme27.panelPaddingX,
7217
- paddingY: theme27.panelPaddingY,
7218
- children: /* @__PURE__ */ jsx26(
7600
+ borderColor: theme28.border,
7601
+ backgroundColor: theme28.background,
7602
+ paddingX: theme28.panelPaddingX,
7603
+ paddingY: theme28.panelPaddingY,
7604
+ children: /* @__PURE__ */ jsx27(
7219
7605
  ToolPanel,
7220
7606
  {
7221
7607
  tools: displayedTools,
@@ -7228,8 +7614,8 @@ function MarathonApp({
7228
7614
  ]
7229
7615
  }
7230
7616
  ),
7231
- showUpgradeModal && upgradePrompt && /* @__PURE__ */ jsx26(
7232
- Box24,
7617
+ showUpgradeModal && upgradePrompt && /* @__PURE__ */ jsx27(
7618
+ Box25,
7233
7619
  {
7234
7620
  marginTop: -adjustedContentHeight,
7235
7621
  marginBottom: -adjustedContentHeight,
@@ -7237,14 +7623,14 @@ function MarathonApp({
7237
7623
  justifyContent: "center",
7238
7624
  alignItems: "center",
7239
7625
  flexShrink: 0,
7240
- children: /* @__PURE__ */ jsx26(UpgradeModal, { prompt: upgradePrompt, width: upgradeModalWidth })
7626
+ children: /* @__PURE__ */ jsx27(UpgradeModal, { prompt: upgradePrompt, width: upgradeModalWidth })
7241
7627
  }
7242
7628
  ),
7243
- /* @__PURE__ */ jsx26(
7629
+ /* @__PURE__ */ jsx27(
7244
7630
  StatusBar2,
7245
7631
  {
7246
7632
  left: `Model: ${currentModel}`,
7247
- center: showEventStream ? detailCenter : currentSandbox ? `Sandbox: ${currentSandbox}` : void 0,
7633
+ center: showFilesPanel ? filesCenter : showEventStream ? detailCenter : currentSandbox ? `Sandbox: ${currentSandbox}` : void 0,
7248
7634
  right: statusRight
7249
7635
  }
7250
7636
  )
@@ -7254,14 +7640,15 @@ function MarathonApp({
7254
7640
  }
7255
7641
 
7256
7642
  // src/commands/agents-task.ts
7257
- import * as fs10 from "fs";
7643
+ import * as fs11 from "fs";
7258
7644
  import {
7259
7645
  RuntypeClient as RuntypeClient2,
7260
- deployWorkflow
7646
+ deployWorkflow,
7647
+ gameWorkflow
7261
7648
  } from "@runtypelabs/sdk";
7262
7649
 
7263
7650
  // src/marathon/checkpoint.ts
7264
- import * as fs4 from "fs";
7651
+ import * as fs5 from "fs";
7265
7652
  import * as path5 from "path";
7266
7653
  function defaultStateDir() {
7267
7654
  return path5.join(process.cwd(), ".runtype", "marathons");
@@ -7286,14 +7673,14 @@ function ensureMarathonFileCheckpoint(taskName, targetPath, stateDir) {
7286
7673
  const normalizedTargetPath = path5.resolve(targetPath);
7287
7674
  const relativeTargetPath = path5.relative(process.cwd(), normalizedTargetPath);
7288
7675
  if (!relativeTargetPath || relativeTargetPath.startsWith("..")) return void 0;
7289
- if (!fs4.existsSync(normalizedTargetPath)) return void 0;
7290
- if (!fs4.statSync(normalizedTargetPath).isFile()) return void 0;
7676
+ if (!fs5.existsSync(normalizedTargetPath)) return void 0;
7677
+ if (!fs5.statSync(normalizedTargetPath).isFile()) return void 0;
7291
7678
  const normalizedRelativeTargetPath = relativeTargetPath.replace(/\\/g, "/");
7292
7679
  if (normalizedRelativeTargetPath.startsWith(".runtype/")) return void 0;
7293
7680
  const checkpointPath = resolveMarathonCheckpointPath(taskName, normalizedTargetPath, stateDir);
7294
- if (fs4.existsSync(checkpointPath)) return checkpointPath;
7295
- fs4.mkdirSync(path5.dirname(checkpointPath), { recursive: true });
7296
- fs4.copyFileSync(normalizedTargetPath, checkpointPath);
7681
+ if (fs5.existsSync(checkpointPath)) return checkpointPath;
7682
+ fs5.mkdirSync(path5.dirname(checkpointPath), { recursive: true });
7683
+ fs5.copyFileSync(normalizedTargetPath, checkpointPath);
7297
7684
  return checkpointPath;
7298
7685
  }
7299
7686
  function restoreMarathonFileCheckpoint(taskName, targetPath, stateDir) {
@@ -7306,15 +7693,15 @@ function restoreMarathonFileCheckpoint(taskName, targetPath, stateDir) {
7306
7693
  };
7307
7694
  }
7308
7695
  const checkpointPath = resolveMarathonCheckpointPath(taskName, normalizedTargetPath, stateDir);
7309
- if (!fs4.existsSync(checkpointPath)) {
7696
+ if (!fs5.existsSync(checkpointPath)) {
7310
7697
  return {
7311
7698
  restored: false,
7312
7699
  checkpointPath,
7313
7700
  error: `No checkpoint found for ${normalizedTargetPath}`
7314
7701
  };
7315
7702
  }
7316
- fs4.mkdirSync(path5.dirname(normalizedTargetPath), { recursive: true });
7317
- fs4.copyFileSync(checkpointPath, normalizedTargetPath);
7703
+ fs5.mkdirSync(path5.dirname(normalizedTargetPath), { recursive: true });
7704
+ fs5.copyFileSync(checkpointPath, normalizedTargetPath);
7318
7705
  return { restored: true, checkpointPath };
7319
7706
  }
7320
7707
 
@@ -7431,7 +7818,7 @@ function isSafeVerificationCommand(command) {
7431
7818
  }
7432
7819
 
7433
7820
  // src/marathon/state.ts
7434
- import * as fs5 from "fs";
7821
+ import * as fs6 from "fs";
7435
7822
  import * as path6 from "path";
7436
7823
  import chalk14 from "chalk";
7437
7824
  function defaultStateDir2() {
@@ -7451,7 +7838,7 @@ function normalizeMarathonStatePath(candidatePath) {
7451
7838
  }
7452
7839
  function marathonStatePathExists(candidatePath) {
7453
7840
  if (!candidatePath) return false;
7454
- return fs5.existsSync(path6.resolve(candidatePath));
7841
+ return fs6.existsSync(path6.resolve(candidatePath));
7455
7842
  }
7456
7843
  function isMarathonArtifactStatePath(candidatePath) {
7457
7844
  if (!candidatePath) return false;
@@ -7619,7 +8006,7 @@ function sanitizeLoadedMarathonState(state) {
7619
8006
  }
7620
8007
  function loadState(filePath) {
7621
8008
  try {
7622
- const raw = fs5.readFileSync(filePath, "utf-8");
8009
+ const raw = fs6.readFileSync(filePath, "utf-8");
7623
8010
  return sanitizeLoadedMarathonState(JSON.parse(raw));
7624
8011
  } catch {
7625
8012
  return null;
@@ -7627,8 +8014,8 @@ function loadState(filePath) {
7627
8014
  }
7628
8015
  function saveState(filePath, state) {
7629
8016
  const dir = path6.dirname(filePath);
7630
- fs5.mkdirSync(dir, { recursive: true });
7631
- fs5.writeFileSync(filePath, JSON.stringify(state, null, 2));
8017
+ fs6.mkdirSync(dir, { recursive: true });
8018
+ fs6.writeFileSync(filePath, JSON.stringify(state, null, 2));
7632
8019
  }
7633
8020
  function extractRunTaskResumeState(state) {
7634
8021
  if (!state) return void 0;
@@ -7653,9 +8040,9 @@ function extractRunTaskResumeState(state) {
7653
8040
  }
7654
8041
  function findStateFile(name, stateDir) {
7655
8042
  const newPath = stateFilePath(name, stateDir || path6.join(process.cwd(), ".runtype", "marathons"));
7656
- if (fs5.existsSync(newPath)) return newPath;
8043
+ if (fs6.existsSync(newPath)) return newPath;
7657
8044
  const oldPath = stateFilePath(name, path6.join(process.cwd(), ".runtype", "tasks"));
7658
- if (fs5.existsSync(oldPath)) return oldPath;
8045
+ if (fs6.existsSync(oldPath)) return oldPath;
7659
8046
  return newPath;
7660
8047
  }
7661
8048
  function findLatestStateFile(stateDir) {
@@ -7665,11 +8052,11 @@ function findLatestStateFile(stateDir) {
7665
8052
  ];
7666
8053
  let latest = null;
7667
8054
  for (const dir of dirs) {
7668
- if (!fs5.existsSync(dir)) continue;
7669
- const files = fs5.readdirSync(dir).filter((f) => f.endsWith(".json"));
8055
+ if (!fs6.existsSync(dir)) continue;
8056
+ const files = fs6.readdirSync(dir).filter((f) => f.endsWith(".json"));
7670
8057
  for (const file of files) {
7671
8058
  const fullPath = path6.join(dir, file);
7672
- const stat = fs5.statSync(fullPath);
8059
+ const stat = fs6.statSync(fullPath);
7673
8060
  if (!latest || stat.mtimeMs > latest.mtime) {
7674
8061
  latest = { name: file.replace(".json", ""), filePath: fullPath, mtime: stat.mtimeMs };
7675
8062
  }
@@ -7760,7 +8147,7 @@ async function resolveMarathonStateResolution(options, taskName, filePath, promp
7760
8147
  if (options.fresh) {
7761
8148
  return { action: "fresh" };
7762
8149
  }
7763
- if (!fs5.existsSync(filePath)) {
8150
+ if (!fs6.existsSync(filePath)) {
7764
8151
  return { action: "fresh" };
7765
8152
  }
7766
8153
  if (!allowPrompt) {
@@ -7897,14 +8284,15 @@ function createSandboxInstructions(provider) {
7897
8284
  "--- Deploy with Preview (Daytona) ---",
7898
8285
  "You also have `deploy_sandbox` to deploy a persistent web server and get a live preview URL.",
7899
8286
  "Call shape:",
7900
- '{ "code": "...", "packageJson": { "dependencies": { "express": "^4.18.2" } }, "language": "typescript", "port": 3000, "startCommand": "npx tsx main.ts" }',
8287
+ '{ "code": "...", "files": { "public/index.html": "..." }, "packageJson": { "dependencies": { "express": "^4.18.2" } }, "language": "typescript", "port": 3000, "startCommand": "npx tsx main.ts" }',
7901
8288
  "Deploy rules:",
7902
8289
  "1. Use this when you need to run a web server (Express, Hono, etc.) that the user can visit in their browser.",
7903
8290
  "2. The `code` is written to main.ts (or main.js/main.py). The `packageJson` object installs dependencies.",
7904
- "3. The server must listen on the specified `port` (default 3000).",
7905
- "4. The sandbox is persistent \u2014 it stays alive across sessions. The same sandbox is reused on subsequent calls.",
7906
- "5. The returned `previewUrl` is a live URL the user can open in their browser.",
7907
- "6. Prefer this over `run_sandbox_code` when building web apps, APIs, or anything with a UI."
8291
+ "3. The `files` parameter writes additional files (path \u2192 content). Use this for HTML/CSS/JS assets instead of embedding them in template literals.",
8292
+ "4. The server must listen on the specified `port` (default 3000).",
8293
+ "5. The sandbox is persistent \u2014 it stays alive across sessions. The same sandbox is reused on subsequent calls.",
8294
+ "6. The returned `previewUrl` is a live URL the user can open in their browser.",
8295
+ "7. Prefer this over `run_sandbox_code` when building web apps, APIs, or anything with a UI."
7908
8296
  ].join("\n");
7909
8297
  }
7910
8298
  function createSandboxLocalTool(client, provider, debugMode) {
@@ -8048,6 +8436,11 @@ function createDeploySandboxLocalTool(client, debugMode) {
8048
8436
  startCommand: {
8049
8437
  type: "string",
8050
8438
  description: "Custom start command (default: auto-detected from language)"
8439
+ },
8440
+ files: {
8441
+ type: "object",
8442
+ description: 'Additional files to write (path \u2192 content), e.g. { "public/index.html": "<html>..." }. Use this for multi-file projects instead of embedding HTML in template literals.',
8443
+ additionalProperties: { type: "string" }
8051
8444
  }
8052
8445
  },
8053
8446
  required: ["code"]
@@ -8063,6 +8456,7 @@ function createDeploySandboxLocalTool(client, debugMode) {
8063
8456
  ...typeof args.language === "string" ? { language: args.language } : {},
8064
8457
  ...typeof args.port === "number" ? { port: args.port } : {},
8065
8458
  ...typeof args.startCommand === "string" ? { startCommand: args.startCommand } : {},
8459
+ ...args.files && typeof args.files === "object" && !Array.isArray(args.files) ? { files: args.files } : {},
8066
8460
  // Reuse existing sandbox if one was created
8067
8461
  ...activeDeploySandboxId ? { sandboxId: activeDeploySandboxId } : {}
8068
8462
  };
@@ -8109,12 +8503,12 @@ async function cleanupDeploySandboxes(client) {
8109
8503
  }
8110
8504
 
8111
8505
  // src/marathon/local-tools.ts
8112
- import * as fs7 from "fs";
8506
+ import * as fs8 from "fs";
8113
8507
  import * as path8 from "path";
8114
8508
  import { spawnSync } from "child_process";
8115
8509
 
8116
8510
  // src/marathon/repo-discovery.ts
8117
- import * as fs6 from "fs";
8511
+ import * as fs7 from "fs";
8118
8512
  import * as path7 from "path";
8119
8513
  var IGNORED_REPO_DIRS = /* @__PURE__ */ new Set([
8120
8514
  ".git",
@@ -8219,9 +8613,9 @@ function shouldIgnoreRepoEntry(entryPath) {
8219
8613
  }
8220
8614
  function safeReadTextFile(filePath) {
8221
8615
  try {
8222
- const stat = fs6.statSync(filePath);
8616
+ const stat = fs7.statSync(filePath);
8223
8617
  if (!stat.isFile() || stat.size > MAX_FILE_BYTES_TO_SCAN) return null;
8224
- const buffer = fs6.readFileSync(filePath);
8618
+ const buffer = fs7.readFileSync(filePath);
8225
8619
  if (buffer.includes(0)) return null;
8226
8620
  return buffer.toString("utf-8");
8227
8621
  } catch {
@@ -8229,14 +8623,14 @@ function safeReadTextFile(filePath) {
8229
8623
  }
8230
8624
  }
8231
8625
  function walkRepo(startPath, visitor) {
8232
- if (!fs6.existsSync(startPath) || !fs6.statSync(startPath).isDirectory()) return;
8626
+ if (!fs7.existsSync(startPath) || !fs7.statSync(startPath).isDirectory()) return;
8233
8627
  const stack = [startPath];
8234
8628
  while (stack.length > 0) {
8235
8629
  const currentDir = stack.pop();
8236
8630
  if (!currentDir) continue;
8237
8631
  let entries;
8238
8632
  try {
8239
- entries = fs6.readdirSync(currentDir, { withFileTypes: true });
8633
+ entries = fs7.readdirSync(currentDir, { withFileTypes: true });
8240
8634
  } catch {
8241
8635
  continue;
8242
8636
  }
@@ -8279,7 +8673,7 @@ function buildTree(dirPath, maxDepth, depth = 0) {
8279
8673
  if (depth > maxDepth || shouldIgnoreRepoEntry(dirPath)) return [];
8280
8674
  let entries;
8281
8675
  try {
8282
- entries = fs6.readdirSync(dirPath, { withFileTypes: true });
8676
+ entries = fs7.readdirSync(dirPath, { withFileTypes: true });
8283
8677
  } catch (error) {
8284
8678
  const message = error instanceof Error ? error.message : String(error);
8285
8679
  return [`${" ".repeat(depth)}[error reading ${normalizeToolPath(dirPath)}: ${message}]`];
@@ -8308,7 +8702,7 @@ var defaultLocalTools = {
8308
8702
  execute: async (args) => {
8309
8703
  const toolFilePath = String(args.path || "");
8310
8704
  if (!toolFilePath) return "Error: path is required";
8311
- return fs7.readFileSync(toolFilePath, "utf-8");
8705
+ return fs8.readFileSync(toolFilePath, "utf-8");
8312
8706
  }
8313
8707
  },
8314
8708
  write_file: {
@@ -8326,8 +8720,8 @@ var defaultLocalTools = {
8326
8720
  if (!toolFilePath) return "Error: path is required";
8327
8721
  const content = String(args.content || "");
8328
8722
  const dir = path8.dirname(toolFilePath);
8329
- fs7.mkdirSync(dir, { recursive: true });
8330
- fs7.writeFileSync(toolFilePath, content);
8723
+ fs8.mkdirSync(dir, { recursive: true });
8724
+ fs8.writeFileSync(toolFilePath, content);
8331
8725
  return "ok";
8332
8726
  }
8333
8727
  },
@@ -8339,7 +8733,7 @@ var defaultLocalTools = {
8339
8733
  },
8340
8734
  execute: async (args) => {
8341
8735
  const dirPath = String(args.path || ".");
8342
- return fs7.readdirSync(dirPath).join("\n");
8736
+ return fs8.readdirSync(dirPath).join("\n");
8343
8737
  }
8344
8738
  },
8345
8739
  search_repo: {
@@ -8366,7 +8760,7 @@ var defaultLocalTools = {
8366
8760
  const query = String(args.query || "").trim();
8367
8761
  if (!query) return "Error: query is required";
8368
8762
  const startPath = path8.resolve(String(args.path || "."));
8369
- if (!fs7.existsSync(startPath)) return `Error: path does not exist: ${startPath}`;
8763
+ if (!fs8.existsSync(startPath)) return `Error: path does not exist: ${startPath}`;
8370
8764
  const maxResults = Math.max(
8371
8765
  1,
8372
8766
  Math.min(100, Number.isFinite(Number(args.maxResults)) ? Math.floor(Number(args.maxResults)) : 20)
@@ -8429,7 +8823,7 @@ var defaultLocalTools = {
8429
8823
  const pattern = String(args.pattern || "").trim();
8430
8824
  if (!pattern) return "Error: pattern is required";
8431
8825
  const startPath = path8.resolve(String(args.path || "."));
8432
- if (!fs7.existsSync(startPath)) return `Error: path does not exist: ${startPath}`;
8826
+ if (!fs8.existsSync(startPath)) return `Error: path does not exist: ${startPath}`;
8433
8827
  const maxResults = Math.max(
8434
8828
  1,
8435
8829
  Math.min(
@@ -8469,8 +8863,8 @@ var defaultLocalTools = {
8469
8863
  },
8470
8864
  execute: async (args) => {
8471
8865
  const dirPath = path8.resolve(String(args.path || "."));
8472
- if (!fs7.existsSync(dirPath)) return `Error: path does not exist: ${dirPath}`;
8473
- if (!fs7.statSync(dirPath).isDirectory()) return `Error: path is not a directory: ${dirPath}`;
8866
+ if (!fs8.existsSync(dirPath)) return `Error: path does not exist: ${dirPath}`;
8867
+ if (!fs8.statSync(dirPath).isDirectory()) return `Error: path is not a directory: ${dirPath}`;
8474
8868
  const maxDepth = Math.max(
8475
8869
  0,
8476
8870
  Math.min(6, Number.isFinite(Number(args.maxDepth)) ? Math.floor(Number(args.maxDepth)) : 2)
@@ -8497,8 +8891,8 @@ function createCheckpointedWriteFileTool(taskName, stateDir) {
8497
8891
  const content = String(args.content || "");
8498
8892
  ensureMarathonFileCheckpoint(taskName, toolFilePath, stateDir);
8499
8893
  const dir = path8.dirname(toolFilePath);
8500
- fs7.mkdirSync(dir, { recursive: true });
8501
- fs7.writeFileSync(toolFilePath, content);
8894
+ fs8.mkdirSync(dir, { recursive: true });
8895
+ fs8.writeFileSync(toolFilePath, content);
8502
8896
  return "ok";
8503
8897
  }
8504
8898
  };
@@ -8693,7 +9087,7 @@ function createLoopDetector(options = {}) {
8693
9087
  }
8694
9088
 
8695
9089
  // src/marathon/context-offload.ts
8696
- import * as fs8 from "fs";
9090
+ import * as fs9 from "fs";
8697
9091
  import * as path9 from "path";
8698
9092
  var DEFAULT_OUTPUT_THRESHOLD = 2e3;
8699
9093
  var DEFAULT_PREVIEW_LENGTH = 200;
@@ -8718,8 +9112,8 @@ function offloadToolOutput(taskName, toolName, output, options = {}, stateDir) {
8718
9112
  );
8719
9113
  const fileName = `${stateSafeName3(toolName)}-${offloadCounter}.txt`;
8720
9114
  const filePath = path9.join(dir, fileName);
8721
- fs8.mkdirSync(dir, { recursive: true });
8722
- fs8.writeFileSync(filePath, output, "utf-8");
9115
+ fs9.mkdirSync(dir, { recursive: true });
9116
+ fs9.writeFileSync(filePath, output, "utf-8");
8723
9117
  const preview = output.slice(0, previewLength).replace(/\n/g, " ");
8724
9118
  const relativePath = path9.relative(process.cwd(), filePath);
8725
9119
  const reference = [
@@ -8743,7 +9137,7 @@ function withOffloading(tool, taskName, toolName, options, stateDir) {
8743
9137
  }
8744
9138
 
8745
9139
  // src/marathon/recipes.ts
8746
- import * as fs9 from "fs";
9140
+ import * as fs10 from "fs";
8747
9141
  import * as path10 from "path";
8748
9142
  var RECIPES_DIR = ".marathon/recipes";
8749
9143
  function loadRecipe(nameOrPath, cwd) {
@@ -8757,9 +9151,9 @@ function loadRecipe(nameOrPath, cwd) {
8757
9151
  path10.resolve(baseCwd, RECIPES_DIR, nameOrPath, "recipe.json")
8758
9152
  ];
8759
9153
  for (const candidate of candidates) {
8760
- if (!fs9.existsSync(candidate)) continue;
9154
+ if (!fs10.existsSync(candidate)) continue;
8761
9155
  try {
8762
- const raw = fs9.readFileSync(candidate, "utf-8");
9156
+ const raw = fs10.readFileSync(candidate, "utf-8");
8763
9157
  const parsed = JSON.parse(raw);
8764
9158
  if (parsed.meta?.name && parsed.meta?.version) {
8765
9159
  return parsed;
@@ -8774,14 +9168,14 @@ var RULES_DIR = ".marathon/rules";
8774
9168
  function loadRules(cwd) {
8775
9169
  const baseCwd = cwd || process.cwd();
8776
9170
  const rulesDir = path10.resolve(baseCwd, RULES_DIR);
8777
- if (!fs9.existsSync(rulesDir)) return [];
9171
+ if (!fs10.existsSync(rulesDir)) return [];
8778
9172
  const rules = [];
8779
9173
  try {
8780
- const entries = fs9.readdirSync(rulesDir).filter((f) => f.endsWith(".md"));
9174
+ const entries = fs10.readdirSync(rulesDir).filter((f) => f.endsWith(".md"));
8781
9175
  for (const entry of entries) {
8782
9176
  const filePath = path10.join(rulesDir, entry);
8783
9177
  try {
8784
- const raw = fs9.readFileSync(filePath, "utf-8");
9178
+ const raw = fs10.readFileSync(filePath, "utf-8");
8785
9179
  const parsed = parseRuleFile(raw);
8786
9180
  rules.push({ ...parsed, filePath });
8787
9181
  } catch {
@@ -8912,7 +9306,7 @@ async function taskAction(agent, options) {
8912
9306
  forcedResumeFilePath = stateResolution.filePath;
8913
9307
  } else {
8914
9308
  resumeRequested = false;
8915
- if (options.fresh && fs10.existsSync(filePath)) {
9309
+ if (options.fresh && fs11.existsSync(filePath)) {
8916
9310
  console.log(chalk16.gray(`Starting fresh and ignoring saved local state at ${filePath}`));
8917
9311
  }
8918
9312
  }
@@ -9074,6 +9468,7 @@ ${rulesContext}`;
9074
9468
  showFinish: !options.noFinish,
9075
9469
  initialRunnerPosition,
9076
9470
  initialRunnerLaps,
9471
+ planPath: lastKnownState?.planPath ?? resumeState?.planPath,
9077
9472
  debug: options.debug,
9078
9473
  plainText: options.plainText ?? false,
9079
9474
  noSteer: options.noSteer ?? false,
@@ -9126,6 +9521,7 @@ Saving state... done. Session saved to ${filePath}`);
9126
9521
  let shouldContinue = true;
9127
9522
  let accumulatedSessions = 0;
9128
9523
  let accumulatedCost = 0;
9524
+ let accumulatedTokens = { input: 0, output: 0 };
9129
9525
  let lastResult = null;
9130
9526
  let lastSessionMessages = [];
9131
9527
  while (shouldContinue) {
@@ -9227,8 +9623,8 @@ Saving state... done. Session saved to ${filePath}`);
9227
9623
  const recap = {
9228
9624
  sessionNumber: adjustedState.sessionCount,
9229
9625
  toolCallsMade: currentActions.getState().tools.length,
9230
- tokensInput: 0,
9231
- tokensOutput: 0,
9626
+ tokensInput: state.totalTokens?.input ?? 0,
9627
+ tokensOutput: state.totalTokens?.output ?? 0,
9232
9628
  cost: state.totalCost,
9233
9629
  outputPreview: adjustedState.lastOutput.slice(0, 100)
9234
9630
  };
@@ -9308,6 +9704,12 @@ Saving state... done. Session saved to ${filePath}`);
9308
9704
  }
9309
9705
  accumulatedSessions += result2.sessionCount;
9310
9706
  accumulatedCost += result2.totalCost;
9707
+ if (result2.totalTokens) {
9708
+ accumulatedTokens = {
9709
+ input: accumulatedTokens.input + result2.totalTokens.input,
9710
+ output: accumulatedTokens.output + result2.totalTokens.output
9711
+ };
9712
+ }
9311
9713
  lastResult = result2;
9312
9714
  if (shouldContinue) {
9313
9715
  previousMessages = lastSessionMessages;
@@ -9326,8 +9728,8 @@ Saving state... done. Session saved to ${filePath}`);
9326
9728
  const recap = {
9327
9729
  sessionNumber: priorSessionCount + accumulatedSessions,
9328
9730
  toolCallsMade: currentActions.getState().tools.length,
9329
- tokensInput: 0,
9330
- tokensOutput: 0,
9731
+ tokensInput: accumulatedTokens.input,
9732
+ tokensOutput: accumulatedTokens.output,
9331
9733
  cost: accumulatedCost,
9332
9734
  outputPreview: terminalPreview.slice(0, 100)
9333
9735
  };
@@ -9484,11 +9886,29 @@ Resume: ${buildResumeCommand(agent, options, parsedSandbox)}`
9484
9886
  }
9485
9887
  function detectDeployWorkflow(message, sandboxProvider, resumeState) {
9486
9888
  if (sandboxProvider !== "daytona") return void 0;
9889
+ if (resumeState?.workflowVariant === "game") return gameWorkflow;
9890
+ if (resumeState?.workflowPhase === "design" || resumeState?.workflowPhase === "build" || resumeState?.workflowPhase === "verify") {
9891
+ return gameWorkflow;
9892
+ }
9487
9893
  if (resumeState?.workflowVariant === "deploy") return deployWorkflow;
9488
9894
  if (resumeState?.workflowPhase === "scaffold" || resumeState?.workflowPhase === "deploy") {
9489
9895
  return deployWorkflow;
9490
9896
  }
9491
9897
  const lower = message.toLowerCase();
9898
+ const gamePatterns = [
9899
+ /\b3d\b.*\bgame\b/,
9900
+ /\bgame\b.*\b3d\b/,
9901
+ /\bthree\.?js\b/,
9902
+ /\bphaser\b/,
9903
+ /\bwebgl\b/,
9904
+ /\bplatformer\b/,
9905
+ /\bracing\b.*\bgame\b/,
9906
+ /\bgame\b.*\bracing\b/,
9907
+ /\bgame\b/
9908
+ ];
9909
+ if (gamePatterns.some((p) => p.test(lower))) {
9910
+ return gameWorkflow;
9911
+ }
9492
9912
  const deployPatterns = [
9493
9913
  /\bdeploy\b/,
9494
9914
  /\bsandbox\b/,
@@ -10439,15 +10859,15 @@ import chalk20 from "chalk";
10439
10859
  import React16 from "react";
10440
10860
  import { render as render14 } from "ink";
10441
10861
  import { useState as useState24, useEffect as useEffect21 } from "react";
10442
- import { Text as Text24 } from "ink";
10443
- import { readFileSync as readFileSync8 } from "fs";
10862
+ import { Text as Text25 } from "ink";
10863
+ import { readFileSync as readFileSync9 } from "fs";
10444
10864
  var evalCommand = new Command15("eval").description("Manage evaluations");
10445
10865
  evalCommand.command("submit").description("Submit an eval batch").requiredOption("-f, --flow <id>", "Flow ID to evaluate").requiredOption("-r, --records <file>", "JSON file with record IDs").option("-n, --name <name>", "Eval batch name").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
10446
10866
  const apiKey = await ensureAuth();
10447
10867
  if (!apiKey) return;
10448
10868
  let recordIds;
10449
10869
  try {
10450
- const content = readFileSync8(options.records, "utf-8");
10870
+ const content = readFileSync9(options.records, "utf-8");
10451
10871
  const parsed = JSON.parse(content);
10452
10872
  recordIds = Array.isArray(parsed) ? parsed : parsed.recordIds || parsed.records || [];
10453
10873
  } catch (error) {
@@ -10597,7 +11017,7 @@ evalCommand.command("list").description("List eval batches").option("--flow <id>
10597
11017
  const progress = b.totalRecords ? `${b.completedRecords ?? 0}/${b.totalRecords}` : "";
10598
11018
  const statusColor = b.status === "completed" ? "green" : "yellow";
10599
11019
  return React16.createElement(
10600
- Text24,
11020
+ Text25,
10601
11021
  { color: statusColor },
10602
11022
  ` ${b.id} ${name} [${b.status}] ${progress}`
10603
11023
  );
@@ -10746,7 +11166,7 @@ import chalk21 from "chalk";
10746
11166
  import React17 from "react";
10747
11167
  import { render as render15 } from "ink";
10748
11168
  import { useState as useState25, useEffect as useEffect22 } from "react";
10749
- import { Text as Text25 } from "ink";
11169
+ import { Text as Text26 } from "ink";
10750
11170
  var apiKeysCommand = new Command16("api-keys").description("Manage API keys");
10751
11171
  apiKeysCommand.command("list").description("List your API keys").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
10752
11172
  const apiKey = await ensureAuth();
@@ -10813,7 +11233,7 @@ apiKeysCommand.command("list").description("List your API keys").option("--json"
10813
11233
  const k = item;
10814
11234
  const prefix = k.prefix ? ` (${k.prefix}...)` : "";
10815
11235
  const lastUsed = k.lastUsedAt ? ` last used: ${k.lastUsedAt}` : "";
10816
- return React17.createElement(Text25, null, ` ${k.id} ${k.name}${prefix}${lastUsed}`);
11236
+ return React17.createElement(Text26, null, ` ${k.id} ${k.name}${prefix}${lastUsed}`);
10817
11237
  }
10818
11238
  });
10819
11239
  };
@@ -11165,7 +11585,7 @@ import chalk22 from "chalk";
11165
11585
  import React18 from "react";
11166
11586
  import { render as render16 } from "ink";
11167
11587
  import { useState as useState26, useEffect as useEffect23 } from "react";
11168
- import { Text as Text26 } from "ink";
11588
+ import { Text as Text27 } from "ink";
11169
11589
  var analyticsCommand = new Command17("analytics").description("View analytics and execution results");
11170
11590
  analyticsCommand.command("stats").description("Show account statistics").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
11171
11591
  const apiKey = await ensureAuth();
@@ -11309,7 +11729,7 @@ analyticsCommand.command("results").description("List execution results").option
11309
11729
  const r = item;
11310
11730
  const statusColor = r.status === "completed" ? "green" : "red";
11311
11731
  return React18.createElement(
11312
- Text26,
11732
+ Text27,
11313
11733
  { color: statusColor },
11314
11734
  ` ${r.id} [${r.status}] flow=${r.flowId} record=${r.recordId}${r.createdAt ? ` ${r.createdAt}` : ""}`
11315
11735
  );
@@ -11531,7 +11951,7 @@ import chalk24 from "chalk";
11531
11951
  import React20 from "react";
11532
11952
  import { render as render18 } from "ink";
11533
11953
  import { useState as useState28, useEffect as useEffect25 } from "react";
11534
- import { Text as Text27 } from "ink";
11954
+ import { Text as Text28 } from "ink";
11535
11955
  var flowVersionsCommand = new Command19("flow-versions").description(
11536
11956
  "Manage flow versions"
11537
11957
  );
@@ -11593,7 +12013,7 @@ flowVersionsCommand.command("list <flowId>").description("List all versions for
11593
12013
  const v = item;
11594
12014
  const publishedTag = v.published ? " [published]" : "";
11595
12015
  const versionNum = v.version !== void 0 ? `v${v.version}` : v.id;
11596
- return React20.createElement(Text27, null, ` ${v.id} ${versionNum}${publishedTag}${v.createdAt ? ` ${v.createdAt}` : ""}`);
12016
+ return React20.createElement(Text28, null, ` ${v.id} ${versionNum}${publishedTag}${v.createdAt ? ` ${v.createdAt}` : ""}`);
11597
12017
  }
11598
12018
  });
11599
12019
  };
@@ -11788,7 +12208,7 @@ loadEnv();
11788
12208
  function getPackageVersion() {
11789
12209
  try {
11790
12210
  const pkgPath = join6(dirname4(fileURLToPath(import.meta.url)), "..", "package.json");
11791
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
12211
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
11792
12212
  return pkg.version || "0.0.0";
11793
12213
  } catch {
11794
12214
  return "0.0.0";