@ordewell/cli 0.5.6 → 0.6.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 +4 -2
  2. package/dist/main.js +128 -105
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -12,8 +12,10 @@ npm install -g @ordewell/cli # or: npm install -g ordewell
12
12
  ```
13
13
 
14
14
  **Requirements:** Node.js 20 or newer, at least one of Claude Code, Codex or
15
- OpenCode, git for task isolation, and tmux for the terminal UI. Linux, macOS and
16
- Windows are supported; on Windows, run the terminal UI under WSL.
15
+ OpenCode, and git for task isolation. tmux is optional: only the terminal
16
+ transport (`/transport terminal`) uses it, to give each task a terminal window
17
+ you can open. Linux, macOS and Windows are supported; on Windows, run the
18
+ terminal UI under WSL.
17
19
 
18
20
  ## Usage
19
21
 
package/dist/main.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/main.ts
27
- var import_core33 = require("@ordewell/core");
27
+ var import_core34 = require("@ordewell/core");
28
28
 
29
29
  // src/utils/env.ts
30
30
  var import_fs = require("fs");
@@ -138,8 +138,8 @@ Planner, models and runners:
138
138
  ordewell key [set <p> <key>] Show which providers have a key, or store one
139
139
  ordewell runners [<id> on|off] Enable or disable runners (claude-code, opencode, codex)
140
140
  ordewell allowlist set|clear|show Limit which models a runner may use
141
- ordewell auto [on|off] Autonomous permission mode for new sessions
142
- ordewell transport [terminal|structured] Experimental: run Claude Code tasks over its protocol, from the next run
141
+ ordewell auto [full|auto] Autonomy level for new sessions: Full auto or Auto
142
+ ordewell transport [terminal|structured] Structured (default) or terminal task transport, from the next run
143
143
  ordewell parallel [<n>] How many AI tasks run at once (default 3; applies to a live run)
144
144
  ordewell refresh Re-discover runners and model catalogs
145
145
  ordewell models List every provider's catalog (works without a server)
@@ -212,7 +212,7 @@ Environment:
212
212
  ORDEWELL_PLANNER_EFFORT Thinking effort for a coding-agent planner
213
213
  ORCHESTRATOR_MODEL Default: deepseek/deepseek-v4-flash
214
214
  OPENROUTER_BASE_URL Default: https://openrouter.ai/api/v1
215
- ORDEWELL_AUTONOMOUS_MODE Approval posture for new sessions (see "ordewell auto")
215
+ ORDEWELL_AUTONOMOUS_MODE true: Full auto, false: Auto, for new sessions (see "ordewell auto")
216
216
  ORDEWELL_PORT Daemon port CLI commands target (default: 3742)
217
217
  ORDEWELL_DIRENV false: agents skip the project's direnv .envrc
218
218
  ORDEWELL_RESEARCH_ENABLED true (default) or false
@@ -2277,7 +2277,7 @@ async function handleVerify(subArgs, api) {
2277
2277
  }
2278
2278
 
2279
2279
  // src/commands/transport.ts
2280
- var describe = (transport) => `Runner transport (experimental): ${transport === "structured" ? "structured" : "terminal"} \u2014 applies from the next run`;
2280
+ var describe = (transport) => `Runner transport: ${transport === "terminal" ? "terminal" : "structured"} \u2014 applies from the next run`;
2281
2281
  async function handleTransport(subArgs, api) {
2282
2282
  const action = subArgs[0];
2283
2283
  const port = api ? void 0 : await ensureDaemon(resolvePort(subArgs));
@@ -2621,14 +2621,14 @@ var import_core15 = require("@ordewell/core");
2621
2621
  function show(state, { view: conversation, gate: turnGate }) {
2622
2622
  const gated = turnGate === state.turnGate ? state : { ...state, turnGate };
2623
2623
  if (conversation === state.conversation) return gated;
2624
- const scroll = conversation.nextId > state.conversation.nextId ? 0 : state.scroll;
2625
- return { ...gated, conversation, scroll };
2624
+ return { ...gated, conversation };
2626
2625
  }
2627
2626
  function fold(state, input) {
2628
2627
  return show(state, { view: (0, import_core15.reduceConversation)(state.conversation, input), gate: state.turnGate });
2629
2628
  }
2630
2629
  function say(state, role, text) {
2631
- return fold(state, { type: "local_entry", role, text });
2630
+ const said = fold(state, { type: "local_entry", role, text });
2631
+ return said.conversation.nextId > state.conversation.nextId ? { ...said, scroll: 0 } : said;
2632
2632
  }
2633
2633
  function hear(state, message) {
2634
2634
  return show(state, (0, import_core15.followTurn)(state.conversation, state.turnGate, message));
@@ -3208,6 +3208,7 @@ async function handleKey(subArgs, injectedApi) {
3208
3208
  }
3209
3209
 
3210
3210
  // src/commands/runners.ts
3211
+ var import_core19 = require("@ordewell/core");
3211
3212
  var USAGE7 = "Usage: ordewell runners [<runner-id> on|off]";
3212
3213
  async function handleRunners(subArgs, injectedApi) {
3213
3214
  const [runner, toggle] = positionals(subArgs);
@@ -3232,21 +3233,21 @@ async function handleRunners(subArgs, injectedApi) {
3232
3233
  await api.setRunnerEnabled(runner, enabled);
3233
3234
  console.log(`${runner} ${enabled ? "enabled" : "disabled"}.`);
3234
3235
  }
3235
- var AUTO_USAGE = "Usage: ordewell auto [on|off]";
3236
+ var AUTO_USAGE = "Usage: ordewell auto [full|auto]";
3236
3237
  function handleAuto(subArgs) {
3237
3238
  const [arg] = positionals(subArgs);
3238
3239
  const currentRaw = process.env.ORDEWELL_AUTONOMOUS_MODE;
3239
3240
  const current = currentRaw !== "false" && currentRaw !== "0";
3240
3241
  if (!arg) {
3241
- console.log(`Autonomous mode: ${current ? "ON" : "OFF"}`);
3242
+ console.log(`Autonomy level: ${(0, import_core19.autonomyLevelLabel)(current)}`);
3242
3243
  console.log(` ${AUTO_USAGE}`);
3243
3244
  return;
3244
3245
  }
3245
- const enabled = arg === "on" ? true : arg === "off" ? false : null;
3246
+ const enabled = (0, import_core19.parseAutonomyLevel)(arg);
3246
3247
  if (enabled === null) fail(AUTO_USAGE);
3247
3248
  writeEnvVar(findEnvFile(), "ORDEWELL_AUTONOMOUS_MODE", String(enabled));
3248
3249
  process.env.ORDEWELL_AUTONOMOUS_MODE = String(enabled);
3249
- console.log(`Autonomous mode ${enabled ? "on" : "off"} for new sessions.`);
3250
+ console.log(`Autonomy level: ${(0, import_core19.autonomyLevelLabel)(enabled)} for new sessions.`);
3250
3251
  }
3251
3252
  async function handleRefresh(subArgs, injectedApi) {
3252
3253
  const api = await connect(subArgs, injectedApi);
@@ -3278,7 +3279,7 @@ async function handleApprove(subArgs, injectedApi) {
3278
3279
  // src/tui/terminalLauncher.ts
3279
3280
  var import_child_process3 = require("child_process");
3280
3281
  var import_util = require("util");
3281
- var import_core19 = require("@ordewell/core");
3282
+ var import_core20 = require("@ordewell/core");
3282
3283
  var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
3283
3284
  var defaultExecFile = async (command, args) => {
3284
3285
  const { stdout, stderr } = await execFileAsync(command, args);
@@ -3298,16 +3299,16 @@ var defaultWhich = (bin) => {
3298
3299
  async function openTaskTerminal(port, planSessionId, taskId, deps = {}) {
3299
3300
  const platform = deps.platform ?? process.platform;
3300
3301
  const env = deps.env ?? process.env;
3301
- const hasTmuxImpl = deps.hasTmuxImpl ?? import_core19.hasTmux;
3302
+ const hasTmuxImpl = deps.hasTmuxImpl ?? import_core20.hasTmux;
3302
3303
  const execFileImpl = deps.execFileImpl ?? defaultExecFile;
3303
3304
  const spawnImpl = deps.spawnImpl ?? defaultSpawn;
3304
3305
  const which = deps.which ?? defaultWhich;
3305
3306
  if (!hasTmuxImpl()) {
3306
- return { ok: false, message: "tmux is not installed \u2014 install it to get a real terminal per task." };
3307
+ return { ok: false, message: "No terminal window for this task: tmux is not installed. Install tmux to get one for terminal-transport tasks; a structured task has its log view instead." };
3307
3308
  }
3308
- const session = (0, import_core19.tmuxSessionName)(port);
3309
- const socket = (0, import_core19.tmuxSocketName)(port);
3310
- const base = (0, import_core19.tmuxWindowName)(taskId, planSessionId);
3309
+ const session = (0, import_core20.tmuxSessionName)(port);
3310
+ const socket = (0, import_core20.tmuxSocketName)(port);
3311
+ const base = (0, import_core20.tmuxWindowName)(taskId, planSessionId);
3311
3312
  let windows;
3312
3313
  try {
3313
3314
  const { stdout } = await execFileImpl("tmux", ["-L", socket, "list-windows", "-t", session, "-F", "#{window_name}"]);
@@ -3317,7 +3318,7 @@ async function openTaskTerminal(port, planSessionId, taskId, deps = {}) {
3317
3318
  }
3318
3319
  const window = latestAttempt(windows, base);
3319
3320
  if (!window) {
3320
- return { ok: false, message: "This task hasn't opened a terminal yet \u2014 it may still be pending." };
3321
+ return { ok: false, message: "This task has no live terminal and no saved log \u2014 it may still be pending." };
3321
3322
  }
3322
3323
  const manualHint = `tmux -L ${socket} attach -t ${session}:${window}`;
3323
3324
  const viewer = `${session}-view-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
@@ -3601,7 +3602,7 @@ Dependencies \xB7 #${task.order} ${task.title}
3601
3602
  }
3602
3603
 
3603
3604
  // src/tui/index.ts
3604
- var import_core32 = require("@ordewell/core");
3605
+ var import_core33 = require("@ordewell/core");
3605
3606
 
3606
3607
  // src/tui/editor.ts
3607
3608
  function emptyEditor() {
@@ -3763,8 +3764,8 @@ var SLASH_COMMANDS = [
3763
3764
  { name: "key", usage: "/key [set <provider> <key>]", description: "Configure API provider keys", category: "models" },
3764
3765
  { name: "allowlist", usage: "/allowlist [set <runner> <ids> | clear <runner>]", description: "Limit which models a runner may use", category: "models" },
3765
3766
  { name: "runners", usage: "/runners [<id> on|off]", description: "Enable or disable runners (claude-code, opencode, codex)", category: "models" },
3766
- { name: "auto", usage: "/auto [on|off]", description: "Autonomous permission mode for new plans", category: "models" },
3767
- { name: "transport", usage: "/transport [terminal|structured]", description: "Experimental: run Claude Code tasks over its protocol instead of a terminal, from the next run", category: "models" },
3767
+ { name: "auto", usage: "/auto [full|auto]", description: "Autonomy level for new plans: Full auto or Auto", category: "models" },
3768
+ { name: "transport", usage: "/transport [terminal|structured]", description: "Drive tasks through each runner's protocol (structured) or a terminal, from the next run", category: "models" },
3768
3769
  { name: "parallel", usage: "/parallel [<n>]", description: "How many AI tasks run at once (applies to a live run)", category: "models" },
3769
3770
  { name: "refresh", usage: "/refresh", description: "Re-discover runners and model catalogs", category: "models" },
3770
3771
  // Skills
@@ -3849,7 +3850,7 @@ function tokenCompletions(token) {
3849
3850
  }
3850
3851
 
3851
3852
  // src/tui/blocks.ts
3852
- var import_core20 = require("@ordewell/core");
3853
+ var import_core21 = require("@ordewell/core");
3853
3854
 
3854
3855
  // src/tui/markdown.ts
3855
3856
  function renderMarkdown(source, cols) {
@@ -4057,16 +4058,16 @@ function resultLines(block, room, detailAll) {
4057
4058
  const fit2 = (line) => detailAll ? wrap(line, room) : [truncate(line, room)];
4058
4059
  const outcome = unsettledOutcome(block);
4059
4060
  if (outcome) {
4060
- const [reason, ...rest] = (0, import_core20.outputLines)(block.output).map(sanitize);
4061
+ const [reason, ...rest] = (0, import_core21.outputLines)(block.output).map(sanitize);
4061
4062
  const status = fit2(reason ? `${outcome.label}: ${reason}` : outcome.label).map(outcome.paint);
4062
4063
  return detailAll ? [...status, ...rest.flatMap(fit2).map(style.grey)] : status;
4063
4064
  }
4064
4065
  if (detailAll) {
4065
- const lines2 = (0, import_core20.outputLines)(block.output).map(sanitize);
4066
+ const lines2 = (0, import_core21.outputLines)(block.output).map(sanitize);
4066
4067
  const note = lines2.length > PREVIEW_LINES ? ["\u2026 (ctrl+o to collapse)"] : [];
4067
4068
  return lines2.length > 0 ? [...lines2.flatMap(fit2), ...note].map(style.grey) : [style.grey("(no output)")];
4068
4069
  }
4069
- const { lines, hiddenLineCount } = (0, import_core20.outputPreview)(block.output, PREVIEW_LINES);
4070
+ const { lines, hiddenLineCount } = (0, import_core21.outputPreview)(block.output, PREVIEW_LINES);
4070
4071
  if (lines.length === 0) return [style.grey("(no output)")];
4071
4072
  const more = hiddenLineCount > 0 ? [`\u2026 +${countOf(hiddenLineCount, "line")} (ctrl+o to expand)`] : [];
4072
4073
  return [...lines.map(sanitize), ...more].map((line) => style.grey(truncate(line, room)));
@@ -4187,7 +4188,7 @@ function tokenLine(blocks, cols) {
4187
4188
  }
4188
4189
 
4189
4190
  // src/tui/state.ts
4190
- var import_core21 = require("@ordewell/core");
4191
+ var import_core22 = require("@ordewell/core");
4191
4192
  var SKILL_IDS = ["tdd", "verify"];
4192
4193
  function noSkills() {
4193
4194
  return { tdd: false, verify: false };
@@ -4234,9 +4235,9 @@ function findTask(tasks, id) {
4234
4235
  function initialState(overrides = {}) {
4235
4236
  return {
4236
4237
  editor: emptyEditor(),
4237
- conversation: import_core21.EMPTY_CONVERSATION,
4238
+ conversation: import_core22.EMPTY_CONVERSATION,
4238
4239
  taskView: null,
4239
- turnGate: import_core21.NO_TURN,
4240
+ turnGate: import_core22.NO_TURN,
4240
4241
  detailAll: false,
4241
4242
  status: "idle",
4242
4243
  busyLabel: "",
@@ -4261,18 +4262,18 @@ function initialState(overrides = {}) {
4261
4262
  orchestratorModel: "",
4262
4263
  plannerProvider: "",
4263
4264
  plannerEffort: "",
4264
- maxParallel: import_core21.DEFAULT_MAX_PARALLEL,
4265
+ maxParallel: import_core22.DEFAULT_MAX_PARALLEL,
4265
4266
  configuredProviders: [],
4266
4267
  allowlist: {},
4267
4268
  autonomous: true,
4268
- runnerTransport: "terminal",
4269
+ runnerTransport: "structured",
4269
4270
  mouseCapture: true,
4270
4271
  selection: null,
4271
4272
  workspace: process.cwd(),
4272
4273
  handoff: null,
4273
4274
  overlay: null,
4274
4275
  pendingApprovals: [],
4275
- queuedPrompts: import_core21.EMPTY_HOLD,
4276
+ queuedPrompts: import_core22.EMPTY_HOLD,
4276
4277
  stopArmed: false,
4277
4278
  stopArmToken: 0,
4278
4279
  stopRequested: false,
@@ -4320,7 +4321,7 @@ function chatEditorRoomFor(state, active) {
4320
4321
  }
4321
4322
 
4322
4323
  // src/tui/layout.ts
4323
- var import_core22 = require("@ordewell/core");
4324
+ var import_core23 = require("@ordewell/core");
4324
4325
  var CHROME_ROWS = 4;
4325
4326
  function chatInputWrap(state) {
4326
4327
  const active = state.focus === "chat" && !state.overlay;
@@ -4359,7 +4360,7 @@ function footerHints(state) {
4359
4360
  "tab chat"
4360
4361
  ];
4361
4362
  }
4362
- const detailHint = (0, import_core22.hasHiddenDetail)(state.taskView ? state.taskView.view.blocks : state.conversation.blocks) ? [state.detailAll ? "ctrl-o collapse all" : "ctrl-o expand all"] : [];
4363
+ const detailHint = (0, import_core23.hasHiddenDetail)(state.taskView ? state.taskView.view.blocks : state.conversation.blocks) ? [state.detailAll ? "ctrl-o collapse all" : "ctrl-o expand all"] : [];
4363
4364
  return [
4364
4365
  "/help",
4365
4366
  "tab plan",
@@ -4464,9 +4465,9 @@ function chatScrollMax(state) {
4464
4465
  }
4465
4466
  function plannerSetup(state) {
4466
4467
  const provider = state.plannerProvider;
4467
- const registration = import_core22.ALL_PROVIDERS[provider];
4468
+ const registration = import_core23.ALL_PROVIDERS[provider];
4468
4469
  if (!registration) return { label: "not set", ready: false };
4469
- const runner = (0, import_core22.runnerForProvider)(provider);
4470
+ const runner = (0, import_core23.runnerForProvider)(provider);
4470
4471
  const ready = runner ? state.runners.some((r) => r.id === runner) : state.configuredProviders.includes(provider);
4471
4472
  return { label: registration.label, ready };
4472
4473
  }
@@ -4657,7 +4658,7 @@ function taskLines(state, row, index, cols) {
4657
4658
  const icon = paint(rawIcon);
4658
4659
  const kind = running ? style.yellow("RUN") : task.type === "user" ? style.yellow("MAN") : style.grey(" AI");
4659
4660
  const caret = selected ? style.cyan("\u276F") : " ";
4660
- const orderLabel = parent ? (0, import_core22.taskOrderLabel)(task, parent) : String(task.order).padStart(2);
4661
+ const orderLabel = parent ? (0, import_core23.taskOrderLabel)(task, parent) : String(task.order).padStart(2);
4661
4662
  const indent = parent ? " " : "";
4662
4663
  const head = `${indent}${caret} ${icon} ${orderLabel} ${kind} `;
4663
4664
  const titleRoom = Math.max(1, cols - width(head));
@@ -4687,16 +4688,16 @@ function taskLines(state, row, index, cols) {
4687
4688
  if (effort || mode) lines.push(style.grey(truncate(`${bodyPad}${[effort, mode].filter(Boolean).join(" \xB7 ")}`, cols)));
4688
4689
  if (task.isolation?.state === "conflict") {
4689
4690
  const where = task.isolation.conflictRepo && task.isolation.conflictRepo !== "." ? ` in ${task.isolation.conflictRepo}` : "";
4690
- const files = task.isolation.conflictFiles?.length ? ` (${(0, import_core22.capConflictFiles)(task.isolation.conflictFiles)})` : "";
4691
+ const files = task.isolation.conflictFiles?.length ? ` (${(0, import_core23.capConflictFiles)(task.isolation.conflictFiles)})` : "";
4691
4692
  lines.push(style.red(truncate(`${bodyPad}\u26A0 merge conflict${where}${files} \u2014 its work is kept on its own branch`, cols)));
4692
4693
  }
4693
4694
  if (task.isolation?.state === "repairing") {
4694
- const files = task.isolation.conflictFiles?.length ? ` in ${(0, import_core22.capConflictFiles)(task.isolation.conflictFiles)}` : "";
4695
+ const files = task.isolation.conflictFiles?.length ? ` in ${(0, import_core23.capConflictFiles)(task.isolation.conflictFiles)}` : "";
4695
4696
  const attempt = task.isolation.repair ? ` (attempt ${task.isolation.repair.attempt}/${task.isolation.repair.limit})` : "";
4696
4697
  lines.push(style.yellow(truncate(`${bodyPad}\u21BB repairing conflict${files}${attempt}`, cols)));
4697
4698
  }
4698
4699
  if (task.isolation?.state === "integrated" && task.isolation.repairedFiles?.length) {
4699
- lines.push(style.grey(truncate(`${bodyPad}\u21BB landed after repairing conflict in ${(0, import_core22.capConflictFiles)(task.isolation.repairedFiles)}`, cols)));
4700
+ lines.push(style.grey(truncate(`${bodyPad}\u21BB landed after repairing conflict in ${(0, import_core23.capConflictFiles)(task.isolation.repairedFiles)}`, cols)));
4700
4701
  }
4701
4702
  let editorLine;
4702
4703
  if (expanded) {
@@ -4710,7 +4711,7 @@ function taskLines(state, row, index, cols) {
4710
4711
  }
4711
4712
  }
4712
4713
  if (modeInfo?.autonomous) {
4713
- lines.push(...taskText("Autonomy", "Runs without permission prompts. Toggle with /auto.", cols, bodyPad));
4714
+ lines.push(...taskText("Autonomy", "Runs without permission prompts (Full auto). Change the level with /auto.", cols, bodyPad));
4714
4715
  }
4715
4716
  const editableSeed = task.prompt ?? task.description ?? task.title;
4716
4717
  if (task.description && task.description !== task.title && task.description !== editableSeed) {
@@ -4784,7 +4785,7 @@ function helpScrollMax(state) {
4784
4785
  }
4785
4786
 
4786
4787
  // src/tui/render.ts
4787
- var import_core23 = require("@ordewell/core");
4788
+ var import_core24 = require("@ordewell/core");
4788
4789
  function render(state) {
4789
4790
  const { rows, cols } = state;
4790
4791
  const rowsForBody = bodyRows(state);
@@ -4879,9 +4880,9 @@ function renderSkills(state, cols) {
4879
4880
  const badges = SKILL_IDS.map(
4880
4881
  (id) => state.skills[id] ? style.green(`\u25CF ${id}`) : style.grey(`\u25CB ${id}`)
4881
4882
  );
4882
- const auto = state.autonomous ? style.yellow("\u25CF auto") : style.grey("\u25CB auto");
4883
- const structured = state.runnerTransport === "structured" ? [style.yellow("\u25CF structured")] : [];
4884
- return truncate([...badges, auto, ...structured].join(" "), cols);
4883
+ const auto = state.autonomous ? style.yellow("\u25CF Full auto") : style.green("\u25CF Auto");
4884
+ const terminal = state.runnerTransport === "terminal" ? [style.yellow("\u25CF terminal")] : [];
4885
+ return truncate([...badges, auto, ...terminal].join(" "), cols);
4885
4886
  }
4886
4887
  function scrolledBackMark(state) {
4887
4888
  return state.focus !== "plan" && state.scroll > 0 ? "\u2191 scrolled back \xB7 pgdn for live" : "";
@@ -5049,7 +5050,7 @@ function renderHandoff(state, overlay, rows, cols) {
5049
5050
  return frame(`Diff \u2014 ${handoffBranch(handoff)}`, [...shown, "", style.grey(`${more}enter or esc goes back`)], rows, cols);
5050
5051
  }
5051
5052
  const landed = handoff.landed.length === 0 ? [style.grey("Nothing landed on it.")] : handoff.landed.map((task) => {
5052
- const repaired = task.repairedFiles?.length ? style.yellow(` \u2014 repaired (${(0, import_core23.capConflictFiles)(task.repairedFiles)})`) : "";
5053
+ const repaired = task.repairedFiles?.length ? style.yellow(` \u2014 repaired (${(0, import_core24.capConflictFiles)(task.repairedFiles)})`) : "";
5053
5054
  return ` ${style.green("\u2713")} #${task.order} ${task.title}${repaired}`;
5054
5055
  });
5055
5056
  const actions = handoffActions(handoff).map((action, index) => {
@@ -5139,7 +5140,7 @@ function frame(title, body, rows, cols) {
5139
5140
  }
5140
5141
 
5141
5142
  // src/tui/reducer.ts
5142
- var import_core29 = require("@ordewell/core");
5143
+ var import_core30 = require("@ordewell/core");
5143
5144
 
5144
5145
  // src/tui/reducers/shared.ts
5145
5146
  function step(state, effects = []) {
@@ -5188,10 +5189,10 @@ function handleApprovalKey(state, overlay, key) {
5188
5189
  }
5189
5190
 
5190
5191
  // src/tui/reducers/commands.ts
5191
- var import_core27 = require("@ordewell/core");
5192
+ var import_core28 = require("@ordewell/core");
5192
5193
 
5193
5194
  // src/tui/reducers/pickers.ts
5194
- var import_core24 = require("@ordewell/core");
5195
+ var import_core25 = require("@ordewell/core");
5195
5196
  var import_plan_utils2 = require("@ordewell/core/plan-utils");
5196
5197
  var DEFAULT_EFFORT = "__runner_default__";
5197
5198
  function picker(title, items, action, extra = {}) {
@@ -5210,11 +5211,11 @@ function modelDetail(m) {
5210
5211
  function providerErrorHint(state) {
5211
5212
  const failed = Object.keys(state.providerErrors ?? {});
5212
5213
  if (failed.length === 0) return void 0;
5213
- const names = failed.map((p) => import_core24.ALL_PROVIDERS[p]?.label ?? p);
5214
+ const names = failed.map((p) => import_core25.ALL_PROVIDERS[p]?.label ?? p);
5214
5215
  return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")} \u2014 showing working providers only.`;
5215
5216
  }
5216
5217
  function plannerModelItems(state) {
5217
- const runner = (0, import_core24.runnerForProvider)(state.plannerProvider);
5218
+ const runner = (0, import_core25.runnerForProvider)(state.plannerProvider);
5218
5219
  if (!runner) return state.orchestratorModels.map((m) => ({ id: m.id, label: m.label, detail: modelDetail(m) }));
5219
5220
  const items = state.models.filter((m) => m.runners?.includes(runner)).map((m) => ({
5220
5221
  id: m.id,
@@ -5228,7 +5229,7 @@ function plannerModelItems(state) {
5228
5229
  return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
5229
5230
  }
5230
5231
  function plannerEffortItems(state) {
5231
- const runner = (0, import_core24.runnerForProvider)(state.plannerProvider);
5232
+ const runner = (0, import_core25.runnerForProvider)(state.plannerProvider);
5232
5233
  if (!runner) {
5233
5234
  return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
5234
5235
  }
@@ -5337,21 +5338,21 @@ function pickerItemsFor(state, action) {
5337
5338
  }
5338
5339
  function plannerItems(state) {
5339
5340
  const installed = new Set(state.runners.map((r) => r.id));
5340
- const agents = import_core24.CLI_PROVIDERS.map((id) => {
5341
- const runner = (0, import_core24.runnerForProvider)(id);
5341
+ const agents = import_core25.CLI_PROVIDERS.map((id) => {
5342
+ const runner = (0, import_core25.runnerForProvider)(id);
5342
5343
  const ready = installed.has(runner);
5343
5344
  return {
5344
5345
  id,
5345
- label: import_core24.ALL_PROVIDERS[id].label,
5346
+ label: import_core25.ALL_PROVIDERS[id].label,
5346
5347
  detail: ready ? "coding agent \xB7 no API key needed" : "CLI not installed or not on PATH",
5347
5348
  selected: state.plannerProvider === id,
5348
5349
  disabled: !ready
5349
5350
  };
5350
5351
  });
5351
- const vendors = import_core24.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
5352
+ const vendors = import_core25.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
5352
5353
  id,
5353
- label: import_core24.ALL_PROVIDERS[id].label,
5354
- detail: import_core24.ALL_PROVIDERS[id].apiKeyEnvVar,
5354
+ label: import_core25.ALL_PROVIDERS[id].label,
5355
+ detail: import_core25.ALL_PROVIDERS[id].apiKeyEnvVar,
5355
5356
  selected: state.plannerProvider === id
5356
5357
  }));
5357
5358
  return vendors.length > 0 ? [...agents, ...vendors] : [...agents, { id: "", label: "No API providers configured", detail: "add a key with /key", disabled: true }];
@@ -5433,13 +5434,13 @@ function handleMouseSelect(state, key) {
5433
5434
  }
5434
5435
 
5435
5436
  // src/tui/reducers/taskView.ts
5436
- var import_core25 = require("@ordewell/core");
5437
+ var import_core26 = require("@ordewell/core");
5437
5438
  function openTaskView(state, sessionId, taskId) {
5438
5439
  const task = findTask(state.tasks, taskId);
5439
5440
  if (!task) return step(say(state, "system", `No task ${taskId} in this plan.`));
5440
5441
  const taskView = {
5441
5442
  taskId,
5442
- view: import_core25.EMPTY_TASK_LOG,
5443
+ view: import_core26.EMPTY_TASK_LOG,
5443
5444
  attempts: [],
5444
5445
  attempt: 0,
5445
5446
  pending: [],
@@ -5472,13 +5473,13 @@ function applyLive(tv, attempt, events) {
5472
5473
  }
5473
5474
  next = {
5474
5475
  ...next,
5475
- view: import_core25.EMPTY_TASK_LOG,
5476
+ view: import_core26.EMPTY_TASK_LOG,
5476
5477
  attempt,
5477
5478
  attempts: next.attempts.includes(attempt) ? next.attempts : [...next.attempts, attempt]
5478
5479
  };
5479
5480
  }
5480
5481
  if (attempt < next.attempt) return next;
5481
- return clampQueueIndex({ ...next, view: events.reduce(import_core25.reduceTaskLog, next.view) });
5482
+ return clampQueueIndex({ ...next, view: events.reduce(import_core26.reduceTaskLog, next.view) });
5482
5483
  }
5483
5484
  function taskLogAbandoned(state) {
5484
5485
  const tv = state.taskView;
@@ -5498,15 +5499,15 @@ function taskLogArrived(state, action) {
5498
5499
  function announceApprovals(state, action) {
5499
5500
  if (state.taskView?.taskId === action.taskId) return state;
5500
5501
  const task = findTask(state.tasks, action.taskId);
5501
- const spoken = action.events.reduce((s, event) => event.type === "approval_requested" ? say(s, "system", `\xB7 Task ${task?.order ?? "?"} waits for approval: ${(0, import_core25.runnerToolSubject)(event.tool, event.args)} \u2014 t on it to answer`) : s, state);
5502
- return state.taskView && spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
5502
+ const spoken = action.events.reduce((s, event) => event.type === "approval_requested" ? say(s, "system", `\xB7 Task ${task?.order ?? "?"} waits for approval: ${(0, import_core26.runnerToolSubject)(event.tool, event.args)} \u2014 t on it to answer`) : s, state);
5503
+ return spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
5503
5504
  }
5504
5505
  function taskLogLoaded(state, action) {
5505
5506
  const tv = state.taskView;
5506
5507
  if (!tv || tv.taskId !== action.taskId) return state;
5507
5508
  const loaded = action.attempt >= 1 ? {
5508
5509
  ...tv,
5509
- view: (0, import_core25.replayTaskLog)(action.events),
5510
+ view: (0, import_core26.replayTaskLog)(action.events),
5510
5511
  attempts: action.attempts ?? tv.attempts,
5511
5512
  attempt: action.attempt
5512
5513
  } : { ...tv, attempts: action.attempts ?? tv.attempts };
@@ -5868,9 +5869,9 @@ function commitTaskEdit(state, task, editor) {
5868
5869
  }
5869
5870
 
5870
5871
  // src/tui/reducers/turnQueue.ts
5871
- var import_core26 = require("@ordewell/core");
5872
+ var import_core27 = require("@ordewell/core");
5872
5873
  function drainQueue(state, settled) {
5873
- const next = (0, import_core26.drainNext)(state.queuedPrompts);
5874
+ const next = (0, import_core27.drainNext)(state.queuedPrompts);
5874
5875
  const ended = disarmStop({ ...settled, stopRequested: false });
5875
5876
  if (!next) return step(ended);
5876
5877
  const spoken = say(ended, "user", next.text);
@@ -5889,14 +5890,14 @@ function stopPlanning(state, sessionId) {
5889
5890
  return step(queueToEditor(cutTurn({ ...dismissed, stopArmed: false, stopRequested: true })), [{ type: "cancelPlanning", sessionId }]);
5890
5891
  }
5891
5892
  function queueToEditor(state) {
5892
- const all = (0, import_core26.unsendAll)(state.queuedPrompts);
5893
+ const all = (0, import_core27.unsendAll)(state.queuedPrompts);
5893
5894
  if (!all) return state;
5894
5895
  return { ...state, queuedPrompts: all.rest, editor: { ...state.editor, text: all.text, cursor: all.text.length } };
5895
5896
  }
5896
5897
  function unsendLatest(state) {
5897
- const latest = (0, import_core26.unsendLatest)(state.queuedPrompts);
5898
+ const latest = (0, import_core27.unsendLatest)(state.queuedPrompts);
5898
5899
  if (!latest) return step(state);
5899
- const text = (0, import_core26.aheadOfDraft)(latest.text, state.editor.text);
5900
+ const text = (0, import_core27.aheadOfDraft)(latest.text, state.editor.text);
5900
5901
  return step({
5901
5902
  ...state,
5902
5903
  queuedPrompts: latest.rest,
@@ -5913,7 +5914,7 @@ function plannerEscape(state) {
5913
5914
  }
5914
5915
 
5915
5916
  // src/tui/reducers/commands.ts
5916
- var KNOWN_PROVIDERS2 = Object.keys(import_core27.ALL_PROVIDERS);
5917
+ var KNOWN_PROVIDERS2 = Object.keys(import_core28.ALL_PROVIDERS);
5917
5918
  function runCommand(state, { name, args }) {
5918
5919
  if (!findCommand(name)) {
5919
5920
  return fail3(state, `Unknown command: /${name} \u2014 type /help to see what's available.`);
@@ -6069,8 +6070,8 @@ function newSession(state) {
6069
6070
  taskView: null,
6070
6071
  tasks: [],
6071
6072
  planApproved: false,
6072
- conversation: import_core27.EMPTY_CONVERSATION,
6073
- turnGate: import_core27.NO_TURN,
6073
+ conversation: import_core28.EMPTY_CONVERSATION,
6074
+ turnGate: import_core28.NO_TURN,
6074
6075
  selectedTask: 0,
6075
6076
  expandedTaskId: null,
6076
6077
  taskEditor: null,
@@ -6081,14 +6082,14 @@ function newSession(state) {
6081
6082
  // Prompts belong to the session that raised them; the old planner is gone
6082
6083
  // and its pending requests deny on their own timeout.
6083
6084
  pendingApprovals: [],
6084
- queuedPrompts: import_core27.EMPTY_HOLD,
6085
+ queuedPrompts: import_core28.EMPTY_HOLD,
6085
6086
  stopArmed: false,
6086
6087
  overlay: state.overlay?.kind === "approval" ? null : state.overlay
6087
6088
  }, closeEffects);
6088
6089
  }
6089
6090
  function setModel(state, args) {
6090
6091
  if (args[0] === "set" && args[1]) {
6091
- const runner = (0, import_core27.runnerForProvider)(state.plannerProvider);
6092
+ const runner = (0, import_core28.runnerForProvider)(state.plannerProvider);
6092
6093
  const known = runner ? modelsForRunner(state.models, runner) : [];
6093
6094
  if (runner && known.length > 0 && !known.some((m) => m.id === args[1])) {
6094
6095
  return fail3(state, `${args[1]} was not discovered for ${runner}.`);
@@ -6161,16 +6162,16 @@ function setKey(state, args) {
6161
6162
  }
6162
6163
  return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
6163
6164
  }
6164
- const items = import_core27.PROVIDER_PRIORITY.map((id) => ({
6165
+ const items = import_core28.PROVIDER_PRIORITY.map((id) => ({
6165
6166
  id,
6166
- label: import_core27.ALL_PROVIDERS[id].label,
6167
- detail: import_core27.ALL_PROVIDERS[id].apiKeyEnvVar,
6167
+ label: import_core28.ALL_PROVIDERS[id].label,
6168
+ detail: import_core28.ALL_PROVIDERS[id].apiKeyEnvVar,
6168
6169
  selected: state.configuredProviders.includes(id)
6169
6170
  }));
6170
6171
  return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
6171
6172
  }
6172
6173
  function keyPrompt(provider) {
6173
- const meta = import_core27.ALL_PROVIDERS[provider];
6174
+ const meta = import_core28.ALL_PROVIDERS[provider];
6174
6175
  return {
6175
6176
  kind: "prompt",
6176
6177
  title: `${meta.label} API key`,
@@ -6247,18 +6248,19 @@ function runners(state, args) {
6247
6248
  }
6248
6249
  function setMaxParallel(state, arg) {
6249
6250
  if (!arg) return step(say(state, "system", `Up to ${state.maxParallel} AI task${state.maxParallel === 1 ? "" : "s"} run at once \u2014 /parallel <n> changes it.`));
6250
- const limit = (0, import_core27.parseMaxParallel)(arg);
6251
+ const limit = (0, import_core28.parseMaxParallel)(arg);
6251
6252
  if (limit === null) return fail3(state, "Usage: /parallel [<number of tasks, 1 or more>]");
6252
6253
  return step({ ...state, maxParallel: limit }, [{ type: "setMaxParallel", limit }]);
6253
6254
  }
6254
6255
  function setAutonomous(state, arg) {
6255
- const enabled = resolveToggle(arg, state.autonomous);
6256
- if (enabled === null) return fail3(state, "Usage: /auto [on|off]");
6256
+ if (arg === void 0) return step(say(state, "system", `Autonomy level: ${(0, import_core28.autonomyLevelLabel)(state.autonomous)} \u2014 /auto full or /auto auto changes it for new plans.`));
6257
+ const enabled = (0, import_core28.parseAutonomyLevel)(arg);
6258
+ if (enabled === null) return fail3(state, "Usage: /auto [full|auto]");
6257
6259
  return step({ ...state, autonomous: enabled }, [{ type: "setAutonomous", enabled }]);
6258
6260
  }
6259
6261
  function setTransport(state, arg) {
6260
6262
  const value = arg?.toLowerCase() ?? (state.runnerTransport === "structured" ? "terminal" : "structured");
6261
- if (!(0, import_core27.isRunnerTransport)(value)) return fail3(state, "Usage: /transport [terminal|structured]");
6263
+ if (!(0, import_core28.isRunnerTransport)(value)) return fail3(state, "Usage: /transport [terminal|structured]");
6262
6264
  return step(state, [{ type: "setTransport", transport: value }]);
6263
6265
  }
6264
6266
  function setMouseCapture(state, arg) {
@@ -6508,7 +6510,7 @@ function choose(state, picker2, item) {
6508
6510
  }
6509
6511
 
6510
6512
  // src/tui/reducers/incoming.ts
6511
- var import_core28 = require("@ordewell/core");
6513
+ var import_core29 = require("@ordewell/core");
6512
6514
  var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
6513
6515
  var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
6514
6516
  var asRecord = (value) => typeof value === "object" && value !== null ? value : {};
@@ -6539,7 +6541,7 @@ function toTaskView2(t, index) {
6539
6541
  prompt: typeof t.prompt === "string" ? sanitize(t.prompt) : void 0,
6540
6542
  type: t.type === "user" ? "user" : "ai",
6541
6543
  status: planLabel(t.status, "pending"),
6542
- awaitingReason: t.status === "awaiting_user" && (0, import_core28.isAwaitingReason)(t.awaitingReason) ? t.awaitingReason : void 0,
6544
+ awaitingReason: t.status === "awaiting_user" && (0, import_core29.isAwaitingReason)(t.awaitingReason) ? t.awaitingReason : void 0,
6543
6545
  dependencies: Array.isArray(t.dependencies) ? t.dependencies.map((id) => planLabel(id)) : [],
6544
6546
  assignedRunner: typeof t.assignedRunner === "string" ? planLabel(t.assignedRunner) : void 0,
6545
6547
  taskMode: typeof t.taskMode === "string" ? planLabel(t.taskMode) : void 0,
@@ -6570,7 +6572,7 @@ function applySettings(state, settings) {
6570
6572
  plannerProvider: typeof settings.aiProvider === "string" ? settings.aiProvider : state.plannerProvider,
6571
6573
  plannerEffort: typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : state.plannerEffort,
6572
6574
  maxParallel: typeof settings.maxParallel === "number" ? settings.maxParallel : state.maxParallel,
6573
- runnerTransport: (0, import_core28.isRunnerTransport)(settings.runnerTransport) ? settings.runnerTransport : state.runnerTransport,
6575
+ runnerTransport: (0, import_core29.isRunnerTransport)(settings.runnerTransport) ? settings.runnerTransport : state.runnerTransport,
6574
6576
  allowlist: settings.modelAllowlist && typeof settings.modelAllowlist === "object" ? settings.modelAllowlist : state.allowlist
6575
6577
  };
6576
6578
  }
@@ -6612,7 +6614,18 @@ function followSession(state, message) {
6612
6614
  }
6613
6615
 
6614
6616
  // src/tui/reducer.ts
6617
+ function holdReadingPlace(before, after) {
6618
+ if (before.scroll <= 0 || after.scroll !== before.scroll) return after;
6619
+ if (before.taskView?.taskId !== after.taskView?.taskId) return after;
6620
+ const grown = chatScrollMax(after) - chatScrollMax(before);
6621
+ return grown > 0 ? { ...after, scroll: clamp(after.scroll + grown, chatScrollMax(after)) } : after;
6622
+ }
6615
6623
  function reduce(state, action) {
6624
+ const result = reduceAction(state, action);
6625
+ if (action.type !== "sessionMessage" && action.type !== "taskLog") return result;
6626
+ return { ...result, state: holdReadingPlace(state, result.state) };
6627
+ }
6628
+ function reduceAction(state, action) {
6616
6629
  switch (action.type) {
6617
6630
  case "key":
6618
6631
  return handleKey2(state, action.key);
@@ -6630,13 +6643,13 @@ function reduce(state, action) {
6630
6643
  goal: "",
6631
6644
  taskView: null,
6632
6645
  pendingApprovals: [],
6633
- queuedPrompts: import_core29.EMPTY_HOLD,
6646
+ queuedPrompts: import_core30.EMPTY_HOLD,
6634
6647
  stopArmed: false,
6635
6648
  overlay: state.overlay?.kind === "approval" ? null : state.overlay
6636
6649
  });
6637
6650
  case "chatRestored":
6638
6651
  if (stale(state, action.sessionId)) return step(state);
6639
- return step({ ...state, conversation: (0, import_core29.fromTranscript)(action.history, action.researchLog, action.plannerUsage), scroll: 0 });
6652
+ return step({ ...state, conversation: (0, import_core30.fromTranscript)(action.history, action.researchLog, action.plannerUsage), scroll: 0 });
6640
6653
  case "planUpdated": {
6641
6654
  if (stale(state, action.sessionId)) return step(state);
6642
6655
  const isolation = isolationOfPlan(action.plan);
@@ -6671,6 +6684,9 @@ function reduce(state, action) {
6671
6684
  case "taskLog":
6672
6685
  if (stale(state, action.sessionId)) return step(state);
6673
6686
  return step(announceApprovals(taskLogArrived(state, action), action));
6687
+ case "taskViewRequested":
6688
+ if (stale(state, action.sessionId)) return step(state);
6689
+ return openTaskView(state, action.sessionId, action.taskId);
6674
6690
  case "taskLogLoaded":
6675
6691
  if (stale(state, action.sessionId)) return step(state);
6676
6692
  return step(taskLogLoaded(state, action));
@@ -6682,7 +6698,7 @@ function reduce(state, action) {
6682
6698
  const existing = findTask(state.tasks, action.taskId);
6683
6699
  if (existing && isTaskRunning(existing)) return step(state);
6684
6700
  const tasks = state.tasks.map((t) => t.id === action.taskId && !isTaskRunning(t) ? { ...t, status: "in_progress" } : t);
6685
- const spoken = say(state, "system", (0, import_core29.taskStartedNotice)(action.title, action.runner));
6701
+ const spoken = say(state, "system", (0, import_core30.taskStartedNotice)(action.title, action.runner));
6686
6702
  return step({ ...spoken, tasks, status: "executing", busyLabel: runLabel(tasks) });
6687
6703
  }
6688
6704
  case "taskStatus": {
@@ -6786,7 +6802,7 @@ function reduce(state, action) {
6786
6802
  busyLabel: "",
6787
6803
  planApproved: false,
6788
6804
  pendingApprovals: [],
6789
- queuedPrompts: import_core29.EMPTY_HOLD,
6805
+ queuedPrompts: import_core30.EMPTY_HOLD,
6790
6806
  stopArmed: false,
6791
6807
  overlay: state.overlay?.kind === "approval" ? null : state.overlay
6792
6808
  });
@@ -6915,7 +6931,7 @@ function submit(state) {
6915
6931
  return step(cleared, [{ type: "sendTaskMessage", sessionId: state.sessionId, taskId, text }]);
6916
6932
  }
6917
6933
  if (plannerInFlight(state)) {
6918
- return step({ ...cleared, queuedPrompts: (0, import_core29.holdPrompt)(state.queuedPrompts, text) });
6934
+ return step({ ...cleared, queuedPrompts: (0, import_core30.holdPrompt)(state.queuedPrompts, text) });
6919
6935
  }
6920
6936
  const spoken = say(cleared, "user", text);
6921
6937
  const effect = state.sessionId ? { type: "sendMessage", sessionId: state.sessionId, message: text } : { type: "startConversation", goal: text };
@@ -6975,10 +6991,10 @@ function createApp(deps) {
6975
6991
 
6976
6992
  // src/tui/effects.ts
6977
6993
  var import_child_process4 = require("child_process");
6978
- var import_core31 = require("@ordewell/core");
6994
+ var import_core32 = require("@ordewell/core");
6979
6995
 
6980
6996
  // src/tui/inbound.ts
6981
- var import_core30 = require("@ordewell/core");
6997
+ var import_core31 = require("@ordewell/core");
6982
6998
  var CONVERSATION_TYPES = /* @__PURE__ */ new Set([
6983
6999
  "planner_message",
6984
7000
  "planner_turn_started",
@@ -7193,7 +7209,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
7193
7209
  idleSince: task.idleSince ?? null,
7194
7210
  isolation: task.isolation,
7195
7211
  transport: task.transport,
7196
- awaitingReason: (0, import_core30.isAwaitingReason)(task.awaitingReason) ? task.awaitingReason : void 0,
7212
+ awaitingReason: (0, import_core31.isAwaitingReason)(task.awaitingReason) ? task.awaitingReason : void 0,
7197
7213
  ...task.continuable ? { continuable: true } : {},
7198
7214
  awaitingApproval: typeof task.awaitingApproval === "number" && task.awaitingApproval > 0 ? task.awaitingApproval : void 0
7199
7215
  };
@@ -7207,7 +7223,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
7207
7223
  // The extension shows these in its checkpoint panel; here they are
7208
7224
  // transcript lines, which is the TUI's equivalent surface.
7209
7225
  case "checkpoint":
7210
- dispatch({ type: "notice", message: `\xB7 Checkpoint \u2014 ${event.taskTitle}: ${(0, import_core30.truncateCheckpointSummary)(event.summary)}` });
7226
+ dispatch({ type: "notice", message: `\xB7 Checkpoint \u2014 ${event.taskTitle}: ${(0, import_core31.truncateCheckpointSummary)(event.summary)}` });
7211
7227
  return;
7212
7228
  case "review_needed":
7213
7229
  dispatch({ type: "notice", message: "Plan needs your sign-off \u2014 /approve to continue." });
@@ -7405,7 +7421,14 @@ async function perform(effect, deps) {
7405
7421
  }
7406
7422
  return;
7407
7423
  }
7424
+ // A task with a saved log ran structured, whatever its `transport` says
7425
+ // after a reload; it has no tmux window, so open the log rather than fail.
7408
7426
  case "openTaskTerminal": {
7427
+ const saved = await api.getTaskLogAttempts(effect.sessionId, effect.taskId, workspace).catch(() => []);
7428
+ if (saved.length > 0) {
7429
+ dispatch({ type: "taskViewRequested", sessionId: effect.sessionId, taskId: effect.taskId });
7430
+ return;
7431
+ }
7409
7432
  const result = await deps.openTerminal(effect.sessionId, effect.taskId);
7410
7433
  dispatch(result.ok ? { type: "notice", message: result.message } : { type: "failed", message: result.message });
7411
7434
  return;
@@ -7460,7 +7483,7 @@ async function perform(effect, deps) {
7460
7483
  case "setTransport": {
7461
7484
  const result = await api.sendCommand("transport", { action: effect.transport });
7462
7485
  if (result.settings) dispatch({ type: "settingsLoaded", settings: result.settings });
7463
- dispatch({ type: "notice", message: `Runner transport is ${effect.transport} (experimental) \u2014 it applies from the next run.` });
7486
+ dispatch({ type: "notice", message: `Runner transport is ${effect.transport} \u2014 it applies from the next run.` });
7464
7487
  return;
7465
7488
  }
7466
7489
  case "setModel":
@@ -7470,7 +7493,7 @@ async function perform(effect, deps) {
7470
7493
  dispatch({ type: "notice", message: `Orchestrator model set to ${effect.modelId}.` });
7471
7494
  return;
7472
7495
  case "setPlanner": {
7473
- const meta = import_core31.ALL_PROVIDERS[effect.provider];
7496
+ const meta = import_core32.ALL_PROVIDERS[effect.provider];
7474
7497
  if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
7475
7498
  const env = { AI_PROVIDER: effect.provider };
7476
7499
  const settings = await api.updateSettings({ env });
@@ -7482,7 +7505,7 @@ async function perform(effect, deps) {
7482
7505
  });
7483
7506
  dispatch({
7484
7507
  type: "notice",
7485
- message: `${(0, import_core31.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
7508
+ message: `${(0, import_core32.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
7486
7509
  });
7487
7510
  await loadModels(deps);
7488
7511
  return;
@@ -7494,7 +7517,7 @@ async function perform(effect, deps) {
7494
7517
  dispatch({ type: "notice", message: `Planner effort set to ${effect.effort || "the runner default"}.` });
7495
7518
  return;
7496
7519
  case "setApiKey": {
7497
- const meta = import_core31.ALL_PROVIDERS[effect.provider];
7520
+ const meta = import_core32.ALL_PROVIDERS[effect.provider];
7498
7521
  if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
7499
7522
  await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
7500
7523
  deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
@@ -7532,7 +7555,7 @@ async function perform(effect, deps) {
7532
7555
  return;
7533
7556
  case "setAutonomous":
7534
7557
  deps.setEnvVar("ORDEWELL_AUTONOMOUS_MODE", String(effect.enabled));
7535
- dispatch({ type: "notice", message: `Autonomous mode ${effect.enabled ? "on" : "off"}.` });
7558
+ dispatch({ type: "notice", message: `Autonomy level: ${(0, import_core32.autonomyLevelLabel)(effect.enabled)} for new plans.` });
7536
7559
  return;
7537
7560
  case "setMouseCapture":
7538
7561
  deps.setMouseCapture(effect.enabled);
@@ -7665,7 +7688,7 @@ function osc52(text) {
7665
7688
  function copySelection(deps, text) {
7666
7689
  if (!text) return;
7667
7690
  const lines = text.split("\n").length;
7668
- const command = (0, import_core31.clipboardCopyCommand)(deps.hasBin);
7691
+ const command = (0, import_core32.clipboardCopyCommand)(deps.hasBin);
7669
7692
  if (command) {
7670
7693
  try {
7671
7694
  (deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
@@ -8055,7 +8078,7 @@ async function handleTui(subArgs) {
8055
8078
  }
8056
8079
  const workspace = flag(subArgs, "--workspace") || process.cwd();
8057
8080
  try {
8058
- (0, import_core32.assertWorkspaceExists)(workspace);
8081
+ (0, import_core33.assertWorkspaceExists)(workspace);
8059
8082
  } catch {
8060
8083
  console.error(
8061
8084
  `Workspace "${workspace}" does not exist.
@@ -8064,7 +8087,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
8064
8087
  );
8065
8088
  process.exit(1);
8066
8089
  }
8067
- registerSkillCommands((0, import_core32.createSkillsService)(workspace).listSkills());
8090
+ registerSkillCommands((0, import_core33.createSkillsService)(workspace).listSkills());
8068
8091
  const explicitPort = flag(subArgs, "--port") || process.env.ORDEWELL_PORT;
8069
8092
  const targetPort = explicitPort ? resolvePort(subArgs) : await findFreePort();
8070
8093
  const { port, owned: ownedAtLaunch } = await ensureDaemonOwned(targetPort, { detached: false });
@@ -8081,7 +8104,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
8081
8104
  workspace,
8082
8105
  port,
8083
8106
  dispatch: (action) => app.dispatch(action),
8084
- newSessionId: () => (0, import_core32.mintSessionId)(),
8107
+ newSessionId: () => (0, import_core33.mintSessionId)(),
8085
8108
  setEnvVar: (key, value) => {
8086
8109
  writeEnvVar(findEnvFile(), key, value);
8087
8110
  process.env[key] = value;
@@ -8204,7 +8227,7 @@ function cliVersion() {
8204
8227
  // src/main.ts
8205
8228
  var TUI_FLAGS = /* @__PURE__ */ new Set(["--workspace", "--port"]);
8206
8229
  async function main() {
8207
- (0, import_core33.migrateOldConfigDir)();
8230
+ (0, import_core34.migrateOldConfigDir)();
8208
8231
  loadEnvFile();
8209
8232
  const argv = process.argv.slice(2);
8210
8233
  if (TUI_FLAGS.has(argv[0] ?? "")) argv.unshift("tui");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.5.6",
3
+ "version": "0.6.0",
4
4
  "description": "Ordewell CLI and TUI — plan, run and verify coding-agent tasks from the terminal.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Alessandro Costanzo Ciano",
@@ -49,8 +49,8 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@ordewell/core": "^0.5.6",
53
- "@ordewell/web": "^0.5.6",
52
+ "@ordewell/core": "^0.6.0",
53
+ "@ordewell/web": "^0.6.0",
54
54
  "string-width": "^7.2.0",
55
55
  "ws": "^8.18.0"
56
56
  },