@ordewell/cli 0.10.2 → 0.10.4
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/README.md +1 -0
- package/dist/main.js +697 -497
- package/package.json +3 -3
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
|
|
27
|
+
var import_core52 = require("@ordewell/core");
|
|
28
28
|
|
|
29
29
|
// src/utils/env.ts
|
|
30
30
|
var import_fs = require("fs");
|
|
@@ -127,7 +127,7 @@ function runnerNamesOr() {
|
|
|
127
127
|
// src/help.ts
|
|
128
128
|
function printHelp() {
|
|
129
129
|
console.log(`
|
|
130
|
-
Ordewell
|
|
130
|
+
Ordewell: task orchestration for coding agents
|
|
131
131
|
|
|
132
132
|
Run "ordewell" with no arguments for the TUI. Every command below has a TUI
|
|
133
133
|
slash command of the same name, where each one opens a picker instead of taking
|
|
@@ -135,7 +135,7 @@ an id.
|
|
|
135
135
|
|
|
136
136
|
Planning:
|
|
137
137
|
ordewell Full-screen terminal UI (everything the VS Code extension does)
|
|
138
|
-
ordewell tui
|
|
138
|
+
ordewell tui Explicit TUI command for scripts and aliases
|
|
139
139
|
ordewell plan --goal "text" Plan a goal conversationally
|
|
140
140
|
ordewell run Execute the last generated plan (streams status)
|
|
141
141
|
ordewell approve Sign off a plan paused for review, and continue it
|
|
@@ -170,7 +170,7 @@ Per-task assignment (omit the value to list the options):
|
|
|
170
170
|
ordewell task-ops <id> [on|off] Make a task an ops task (runs in your checkout once its dependencies are merged) or a change task
|
|
171
171
|
|
|
172
172
|
Planner, models and runners:
|
|
173
|
-
ordewell planner [<provider>] Choose who plans
|
|
173
|
+
ordewell planner [<provider>] Choose who plans: an API provider or a coding agent (no API key)
|
|
174
174
|
ordewell model [set <id>] Show or set the planner model (applies without a restart)
|
|
175
175
|
ordewell planner-effort [lvl] Thinking effort for a coding-agent planner
|
|
176
176
|
ordewell key [set <p> <key>] Show which providers have a key, or store one
|
|
@@ -249,17 +249,20 @@ Global options:
|
|
|
249
249
|
--session-id <id> Session to act on (default: last planned session)
|
|
250
250
|
|
|
251
251
|
Quick Start:
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
Run "ordewell", then use /planner to choose a coding agent,
|
|
253
|
+
or /key to choose an API provider and add its key.
|
|
254
|
+
|
|
255
|
+
To plan from the command line after setup:
|
|
256
|
+
ordewell plan --goal "Your task description"
|
|
254
257
|
|
|
255
258
|
No API key? Plan with a coding agent you already pay for:
|
|
256
259
|
AI_PROVIDER=<runner> ordewell plan --goal "Your task description"
|
|
257
260
|
|
|
258
261
|
Environment:
|
|
259
|
-
OPENROUTER_API_KEY OpenRouter API key
|
|
260
|
-
GEMINI_API_KEY Google Gemini API key
|
|
261
|
-
AI_PROVIDER
|
|
262
|
-
${runnerIdList()}
|
|
262
|
+
OPENROUTER_API_KEY OpenRouter API key
|
|
263
|
+
GEMINI_API_KEY Google Gemini API key
|
|
264
|
+
AI_PROVIDER Provider ID (auto-detected from keys); list with "ordewell planner"
|
|
265
|
+
${runnerIdList()}: plan with that CLI,
|
|
263
266
|
no API key (uses its own subscription)
|
|
264
267
|
ORDEWELL_PLANNER_EFFORT Thinking effort for a coding-agent planner
|
|
265
268
|
ORCHESTRATOR_MODEL Default: deepseek/deepseek-v4-flash
|
|
@@ -433,7 +436,7 @@ function isConnectionRefused(err) {
|
|
|
433
436
|
return message.includes("ECONNREFUSED");
|
|
434
437
|
}
|
|
435
438
|
function describeConnectionRefused(port) {
|
|
436
|
-
return `No Ordewell server is listening on port ${port}. It may have been stopped, or it may have crashed
|
|
439
|
+
return `No Ordewell server is listening on port ${port}. It may have been stopped, or it may have crashed: check ${(0, import_path2.join)(CONFIG_DIR, "server.log")}. Start one with \`ordewell web --daemon\`.`;
|
|
437
440
|
}
|
|
438
441
|
function findFreePort() {
|
|
439
442
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -464,14 +467,16 @@ var import_ws = __toESM(require("ws"));
|
|
|
464
467
|
var import_core4 = require("@ordewell/core");
|
|
465
468
|
var DEFAULT_HTTP_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
466
469
|
var DaemonError = class extends Error {
|
|
467
|
-
constructor(message, status, code) {
|
|
470
|
+
constructor(message, status, code, plannerNotReady) {
|
|
468
471
|
super(message);
|
|
469
472
|
this.status = status;
|
|
470
473
|
this.code = code;
|
|
474
|
+
this.plannerNotReady = plannerNotReady;
|
|
471
475
|
this.name = "DaemonError";
|
|
472
476
|
}
|
|
473
477
|
status;
|
|
474
478
|
code;
|
|
479
|
+
plannerNotReady;
|
|
475
480
|
};
|
|
476
481
|
var WorkspaceInitNeededError = class extends DaemonError {
|
|
477
482
|
constructor(message, workspace) {
|
|
@@ -483,9 +488,9 @@ var WorkspaceInitNeededError = class extends DaemonError {
|
|
|
483
488
|
};
|
|
484
489
|
var SESSION_PATH = /^\/api\/(?:plans|sessions)\/([^/?]+)/;
|
|
485
490
|
function daemonError(status, data, fallback) {
|
|
486
|
-
const { error, code, workspace } = typeof data === "object" && data !== null ? data : {};
|
|
491
|
+
const { error, code, workspace, plannerNotReady } = typeof data === "object" && data !== null ? data : {};
|
|
487
492
|
if (code === "workspace_not_a_project") return new WorkspaceInitNeededError(error || "Not a project directory", workspace ?? "");
|
|
488
|
-
return new DaemonError(error || fallback, status, code);
|
|
493
|
+
return new DaemonError(error || fallback, status, code, plannerNotReady);
|
|
489
494
|
}
|
|
490
495
|
var ApiClient = class _ApiClient {
|
|
491
496
|
/** Where a CLI invocation's sessions live, when a client is built without one: `--workspace`, else the cwd. */
|
|
@@ -628,7 +633,7 @@ var ApiClient = class _ApiClient {
|
|
|
628
633
|
markTaskComplete(sessionId, taskId) {
|
|
629
634
|
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/complete`, "Mark complete failed");
|
|
630
635
|
}
|
|
631
|
-
/** Skip: end the task without landing its work, so what depends on it goes ahead; never a mark complete. */
|
|
636
|
+
/** Skip: end the task without landing its work, so what depends on it goes ahead once the work it waited on is done; never a mark complete. */
|
|
632
637
|
skipTask(sessionId, taskId, reason) {
|
|
633
638
|
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/skip`, "Skip failed", reason ? { reason } : {});
|
|
634
639
|
}
|
|
@@ -728,7 +733,7 @@ var ApiClient = class _ApiClient {
|
|
|
728
733
|
compactConversation(sessionId) {
|
|
729
734
|
return this.call("POST", `/api/plans/${sessionId}/conversation/compact`, "Compaction failed");
|
|
730
735
|
}
|
|
731
|
-
/** The
|
|
736
|
+
/** The ancestors a task waits on at its merge gate (ADR-0020); empty when nothing gates it. */
|
|
732
737
|
async getMergeGate(sessionId, taskId) {
|
|
733
738
|
const res = await this.call("GET", `/api/plans/${sessionId}/tasks/${taskId}/merge-gate`, "Could not read the merge gate");
|
|
734
739
|
return res.mergeGate ?? [];
|
|
@@ -979,7 +984,7 @@ var ApiClient = class _ApiClient {
|
|
|
979
984
|
};
|
|
980
985
|
|
|
981
986
|
// src/commands/plan.ts
|
|
982
|
-
var
|
|
987
|
+
var import_readline2 = require("readline");
|
|
983
988
|
|
|
984
989
|
// src/utils/args.ts
|
|
985
990
|
function flag(args, name) {
|
|
@@ -1140,7 +1145,7 @@ ${describeApproval(event)}
|
|
|
1140
1145
|
deps.write(" Approved automatically (--yes).\n");
|
|
1141
1146
|
} else if (!interactive) {
|
|
1142
1147
|
granted = false;
|
|
1143
|
-
deps.write(" Denied: no interactive terminal. Re-run with --yes, or pre-approve via ORDEWELL_APPROVAL_ALLOW.\n");
|
|
1148
|
+
deps.write(event.kind === "task_start" ? " Not started: there is no terminal here to ask you. Start it yourself with `ordewell run`, or Execute plan in the TUI or VS Code.\n" : " Denied: no interactive terminal. Re-run with --yes, or pre-approve via ORDEWELL_APPROVAL_ALLOW.\n");
|
|
1144
1149
|
} else {
|
|
1145
1150
|
const answer = (await deps.ask(" Allow? [y/N] ")).trim().toLowerCase();
|
|
1146
1151
|
granted = answer === "y" || answer === "yes";
|
|
@@ -1148,7 +1153,7 @@ ${describeApproval(event)}
|
|
|
1148
1153
|
try {
|
|
1149
1154
|
await deps.respond(sessionId, id, granted);
|
|
1150
1155
|
} catch {
|
|
1151
|
-
deps.write(" (could not deliver the answer
|
|
1156
|
+
deps.write(" (could not deliver the answer: the planner will treat it as denied)\n");
|
|
1152
1157
|
}
|
|
1153
1158
|
}
|
|
1154
1159
|
|
|
@@ -1215,7 +1220,118 @@ function createStepLog(options = {}) {
|
|
|
1215
1220
|
}
|
|
1216
1221
|
|
|
1217
1222
|
// src/commands/plan.ts
|
|
1223
|
+
var import_core9 = require("@ordewell/core");
|
|
1224
|
+
|
|
1225
|
+
// src/commands/shared.ts
|
|
1226
|
+
var import_readline = require("readline");
|
|
1227
|
+
|
|
1228
|
+
// src/catalog.ts
|
|
1229
|
+
function normalizeCatalog(result) {
|
|
1230
|
+
const runnersByModel = /* @__PURE__ */ new Map();
|
|
1231
|
+
for (const [runner, entries] of Object.entries(result.modelsByRunner ?? {})) {
|
|
1232
|
+
for (const entry of entries ?? []) {
|
|
1233
|
+
const id = String(entry.modelId);
|
|
1234
|
+
runnersByModel.set(id, [.../* @__PURE__ */ new Set([...runnersByModel.get(id) ?? [], runner])]);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
const models = (result.models ?? []).map((m) => {
|
|
1238
|
+
const id = String(m.modelId);
|
|
1239
|
+
return {
|
|
1240
|
+
id,
|
|
1241
|
+
label: String(m.modelLabel ?? id),
|
|
1242
|
+
provider: String(m.runnerProviderLabel ?? m.runnerProvider ?? ""),
|
|
1243
|
+
variants: Array.isArray(m.variants) ? m.variants.map((v) => ({ id: String(v.id), label: String(v.label ?? v.id) })) : [],
|
|
1244
|
+
runners: runnersByModel.get(id) ?? []
|
|
1245
|
+
};
|
|
1246
|
+
});
|
|
1247
|
+
const orchestratorModels = (result.orchestratorModels ?? []).map((m) => ({
|
|
1248
|
+
id: String(m.id),
|
|
1249
|
+
label: String(m.label ?? m.id),
|
|
1250
|
+
provider: String(m.provider ?? ""),
|
|
1251
|
+
pricing: m.pricing ? `$${m.pricing}/MTok` : void 0
|
|
1252
|
+
}));
|
|
1253
|
+
return {
|
|
1254
|
+
models,
|
|
1255
|
+
orchestratorModels,
|
|
1256
|
+
providers: result.providers ?? [],
|
|
1257
|
+
providerErrors: result.providerErrors ?? {},
|
|
1258
|
+
modesByRunner: Object.fromEntries(
|
|
1259
|
+
Object.entries(result.modesByRunner ?? {}).map(([runner, modes]) => [
|
|
1260
|
+
runner,
|
|
1261
|
+
(modes ?? []).map((m) => ({
|
|
1262
|
+
id: String(m.id),
|
|
1263
|
+
label: String(m.label ?? m.id),
|
|
1264
|
+
description: m.description ? String(m.description) : void 0,
|
|
1265
|
+
autonomous: m.autonomous ? true : void 0,
|
|
1266
|
+
unavailable: m.unavailable ? String(m.unavailable) : void 0
|
|
1267
|
+
}))
|
|
1268
|
+
])
|
|
1269
|
+
)
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// src/commands/shared.ts
|
|
1218
1274
|
var import_core8 = require("@ordewell/core");
|
|
1275
|
+
async function connect(subArgs, injected) {
|
|
1276
|
+
return injected ?? new ApiClient(await ensureDaemon(resolvePort(subArgs)));
|
|
1277
|
+
}
|
|
1278
|
+
async function fetchCatalog(api) {
|
|
1279
|
+
return normalizeCatalog(await api.getModels());
|
|
1280
|
+
}
|
|
1281
|
+
function fail(...lines) {
|
|
1282
|
+
for (const line of lines) console.error(line);
|
|
1283
|
+
process.exit(1);
|
|
1284
|
+
}
|
|
1285
|
+
function headlessPlannerRefusal(err) {
|
|
1286
|
+
return err instanceof DaemonError && err.plannerNotReady ? (0, import_core8.describePlannerNotReady)(err.plannerNotReady, import_core8.HEADLESS_CLI_COMMANDS) : null;
|
|
1287
|
+
}
|
|
1288
|
+
async function askYesNo(question) {
|
|
1289
|
+
if (!process.stdin.isTTY) return false;
|
|
1290
|
+
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stderr });
|
|
1291
|
+
try {
|
|
1292
|
+
const answer = await new Promise((resolve4) => rl.question(`${question} [y/N] `, resolve4));
|
|
1293
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
1294
|
+
} finally {
|
|
1295
|
+
rl.close();
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
function writeEnv(env) {
|
|
1299
|
+
const envFile = findEnvFile();
|
|
1300
|
+
for (const [key, value] of Object.entries(env)) {
|
|
1301
|
+
writeEnvVar(envFile, key, value);
|
|
1302
|
+
process.env[key] = value;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
async function persistEnv(api, env) {
|
|
1306
|
+
const settings = await api.updateSettings({ env });
|
|
1307
|
+
writeEnv(env);
|
|
1308
|
+
return settings;
|
|
1309
|
+
}
|
|
1310
|
+
function toTaskView(task) {
|
|
1311
|
+
return {
|
|
1312
|
+
id: task.id,
|
|
1313
|
+
order: task.order,
|
|
1314
|
+
title: task.title,
|
|
1315
|
+
description: task.description,
|
|
1316
|
+
prompt: task.prompt ?? void 0,
|
|
1317
|
+
type: task.type === "user" ? "user" : "ai",
|
|
1318
|
+
status: task.status ?? "pending",
|
|
1319
|
+
dependencies: task.dependencies ?? [],
|
|
1320
|
+
assignedRunner: task.assignedRunner || void 0,
|
|
1321
|
+
taskMode: task.taskMode || void 0,
|
|
1322
|
+
assignedModel: task.assignedModel ?? void 0,
|
|
1323
|
+
subtasks: [...task.subtasks ?? []].sort((a, b) => a.order - b.order).map(toTaskView),
|
|
1324
|
+
...task.ops ? { ops: true } : {},
|
|
1325
|
+
...task.skills?.length ? { skills: [...task.skills] } : {},
|
|
1326
|
+
...task.addedBy ? { addedBy: task.addedBy } : {}
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function taskViews(plan) {
|
|
1330
|
+
const tasks = allTasksOf(plan);
|
|
1331
|
+
return [...tasks].sort((a, b) => a.order - b.order).map(toTaskView);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// src/commands/plan.ts
|
|
1219
1335
|
function planTasks(plan) {
|
|
1220
1336
|
return plan ? allTasksOf(plan) : [];
|
|
1221
1337
|
}
|
|
@@ -1237,8 +1353,16 @@ function commitNotes(plan) {
|
|
|
1237
1353
|
const marker2 = history.map((e) => e.kind).lastIndexOf("plan_generated");
|
|
1238
1354
|
return marker2 < 0 ? [] : history.slice(marker2 + 1).filter((e) => e.kind === "system").map((e) => e.content);
|
|
1239
1355
|
}
|
|
1356
|
+
async function nextStep(api, plan) {
|
|
1357
|
+
const approved = "approved" in plan && plan.approved === true;
|
|
1358
|
+
const moving = planTasks(plan).some((t) => t.status !== void 0 && t.status !== "pending" && t.status !== "approved");
|
|
1359
|
+
if (!approved && !moving) return `Run 'ordewell run' to execute, 'ordewell status' to inspect, or 'ordewell tui' for the full UI.`;
|
|
1360
|
+
const follow2 = `Follow it with 'ordewell status', or 'ordewell tui' for the full UI.`;
|
|
1361
|
+
const autorun = await api.getSettings().then((settings) => settings.run?.autorun === true, () => false);
|
|
1362
|
+
return autorun ? `Autorun started the plan. ${follow2}` : `The plan is running. ${follow2}`;
|
|
1363
|
+
}
|
|
1240
1364
|
function stdinReader(input = process.stdin) {
|
|
1241
|
-
const rl = (0,
|
|
1365
|
+
const rl = (0, import_readline2.createInterface)({ input, output: process.stderr });
|
|
1242
1366
|
const buffered = [];
|
|
1243
1367
|
let waiting = null;
|
|
1244
1368
|
let ended = false;
|
|
@@ -1293,35 +1417,36 @@ function printPlan(plan, sessionId, runners2, models) {
|
|
|
1293
1417
|
return rp ? ` (${task.assignedModel.modelLabel} \xB7 ${label})` : ` (${task.assignedModel.modelLabel})`;
|
|
1294
1418
|
};
|
|
1295
1419
|
console.log(`
|
|
1296
|
-
Plan: ${tasks.length} task${tasks.length === 1 ? "" : "s"} (${aiCount} AI, ${manCount} Manual)
|
|
1420
|
+
Plan: ${tasks.length} task${tasks.length === 1 ? "" : "s"} (${aiCount} AI, ${manCount} Manual): ${runnersOf(plan, runners2).join(", ")}`);
|
|
1297
1421
|
console.log(`Session: ${sessionId}
|
|
1298
1422
|
`);
|
|
1299
1423
|
for (const t of tasks) {
|
|
1300
1424
|
const typeIcon = t.type === "user" ? "[MAN]" : t.ops ? "[OPS]" : "[ AI]";
|
|
1301
1425
|
const deps = t.dependencies?.length ? ` \u2190 ${t.dependencies.join(", ")}` : "";
|
|
1302
|
-
console.log(` ${(0,
|
|
1426
|
+
console.log(` ${(0, import_core9.taskOrderLabel)(t).padStart(2)}. ${typeIcon} ${t.title}${modelSuffix(t)}${deps}`);
|
|
1303
1427
|
for (const sub of t.subtasks || []) {
|
|
1304
1428
|
const subTypeIcon = sub.type === "user" ? "[MAN]" : "[ AI]";
|
|
1305
1429
|
const subDeps = sub.dependencies?.length ? ` \u2190 ${sub.dependencies.join(", ")}` : "";
|
|
1306
|
-
console.log(` ${(0,
|
|
1430
|
+
console.log(` ${(0, import_core9.taskOrderLabel)(sub, t)}. ${subTypeIcon} ${sub.title}${modelSuffix(sub)}${subDeps}`);
|
|
1307
1431
|
}
|
|
1308
1432
|
}
|
|
1309
1433
|
if (manCount > 0) {
|
|
1310
|
-
console.log("\n [MAN] = manual step
|
|
1434
|
+
console.log("\n [MAN] = manual step: run `ordewell tui` to work through it");
|
|
1311
1435
|
}
|
|
1312
1436
|
if (tasks.some((t) => t.ops)) {
|
|
1313
|
-
console.log(" [OPS] = ops task
|
|
1437
|
+
console.log(" [OPS] = ops task: runs in your checkout once the work it depends on is merged (`ordewell handoff merge`)");
|
|
1314
1438
|
}
|
|
1315
1439
|
}
|
|
1316
|
-
var
|
|
1440
|
+
var INHERITED_PLANNER_ENV = ["ORCHESTRATOR_MODEL", "ORDEWELL_PLANNER_EFFORT"];
|
|
1317
1441
|
async function forwardPlannerEnv(api) {
|
|
1442
|
+
const provider = process.env.AI_PROVIDER;
|
|
1443
|
+
if (provider) await api.updateSettings({ env: { AI_PROVIDER: provider } });
|
|
1318
1444
|
const env = {};
|
|
1319
|
-
for (const key of
|
|
1445
|
+
for (const key of INHERITED_PLANNER_ENV) {
|
|
1320
1446
|
const value = process.env[key];
|
|
1321
1447
|
if (value) env[key] = value;
|
|
1322
1448
|
}
|
|
1323
|
-
if (Object.keys(env).length
|
|
1324
|
-
await api.updateSettings({ env });
|
|
1449
|
+
if (Object.keys(env).length > 0) await api.updateSettings({ env, inherited: true });
|
|
1325
1450
|
}
|
|
1326
1451
|
async function handlePlan(subArgs, deps = {}) {
|
|
1327
1452
|
const injectedApi = deps.api;
|
|
@@ -1340,20 +1465,27 @@ async function handlePlan(subArgs, deps = {}) {
|
|
|
1340
1465
|
await forwardPlannerEnv(api);
|
|
1341
1466
|
const state = await api.getRunners();
|
|
1342
1467
|
const enabledIds = state.runners.filter((r) => r.enabled).map((r) => r.id);
|
|
1468
|
+
const enableHint = (id) => `Enable it with: ordewell runners ${id} on`;
|
|
1343
1469
|
let runners2;
|
|
1344
1470
|
if (runnerFlags.length === 0) {
|
|
1471
|
+
if (enabledIds.length === 0) {
|
|
1472
|
+
const installed = state.runners.map((r) => r.id);
|
|
1473
|
+
fail(
|
|
1474
|
+
"No runner is enabled.",
|
|
1475
|
+
installed.length > 0 ? `Installed: ${installed.join(", ")}. ${enableHint(installed[0])}` : "No supported coding agent is installed or on PATH."
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1345
1478
|
runners2 = enabledIds;
|
|
1346
1479
|
} else {
|
|
1347
1480
|
for (const r of runnerFlags) {
|
|
1348
|
-
if (
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
}
|
|
1481
|
+
if (enabledIds.includes(r)) continue;
|
|
1482
|
+
if (state.runners.some((known) => known.id === r)) fail(`${r} is installed but not enabled. ${enableHint(r)}`);
|
|
1483
|
+
fail(`Unknown runner: "${r}". Enabled runners: ${enabledIds.join(", ")}`);
|
|
1352
1484
|
}
|
|
1353
1485
|
runners2 = runnerFlags;
|
|
1354
1486
|
}
|
|
1355
1487
|
console.error(`Generating plan for: "${goal}"...`);
|
|
1356
|
-
const sessionId = (0,
|
|
1488
|
+
const sessionId = (0, import_core9.mintSessionId)();
|
|
1357
1489
|
const isTty = !!process.stderr.isTTY;
|
|
1358
1490
|
let lastLineLength = 0;
|
|
1359
1491
|
const stepLog = createStepLog({ verbose });
|
|
@@ -1447,19 +1579,24 @@ ${lastPlannerMessage(plan)}
|
|
|
1447
1579
|
${note}`);
|
|
1448
1580
|
saveLastSession(sessionId, goal, runnersOf(plan, runners2), workspace);
|
|
1449
1581
|
console.log(`
|
|
1450
|
-
|
|
1582
|
+
${await nextStep(api, plan)}`);
|
|
1451
1583
|
} catch (err) {
|
|
1452
1584
|
reader?.close();
|
|
1453
1585
|
closeStream();
|
|
1454
1586
|
if (isTty && lastLineLength > 0) process.stderr.write("\r\x1B[2K");
|
|
1455
1587
|
process.stderr.write("\n");
|
|
1456
|
-
|
|
1588
|
+
const refusal = headlessPlannerRefusal(err);
|
|
1589
|
+
if (refusal) {
|
|
1590
|
+
console.error(refusal);
|
|
1591
|
+
} else {
|
|
1592
|
+
console.error(`Plan generation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1593
|
+
}
|
|
1457
1594
|
process.exit(1);
|
|
1458
1595
|
}
|
|
1459
1596
|
}
|
|
1460
1597
|
|
|
1461
1598
|
// src/isolation.ts
|
|
1462
|
-
var
|
|
1599
|
+
var import_core10 = require("@ordewell/core");
|
|
1463
1600
|
var STATE_OF = {
|
|
1464
1601
|
active: "active",
|
|
1465
1602
|
merged: "integrated",
|
|
@@ -1543,7 +1680,7 @@ function repairedLanded(handoff) {
|
|
|
1543
1680
|
function repairedNotice(handoff) {
|
|
1544
1681
|
const repaired = repairedLanded(handoff);
|
|
1545
1682
|
if (repaired.length === 0) return "";
|
|
1546
|
-
const named = repaired.map((t) => `${t.title} (${(0,
|
|
1683
|
+
const named = repaired.map((t) => `${t.title} (${(0, import_core10.capConflictFiles)(t.repairedFiles)})`);
|
|
1547
1684
|
const list2 = named.length === 1 ? named[0] : `${named.slice(0, -1).join(", ")} and ${named[named.length - 1]}`;
|
|
1548
1685
|
return ` ${list2} landed through ${repaired.length === 1 ? "a conflict repair" : "conflict repairs"}.`;
|
|
1549
1686
|
}
|
|
@@ -1556,16 +1693,16 @@ function repoResultLines(handoff) {
|
|
|
1556
1693
|
function mergeOutcome(result, branch, group, repaired = []) {
|
|
1557
1694
|
const ok = result.outcome === "merged";
|
|
1558
1695
|
if (!group && result.outcome === "conflict") {
|
|
1559
|
-
return { ok, message: `Merging ${branch} conflicted, so it was aborted
|
|
1696
|
+
return { ok, message: `Merging ${branch} conflicted, so it was aborted: your tree is as it was. Merge it with git and resolve the conflict there.` };
|
|
1560
1697
|
}
|
|
1561
|
-
const { message } = (0,
|
|
1698
|
+
const { message } = (0, import_core10.describeMergeResult)(result, branch, group, repaired);
|
|
1562
1699
|
return { ok, message: ok || !group ? message : `${message} Each repository's ${branch} is a plain branch you can merge by hand.` };
|
|
1563
1700
|
}
|
|
1564
1701
|
|
|
1565
1702
|
// src/utils/output.ts
|
|
1566
|
-
var
|
|
1703
|
+
var import_core11 = require("@ordewell/core");
|
|
1567
1704
|
function iconFor(status) {
|
|
1568
|
-
switch ((0,
|
|
1705
|
+
switch ((0, import_core11.taskStatusKind)({ status })) {
|
|
1569
1706
|
case "done":
|
|
1570
1707
|
return "\x1B[32m\u2713\x1B[0m";
|
|
1571
1708
|
case "running":
|
|
@@ -1585,114 +1722,8 @@ function iconFor(status) {
|
|
|
1585
1722
|
}
|
|
1586
1723
|
|
|
1587
1724
|
// src/commands/run.ts
|
|
1588
|
-
var
|
|
1589
|
-
|
|
1590
|
-
// src/commands/shared.ts
|
|
1591
|
-
var import_readline2 = require("readline");
|
|
1592
|
-
|
|
1593
|
-
// src/catalog.ts
|
|
1594
|
-
function normalizeCatalog(result) {
|
|
1595
|
-
const runnersByModel = /* @__PURE__ */ new Map();
|
|
1596
|
-
for (const [runner, entries] of Object.entries(result.modelsByRunner ?? {})) {
|
|
1597
|
-
for (const entry of entries ?? []) {
|
|
1598
|
-
const id = String(entry.modelId);
|
|
1599
|
-
runnersByModel.set(id, [.../* @__PURE__ */ new Set([...runnersByModel.get(id) ?? [], runner])]);
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
const models = (result.models ?? []).map((m) => {
|
|
1603
|
-
const id = String(m.modelId);
|
|
1604
|
-
return {
|
|
1605
|
-
id,
|
|
1606
|
-
label: String(m.modelLabel ?? id),
|
|
1607
|
-
provider: String(m.runnerProviderLabel ?? m.runnerProvider ?? ""),
|
|
1608
|
-
variants: Array.isArray(m.variants) ? m.variants.map((v) => ({ id: String(v.id), label: String(v.label ?? v.id) })) : [],
|
|
1609
|
-
runners: runnersByModel.get(id) ?? []
|
|
1610
|
-
};
|
|
1611
|
-
});
|
|
1612
|
-
const orchestratorModels = (result.orchestratorModels ?? []).map((m) => ({
|
|
1613
|
-
id: String(m.id),
|
|
1614
|
-
label: String(m.label ?? m.id),
|
|
1615
|
-
provider: String(m.provider ?? ""),
|
|
1616
|
-
pricing: m.pricing ? `$${m.pricing}/MTok` : void 0
|
|
1617
|
-
}));
|
|
1618
|
-
return {
|
|
1619
|
-
models,
|
|
1620
|
-
orchestratorModels,
|
|
1621
|
-
providers: result.providers ?? [],
|
|
1622
|
-
providerErrors: result.providerErrors ?? {},
|
|
1623
|
-
modesByRunner: Object.fromEntries(
|
|
1624
|
-
Object.entries(result.modesByRunner ?? {}).map(([runner, modes]) => [
|
|
1625
|
-
runner,
|
|
1626
|
-
(modes ?? []).map((m) => ({
|
|
1627
|
-
id: String(m.id),
|
|
1628
|
-
label: String(m.label ?? m.id),
|
|
1629
|
-
description: m.description ? String(m.description) : void 0,
|
|
1630
|
-
autonomous: m.autonomous ? true : void 0,
|
|
1631
|
-
unavailable: m.unavailable ? String(m.unavailable) : void 0
|
|
1632
|
-
}))
|
|
1633
|
-
])
|
|
1634
|
-
)
|
|
1635
|
-
};
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
// src/commands/shared.ts
|
|
1639
|
-
async function connect(subArgs, injected) {
|
|
1640
|
-
return injected ?? new ApiClient(await ensureDaemon(resolvePort(subArgs)));
|
|
1641
|
-
}
|
|
1642
|
-
async function fetchCatalog(api) {
|
|
1643
|
-
return normalizeCatalog(await api.getModels());
|
|
1644
|
-
}
|
|
1645
|
-
function fail(...lines) {
|
|
1646
|
-
for (const line of lines) console.error(line);
|
|
1647
|
-
process.exit(1);
|
|
1648
|
-
}
|
|
1649
|
-
async function askYesNo(question) {
|
|
1650
|
-
if (!process.stdin.isTTY) return false;
|
|
1651
|
-
const rl = (0, import_readline2.createInterface)({ input: process.stdin, output: process.stderr });
|
|
1652
|
-
try {
|
|
1653
|
-
const answer = await new Promise((resolve4) => rl.question(`${question} [y/N] `, resolve4));
|
|
1654
|
-
return /^y(es)?$/i.test(answer.trim());
|
|
1655
|
-
} finally {
|
|
1656
|
-
rl.close();
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
function writeEnv(env) {
|
|
1660
|
-
const envFile = findEnvFile();
|
|
1661
|
-
for (const [key, value] of Object.entries(env)) {
|
|
1662
|
-
writeEnvVar(envFile, key, value);
|
|
1663
|
-
process.env[key] = value;
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
async function persistEnv(api, env) {
|
|
1667
|
-
const settings = await api.updateSettings({ env });
|
|
1668
|
-
writeEnv(env);
|
|
1669
|
-
return settings;
|
|
1670
|
-
}
|
|
1671
|
-
function toTaskView(task) {
|
|
1672
|
-
return {
|
|
1673
|
-
id: task.id,
|
|
1674
|
-
order: task.order,
|
|
1675
|
-
title: task.title,
|
|
1676
|
-
description: task.description,
|
|
1677
|
-
prompt: task.prompt ?? void 0,
|
|
1678
|
-
type: task.type === "user" ? "user" : "ai",
|
|
1679
|
-
status: task.status ?? "pending",
|
|
1680
|
-
dependencies: task.dependencies ?? [],
|
|
1681
|
-
assignedRunner: task.assignedRunner || void 0,
|
|
1682
|
-
taskMode: task.taskMode || void 0,
|
|
1683
|
-
assignedModel: task.assignedModel ?? void 0,
|
|
1684
|
-
subtasks: [...task.subtasks ?? []].sort((a, b) => a.order - b.order).map(toTaskView),
|
|
1685
|
-
...task.ops ? { ops: true } : {},
|
|
1686
|
-
...task.skills?.length ? { skills: [...task.skills] } : {},
|
|
1687
|
-
...task.addedBy ? { addedBy: task.addedBy } : {}
|
|
1688
|
-
};
|
|
1689
|
-
}
|
|
1690
|
-
function taskViews(plan) {
|
|
1691
|
-
const tasks = allTasksOf(plan);
|
|
1692
|
-
return [...tasks].sort((a, b) => a.order - b.order).map(toTaskView);
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// src/commands/run.ts
|
|
1725
|
+
var import_core12 = require("@ordewell/core");
|
|
1726
|
+
var inLine = (label) => label.charAt(0).toLowerCase() + label.slice(1);
|
|
1696
1727
|
function resolveSessionId(subArgs) {
|
|
1697
1728
|
const explicit = flag(subArgs, "--session-id");
|
|
1698
1729
|
if (explicit) return explicit;
|
|
@@ -1718,8 +1749,9 @@ async function followExecution(api, sessionId, start, onBlocked) {
|
|
|
1718
1749
|
const lines = [];
|
|
1719
1750
|
for (const t of entries) {
|
|
1720
1751
|
const icon = iconFor(t.status);
|
|
1721
|
-
const verdict = t.verdict ?
|
|
1722
|
-
|
|
1752
|
+
const verdict = t.verdict ? `: ${t.verdict.outcome.toUpperCase()}: ${t.verdict.reason.slice(0, 60)}` : "";
|
|
1753
|
+
const waits = t.waitsForApproval ? `: ${inLine(import_core12.WAITS_FOR_APPROVAL_LABEL)}: \`ordewell run-task <id>\` starts it` : "";
|
|
1754
|
+
lines.push(` ${icon} #${t.id.slice(-4)} ${t.status}${verdict}${waits}`);
|
|
1723
1755
|
}
|
|
1724
1756
|
const output = lines.join("\n");
|
|
1725
1757
|
if (output !== lastPrinted && output.trim()) {
|
|
@@ -1744,21 +1776,21 @@ async function followExecution(api, sessionId, start, onBlocked) {
|
|
|
1744
1776
|
});
|
|
1745
1777
|
const stream = api.streamExecution(sessionId, (event) => {
|
|
1746
1778
|
if (event.type === "task_started") {
|
|
1747
|
-
process.stderr.write(`\xB7 ${(0,
|
|
1779
|
+
process.stderr.write(`\xB7 ${(0, import_core12.taskStartedNotice)(event.title, event.runner)}
|
|
1748
1780
|
`);
|
|
1749
1781
|
}
|
|
1750
1782
|
if (event.type === "checkpoint") {
|
|
1751
|
-
process.stderr.write(`\xB7 Checkpoint
|
|
1783
|
+
process.stderr.write(`\xB7 Checkpoint: ${event.taskTitle}: ${(0, import_core12.truncateCheckpointSummary)(event.summary)}: \`ordewell checkpoint <id> approve|reject [reason]\` answers it
|
|
1752
1784
|
`);
|
|
1753
1785
|
}
|
|
1754
1786
|
if (event.type === "status_update" && event.tasks) {
|
|
1755
1787
|
printStatus(event.tasks);
|
|
1756
1788
|
const paused = event.runMerge?.paused === true;
|
|
1757
|
-
if (paused && !pausedAtGate) process.stderr.write("\xB7 Paused for Merge all
|
|
1789
|
+
if (paused && !pausedAtGate) process.stderr.write("\xB7 Paused for Merge all: `ordewell handoff merge` merges what has landed, and the waiting tasks start.\n");
|
|
1758
1790
|
pausedAtGate = paused;
|
|
1759
1791
|
}
|
|
1760
1792
|
if (event.type === "review_needed") {
|
|
1761
|
-
console.log("\nPlan needs your sign-off
|
|
1793
|
+
console.log("\nPlan needs your sign-off: run `ordewell approve` to continue.");
|
|
1762
1794
|
}
|
|
1763
1795
|
if (event.type === "execution_complete") {
|
|
1764
1796
|
const s = event;
|
|
@@ -1782,7 +1814,7 @@ Done. ${summary.completed || 0} completed, ${summary.failed || 0} failed, ${summ
|
|
|
1782
1814
|
const n = event.landed.length;
|
|
1783
1815
|
const where = isRepoGroup(event) ? ` in ${event.repos.map((r) => r.path).join(", ")}` : "";
|
|
1784
1816
|
console.log(`
|
|
1785
|
-
Run finished on ${handoffBranch(event)}${where}
|
|
1817
|
+
Run finished on ${handoffBranch(event)}${where}: ${n} task${n === 1 ? "" : "s"} landed.`);
|
|
1786
1818
|
if (isRepoGroup(event)) for (const line of repoResultLines(event)) console.log(` ${line}`);
|
|
1787
1819
|
if (event.deferredTo === "supervisor") console.log(" The supervisor may merge it.");
|
|
1788
1820
|
console.log(" `ordewell handoff review|merge|discard|cleanup` to land it.");
|
|
@@ -1800,7 +1832,7 @@ Run finished on ${handoffBranch(event)}${where} \u2014 ${n} task${n === 1 ? "" :
|
|
|
1800
1832
|
await start();
|
|
1801
1833
|
const outcome = await Promise.race([stream, blockedSeen]).catch(streamFailed);
|
|
1802
1834
|
if (outcome === "lost") {
|
|
1803
|
-
console.error("Lost the connection to the daemon
|
|
1835
|
+
console.error("Lost the connection to the daemon: the run is no longer being followed, but the daemon may still be running it. `ordewell status` shows where it stands.");
|
|
1804
1836
|
process.exit(1);
|
|
1805
1837
|
}
|
|
1806
1838
|
if (blocked === null) return;
|
|
@@ -1829,7 +1861,7 @@ async function handleRun(subArgs, injectedApi) {
|
|
|
1829
1861
|
}
|
|
1830
1862
|
|
|
1831
1863
|
// src/tui/blocks.ts
|
|
1832
|
-
var
|
|
1864
|
+
var import_core14 = require("@ordewell/core");
|
|
1833
1865
|
|
|
1834
1866
|
// src/tui/ansi.ts
|
|
1835
1867
|
var import_string_width = __toESM(require("string-width"));
|
|
@@ -2008,6 +2040,7 @@ var style = {
|
|
|
2008
2040
|
dim: colour("2"),
|
|
2009
2041
|
italic: colour("3"),
|
|
2010
2042
|
underline: colour("4"),
|
|
2043
|
+
strike: colour("9"),
|
|
2011
2044
|
inverse: colour("7"),
|
|
2012
2045
|
red: colour("31"),
|
|
2013
2046
|
green: colour("32"),
|
|
@@ -2044,9 +2077,9 @@ var style = {
|
|
|
2044
2077
|
};
|
|
2045
2078
|
|
|
2046
2079
|
// src/tui/markdown.ts
|
|
2047
|
-
var
|
|
2080
|
+
var import_core13 = require("@ordewell/core");
|
|
2048
2081
|
function renderMarkdown(source, cols) {
|
|
2049
|
-
return blocks((0,
|
|
2082
|
+
return blocks((0, import_core13.markdownTokens)(source), { room: Math.max(1, cols), base: [] });
|
|
2050
2083
|
}
|
|
2051
2084
|
function closeOf(tokens, open) {
|
|
2052
2085
|
const { level } = tokens[open];
|
|
@@ -2297,7 +2330,7 @@ function onBand(rows, cols) {
|
|
|
2297
2330
|
return style.enabled ? band.map((row) => style.band(pad(row, cols))) : band;
|
|
2298
2331
|
}
|
|
2299
2332
|
function skillPaintedLines(text, skills, room) {
|
|
2300
|
-
const tokens = (0,
|
|
2333
|
+
const tokens = (0, import_core14.loadedSkillTokens)(text, skills);
|
|
2301
2334
|
return wrapLines(text, room).map(({ line, start }) => {
|
|
2302
2335
|
let painted = "";
|
|
2303
2336
|
let at = 0;
|
|
@@ -2393,16 +2426,16 @@ function resultLines(block2, room, detailAll) {
|
|
|
2393
2426
|
const fit2 = (line) => detailAll ? wrap(line, room) : [truncate(line, room)];
|
|
2394
2427
|
const outcome = unsettledOutcome(block2);
|
|
2395
2428
|
if (outcome) {
|
|
2396
|
-
const [reason, ...rest] = (0,
|
|
2429
|
+
const [reason, ...rest] = (0, import_core14.outputLines)(block2.output).map(sanitize);
|
|
2397
2430
|
const status = fit2(reason ? `${outcome.label}: ${reason}` : outcome.label).map(outcome.paint);
|
|
2398
2431
|
return detailAll ? [...status, ...rest.flatMap(fit2).map(style.grey)] : status;
|
|
2399
2432
|
}
|
|
2400
2433
|
if (detailAll) {
|
|
2401
|
-
const lines2 = (0,
|
|
2434
|
+
const lines2 = (0, import_core14.outputLines)(block2.output).map(sanitize);
|
|
2402
2435
|
const note = lines2.length > PREVIEW_LINES ? ["\u2026 (ctrl+o to collapse)"] : [];
|
|
2403
2436
|
return lines2.length > 0 ? [...lines2.flatMap(fit2), ...note].map(style.grey) : [style.grey("(no output)")];
|
|
2404
2437
|
}
|
|
2405
|
-
const { lines, hiddenLineCount } = (0,
|
|
2438
|
+
const { lines, hiddenLineCount } = (0, import_core14.outputPreview)(block2.output, PREVIEW_LINES);
|
|
2406
2439
|
if (lines.length === 0) return [style.grey("(no output)")];
|
|
2407
2440
|
const more = hiddenLineCount > 0 ? [`\u2026 +${countOf(hiddenLineCount, "line")} (ctrl+o to expand)`] : [];
|
|
2408
2441
|
return [...lines.map(sanitize), ...more].map((line) => style.grey(truncate(line, room)));
|
|
@@ -2416,7 +2449,7 @@ var DIFF_PAINT = {
|
|
|
2416
2449
|
};
|
|
2417
2450
|
var DIFF_SIGN = { added: "+", removed: "-", context: " ", gap: "" };
|
|
2418
2451
|
function diffLines(block2, diff, room, detailAll) {
|
|
2419
|
-
const rows = (0,
|
|
2452
|
+
const rows = (0, import_core14.diffRows)(block2.output);
|
|
2420
2453
|
const gutter = Math.max(0, ...rows.map((row) => String(row.line ?? "").length));
|
|
2421
2454
|
const draw = (row) => {
|
|
2422
2455
|
const text = row.kind === "gap" ? `${" ".repeat(gutter)} \u22EE` : `${String(row.line ?? "").padStart(gutter)} ${DIFF_SIGN[row.kind]} ${sanitize(row.text)}`;
|
|
@@ -2425,7 +2458,7 @@ function diffLines(block2, diff, room, detailAll) {
|
|
|
2425
2458
|
const shown = detailAll ? rows : rows.slice(0, DIFF_PREVIEW_ROWS);
|
|
2426
2459
|
const hidden = rows.length - shown.length;
|
|
2427
2460
|
const more = hidden > 0 ? [style.grey(`\u2026 +${countOf(hidden, "line")} (ctrl+o to expand)`)] : [];
|
|
2428
|
-
return [(0,
|
|
2461
|
+
return [(0, import_core14.diffSummary)(diff), ...shown.flatMap(draw), ...more];
|
|
2429
2462
|
}
|
|
2430
2463
|
function toolLines(block2, cols, detailAll) {
|
|
2431
2464
|
const room = Math.max(1, cols - OUTPUT_INDENT.length);
|
|
@@ -2486,7 +2519,7 @@ var DECIDED_BY = {
|
|
|
2486
2519
|
function approvalLine(block2, cols) {
|
|
2487
2520
|
const policy = block2.decidedBy && block2.decidedBy !== "asked" ? DECIDED_BY[block2.decidedBy] : void 0;
|
|
2488
2521
|
const [mark, status, paint] = block2.status === "pending" ? ["?", "Waiting for you", style.yellow] : block2.status === "granted" ? ["\u2713", policy ? `Auto-approved (${policy})` : block2.forTask ? "Approved for this task" : "Approved", style.green] : block2.status === "withdrawn" ? ["\xB7", "Withdrawn", style.grey] : ["\u2298", policy ? `Auto-denied (${policy})` : "Denied", style.yellow];
|
|
2489
|
-
const note = block2.note ?
|
|
2522
|
+
const note = block2.note ? `: \u201C${firstLine(block2.note)}\u201D` : "";
|
|
2490
2523
|
const line = `${mark} ${status} \xB7 ${APPROVAL_KIND[block2.kind]}: ${firstLine(block2.subject)}${note}`;
|
|
2491
2524
|
return `${paint(mark)}${truncate(line, cols).slice(mark.length)}`;
|
|
2492
2525
|
}
|
|
@@ -2529,7 +2562,7 @@ function runnerCommandLines(block2, cols, detailAll) {
|
|
|
2529
2562
|
const status = ` ${block2.status}`;
|
|
2530
2563
|
const head = `${style.accent("\u276F")} ${truncate(typed, Math.max(1, cols - 2 - width(status)))}${COMMAND_PAINT[block2.status](status)}`;
|
|
2531
2564
|
const room = Math.max(1, cols - OUTPUT_INDENT.length);
|
|
2532
|
-
const printed = (0,
|
|
2565
|
+
const printed = (0, import_core14.outputLines)(block2.output).map(sanitize);
|
|
2533
2566
|
const shown = detailAll ? printed.flatMap((line) => wrap(line, room)) : printed.slice(0, PREVIEW_LINES).map((line) => truncate(line, room));
|
|
2534
2567
|
return [head, ...underHeader(shown.map(style.grey))];
|
|
2535
2568
|
}
|
|
@@ -2647,7 +2680,7 @@ function taskUsageText(usage) {
|
|
|
2647
2680
|
|
|
2648
2681
|
// src/commands/status.ts
|
|
2649
2682
|
var import_fs4 = require("fs");
|
|
2650
|
-
var
|
|
2683
|
+
var import_core15 = require("@ordewell/core");
|
|
2651
2684
|
async function handleStatus(subArgs, injectedApi) {
|
|
2652
2685
|
const sessionId = flag(subArgs, "--session-id");
|
|
2653
2686
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
@@ -2702,8 +2735,8 @@ function emitJson(payload, outputPath) {
|
|
|
2702
2735
|
}
|
|
2703
2736
|
function printOneSession(sessionId, meta, plan) {
|
|
2704
2737
|
const allTasks = allTasksOf(plan);
|
|
2705
|
-
const everyTask = (0,
|
|
2706
|
-
const blocked = (0,
|
|
2738
|
+
const everyTask = (0, import_core15.flattenTasks)(allTasks);
|
|
2739
|
+
const blocked = (0, import_core15.blockingFailures)(everyTask.map((t) => ({ ...t, subtasks: [], dependencies: t.dependencies ?? [] })));
|
|
2707
2740
|
const count = (status) => everyTask.filter((t) => t.status === status || status === "blocked" && blocked.has(t.id)).length;
|
|
2708
2741
|
console.log(`Session: ${sessionId}`);
|
|
2709
2742
|
console.log(`Goal: ${meta.goal}`);
|
|
@@ -2711,29 +2744,39 @@ function printOneSession(sessionId, meta, plan) {
|
|
|
2711
2744
|
console.log(`Phase: ${plan?.phase || "unknown"} | Created: ${meta.createdAt}`);
|
|
2712
2745
|
console.log(`Tasks: ${everyTask.length} total | ${count("completed")} done | ${count("failed")} failed | ${count("in_progress")} running | ${count("blocked")} blocked | ${count("skipped")} skipped
|
|
2713
2746
|
`);
|
|
2714
|
-
const usage = (0,
|
|
2747
|
+
const usage = (0, import_core15.planUsage)(allTasks);
|
|
2715
2748
|
if (usage.attempts) console.log(`Tasks total: ${taskUsageText(usage)}`);
|
|
2749
|
+
const record = servedApprovalRecord(plan);
|
|
2750
|
+
const approval = (row, ref) => row.waitsForApproval ? `${row.waitsForApproval}: \`ordewell run-task ${ref}\` starts it, or \`ordewell run\` runs the plan` : null;
|
|
2716
2751
|
for (const t of allTasks) {
|
|
2717
2752
|
const icon = iconFor(blocked.has(t.id) ? "blocked" : t.status);
|
|
2718
2753
|
const model = t.assignedModel ? ` (${t.assignedModel.modelLabel})` : "";
|
|
2719
2754
|
const shortId = (t.id ?? "").slice(-8);
|
|
2720
|
-
console.log(` ${(0,
|
|
2721
|
-
const row = (0,
|
|
2722
|
-
|
|
2723
|
-
if (
|
|
2755
|
+
console.log(` ${(0, import_core15.taskOrderLabel)(t).padStart(2)}. ${icon} [${shortId}] ${t.title}${model}`);
|
|
2756
|
+
const row = (0, import_core15.taskRowView)({ ...t, dependencies: t.dependencies ?? [], waitsForApproval: (0, import_core15.waitsForApproval)(t, record) }, { tasks: allTasks });
|
|
2757
|
+
const waiting = approval(row, (0, import_core15.taskOrderLabel)(t));
|
|
2758
|
+
if (waiting) console.log(` ${waiting}`);
|
|
2759
|
+
if (row.blockedBy) console.log(` ${(0, import_core15.blockedByLabel)(row.blockedBy)}`);
|
|
2760
|
+
if (row.status === "skipped") console.log(` ${(0, import_core15.skippedLabel)(row.skipReason)}`);
|
|
2724
2761
|
if (row.usage) console.log(` ${taskUsageText(row.usage)}`);
|
|
2725
2762
|
for (const sub of t.subtasks || []) {
|
|
2726
2763
|
const subIcon = iconFor(blocked.has(sub.id) ? "blocked" : sub.status);
|
|
2727
2764
|
const subModel = sub.assignedModel ? ` (${sub.assignedModel.modelLabel})` : "";
|
|
2728
2765
|
const subShortId = (sub.id ?? "").slice(-8);
|
|
2729
|
-
console.log(` ${(0,
|
|
2730
|
-
const subRow = (0,
|
|
2731
|
-
|
|
2732
|
-
if (
|
|
2766
|
+
console.log(` ${(0, import_core15.taskOrderLabel)(sub, t)}. ${subIcon} [${subShortId}] ${sub.title}${subModel}`);
|
|
2767
|
+
const subRow = (0, import_core15.taskRowView)({ ...sub, dependencies: sub.dependencies ?? [], waitsForApproval: (0, import_core15.waitsForApproval)(sub, record) }, { parent: t, tasks: allTasks });
|
|
2768
|
+
const subWaiting = approval(subRow, (0, import_core15.taskOrderLabel)(sub, t));
|
|
2769
|
+
if (subWaiting) console.log(` ${subWaiting}`);
|
|
2770
|
+
if (subRow.blockedBy) console.log(` ${(0, import_core15.blockedByLabel)(subRow.blockedBy)}`);
|
|
2771
|
+
if (subRow.status === "skipped") console.log(` ${(0, import_core15.skippedLabel)(subRow.skipReason)}`);
|
|
2733
2772
|
if (subRow.usage) console.log(` ${taskUsageText(subRow.usage)}`);
|
|
2734
2773
|
}
|
|
2735
2774
|
}
|
|
2736
2775
|
}
|
|
2776
|
+
function servedApprovalRecord(plan) {
|
|
2777
|
+
const approvedTasks = Array.isArray(plan?.approvedTasks) ? plan.approvedTasks.filter((id) => typeof id === "string") : void 0;
|
|
2778
|
+
return { approved: plan?.approved === true, ...approvedTasks ? { approvedTasks } : {} };
|
|
2779
|
+
}
|
|
2737
2780
|
|
|
2738
2781
|
// src/commands/stop.ts
|
|
2739
2782
|
async function handleStop(subArgs) {
|
|
@@ -2782,10 +2825,32 @@ async function handleWeb(subArgs) {
|
|
|
2782
2825
|
}
|
|
2783
2826
|
|
|
2784
2827
|
// src/commands/models.ts
|
|
2828
|
+
var import_core18 = require("@ordewell/core");
|
|
2829
|
+
|
|
2830
|
+
// src/commands/model.ts
|
|
2831
|
+
var import_core17 = require("@ordewell/core");
|
|
2832
|
+
|
|
2833
|
+
// src/plannerModelSwitch.ts
|
|
2785
2834
|
var import_core16 = require("@ordewell/core");
|
|
2835
|
+
function plannerSwitchRecall(settings) {
|
|
2836
|
+
if (settings.switchRecall) return settings.switchRecall;
|
|
2837
|
+
const model = settings.orchestratorModel;
|
|
2838
|
+
return model ? { model, effort: settings.plannerThinkingEffort, source: "remembered" } : { model: "", effort: "", source: "none" };
|
|
2839
|
+
}
|
|
2840
|
+
function vendorModelRefusal(provider, model, pickPlanner) {
|
|
2841
|
+
const meta = (0, import_core16.getProviderMeta)(provider);
|
|
2842
|
+
if (!(0, import_core16.runnerForProvider)(provider) || !meta || !(0, import_core16.isVendorModelFor)(meta.id, model)) return null;
|
|
2843
|
+
return `${model} is a vendor's model, which ${meta.label} cannot run. To plan with that vendor, switch planner with ${pickPlanner}.`;
|
|
2844
|
+
}
|
|
2845
|
+
function uninstalledPlannerRefusal(provider, installed, pickPlanner) {
|
|
2846
|
+
const runner = (0, import_core16.runnerForProvider)(provider);
|
|
2847
|
+
if (!runner || installed.includes(runner)) return null;
|
|
2848
|
+
const agents = import_core16.CLI_PROVIDERS.filter((id) => installed.includes((0, import_core16.runnerForProvider)(id) ?? ""));
|
|
2849
|
+
const label = (0, import_core16.getProviderMeta)(provider)?.label ?? provider;
|
|
2850
|
+
return agents.length > 0 ? `${label} is not installed or not on PATH. Installed coding agents: ${agents.join(", ")}. Switch with ${pickPlanner}.` : `${label} is not installed or not on PATH, and no other coding agent is. Install one, or plan with an API provider through ${pickPlanner}.`;
|
|
2851
|
+
}
|
|
2786
2852
|
|
|
2787
2853
|
// src/commands/model.ts
|
|
2788
|
-
var import_core15 = require("@ordewell/core");
|
|
2789
2854
|
var USAGE = "Usage: ordewell model [show | set <model-id>]";
|
|
2790
2855
|
async function handleModel(subArgs, injectedApi) {
|
|
2791
2856
|
const [action, modelId] = positionals(subArgs);
|
|
@@ -2800,7 +2865,7 @@ async function handleModel(subArgs, injectedApi) {
|
|
|
2800
2865
|
if (!modelId) fail(USAGE);
|
|
2801
2866
|
const settings = await api.getSettings();
|
|
2802
2867
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
2803
|
-
const runner = (0,
|
|
2868
|
+
const runner = (0, import_core17.runnerForProvider)(provider);
|
|
2804
2869
|
if (runner) {
|
|
2805
2870
|
const known = (await fetchCatalog(api)).models.filter((m) => m.runners?.includes(runner));
|
|
2806
2871
|
if (known.length > 0 && !known.some((m) => m.id === modelId)) {
|
|
@@ -2809,6 +2874,8 @@ async function handleModel(subArgs, injectedApi) {
|
|
|
2809
2874
|
`Run \`ordewell model\` to see what ${runner} offers.`
|
|
2810
2875
|
);
|
|
2811
2876
|
}
|
|
2877
|
+
const refusal = known.length === 0 ? vendorModelRefusal(provider, modelId, import_core17.HEADLESS_CLI_COMMANDS.pickPlanner) : null;
|
|
2878
|
+
if (refusal) fail(refusal);
|
|
2812
2879
|
}
|
|
2813
2880
|
const settled = await api.updateSettings({ orchestratorModel: modelId });
|
|
2814
2881
|
const effort = typeof settled.plannerThinkingEffort === "string" ? settled.plannerThinkingEffort : "";
|
|
@@ -2823,14 +2890,14 @@ async function showModels(api) {
|
|
|
2823
2890
|
const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
|
|
2824
2891
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
2825
2892
|
const current = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
|
|
2826
|
-
const runner = (0,
|
|
2893
|
+
const runner = (0, import_core17.runnerForProvider)(provider);
|
|
2827
2894
|
const options = runner ? catalog.models.filter((m) => m.runners?.includes(runner)) : catalog.orchestratorModels;
|
|
2828
2895
|
console.log(`
|
|
2829
2896
|
Planner model: ${current || "(backend default)"}`);
|
|
2830
|
-
console.log(`Planner backend: ${provider || "(default)"}${runner ?
|
|
2897
|
+
console.log(`Planner backend: ${provider || "(default)"}${runner ? `: showing ${runner} models only` : ""}
|
|
2831
2898
|
`);
|
|
2832
2899
|
if (options.length === 0) {
|
|
2833
|
-
console.log(runner ? ` No ${runner} models discovered yet
|
|
2900
|
+
console.log(runner ? ` No ${runner} models discovered yet: try \`ordewell refresh\`.` : " No models discovered. Configure a provider key with `ordewell key set <provider> <key>`.");
|
|
2834
2901
|
}
|
|
2835
2902
|
for (const m of options) {
|
|
2836
2903
|
const detail = [
|
|
@@ -2839,7 +2906,7 @@ Planner model: ${current || "(backend default)"}`);
|
|
|
2839
2906
|
m.variants?.length ? `${m.variants.length} effort level${m.variants.length === 1 ? "" : "s"}` : void 0
|
|
2840
2907
|
].filter(Boolean).join(" \xB7 ");
|
|
2841
2908
|
console.log(` ${m.id === current ? "*" : " "} ${m.id}`);
|
|
2842
|
-
if (detail) console.log(` ${m.label}
|
|
2909
|
+
if (detail) console.log(` ${m.label}: ${detail}`);
|
|
2843
2910
|
}
|
|
2844
2911
|
const failed = Object.entries(catalog.providerErrors);
|
|
2845
2912
|
for (const [id, message] of failed) {
|
|
@@ -2857,8 +2924,8 @@ async function handleModels(subArgs) {
|
|
|
2857
2924
|
const rest = subArgs.filter((_, i) => i !== setIdx && i !== setIdx + 1);
|
|
2858
2925
|
return handleModel(["set", subArgs[setIdx + 1], ...rest]);
|
|
2859
2926
|
}
|
|
2860
|
-
const config = new
|
|
2861
|
-
const providers = (0,
|
|
2927
|
+
const config = new import_core18.EnvConfig();
|
|
2928
|
+
const providers = (0, import_core18.configuredProviders)(config);
|
|
2862
2929
|
if (providers.length === 0) {
|
|
2863
2930
|
console.error("No providers configured. Set an API key for a provider, e.g.:");
|
|
2864
2931
|
console.error(" OPENROUTER_API_KEY=... (OpenRouter)");
|
|
@@ -2867,19 +2934,19 @@ async function handleModels(subArgs) {
|
|
|
2867
2934
|
console.error(" OPENAI_COMPATIBLE_BASE_URL=... (self-hosted / OpenAI-compatible)");
|
|
2868
2935
|
process.exit(1);
|
|
2869
2936
|
}
|
|
2870
|
-
const { apiKeys, baseUrls } = (0,
|
|
2937
|
+
const { apiKeys, baseUrls } = (0, import_core18.collectProviderCredentials)(config);
|
|
2871
2938
|
let models;
|
|
2872
2939
|
let errors;
|
|
2873
2940
|
try {
|
|
2874
|
-
const result = await (0,
|
|
2875
|
-
models = (0,
|
|
2941
|
+
const result = await (0, import_core18.fetchAllProviderModels)({ apiKeys, baseUrls });
|
|
2942
|
+
models = (0, import_core18.toOrchestratorOptions)(result.models, import_core18.ORCHESTRATOR_SHORTCUTS);
|
|
2876
2943
|
errors = result.errors;
|
|
2877
2944
|
} catch (err) {
|
|
2878
2945
|
console.error(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
2879
2946
|
process.exit(1);
|
|
2880
2947
|
return;
|
|
2881
2948
|
}
|
|
2882
|
-
const providerLabel = (p) => (0,
|
|
2949
|
+
const providerLabel = (p) => (0, import_core18.getProviderMeta)(p)?.label ?? p;
|
|
2883
2950
|
console.log(
|
|
2884
2951
|
`
|
|
2885
2952
|
Available models (${models.length}) across ${providers.length} configured provider(s):`
|
|
@@ -2919,7 +2986,7 @@ ${providerLabel(provider)}`);
|
|
|
2919
2986
|
|
|
2920
2987
|
// src/commands/setup.ts
|
|
2921
2988
|
var import_fs6 = require("fs");
|
|
2922
|
-
var
|
|
2989
|
+
var import_core19 = require("@ordewell/core");
|
|
2923
2990
|
function handleSetup(subArgs) {
|
|
2924
2991
|
const modelIdx = subArgs.indexOf("--model");
|
|
2925
2992
|
const chosenModel = modelIdx !== -1 ? subArgs[modelIdx + 1] : null;
|
|
@@ -2962,7 +3029,7 @@ function handleSetup(subArgs) {
|
|
|
2962
3029
|
# OPENAI_COMPATIBLE_BASE_URL=http://localhost:11434/v1
|
|
2963
3030
|
# OPENAI_COMPATIBLE_API_KEY=your-key-here
|
|
2964
3031
|
|
|
2965
|
-
# Or plan with a coding agent you already have installed
|
|
3032
|
+
# Or plan with a coding agent you already have installed: no API key needed,
|
|
2966
3033
|
# it uses that agent's own subscription. Set the model to one of its own.
|
|
2967
3034
|
# AI_PROVIDER=${runnerIdChoice()}
|
|
2968
3035
|
# ORCHESTRATOR_MODEL=haiku
|
|
@@ -2991,13 +3058,13 @@ function handleSetup(subArgs) {
|
|
|
2991
3058
|
# STEPFUN_API_KEY=...
|
|
2992
3059
|
# SILICONFLOW_API_KEY=...
|
|
2993
3060
|
`;
|
|
2994
|
-
(0,
|
|
3061
|
+
(0, import_core19.writePrivateFile)(envFile, content);
|
|
2995
3062
|
console.log(`Wrote: ${envFile}`);
|
|
2996
3063
|
console.log("Review and edit the file, then run: ordewell tui");
|
|
2997
3064
|
}
|
|
2998
3065
|
|
|
2999
3066
|
// src/commands/skills.ts
|
|
3000
|
-
var
|
|
3067
|
+
var import_core20 = require("@ordewell/core");
|
|
3001
3068
|
var import_path5 = require("path");
|
|
3002
3069
|
function invocation(skill) {
|
|
3003
3070
|
if (skill.userInvocable && skill.modelInvocable) return "both";
|
|
@@ -3010,22 +3077,22 @@ function handleSkills(subArgs) {
|
|
|
3010
3077
|
fail("Usage: ordewell skills [--workspace /path] [--json]");
|
|
3011
3078
|
}
|
|
3012
3079
|
const workspace = (0, import_path5.resolve)(flag(subArgs, "--workspace") || process.cwd());
|
|
3013
|
-
const catalog = (0,
|
|
3080
|
+
const catalog = (0, import_core20.createSkillsService)(workspace, new import_core20.EnvConfig().workspaceRepos).readCatalog();
|
|
3014
3081
|
const skills = catalog.skills.map((skill) => ({
|
|
3015
3082
|
name: skill.name,
|
|
3016
3083
|
scope: skill.source,
|
|
3017
3084
|
appliesTo: skill.appliesTo,
|
|
3018
3085
|
invocation: invocation(skill),
|
|
3019
|
-
path: (0,
|
|
3086
|
+
path: (0, import_core20.abbreviateHome)(skill.path)
|
|
3020
3087
|
}));
|
|
3021
3088
|
const shadowed = catalog.shadowed.map(({ skill, shadowedBy }) => ({
|
|
3022
3089
|
name: skill.name,
|
|
3023
|
-
path: (0,
|
|
3024
|
-
shadowedBy: { scope: shadowedBy.source, path: (0,
|
|
3090
|
+
path: (0, import_core20.abbreviateHome)(skill.path),
|
|
3091
|
+
shadowedBy: { scope: shadowedBy.source, path: (0, import_core20.abbreviateHome)(shadowedBy.path) }
|
|
3025
3092
|
}));
|
|
3026
3093
|
const invalid = catalog.invalid.map((skill) => ({
|
|
3027
3094
|
name: skill.name,
|
|
3028
|
-
path: (0,
|
|
3095
|
+
path: (0, import_core20.abbreviateHome)(skill.path),
|
|
3029
3096
|
reason: skill.reason
|
|
3030
3097
|
}));
|
|
3031
3098
|
if (hasFlag(subArgs, "--json")) {
|
|
@@ -3140,7 +3207,7 @@ async function handleShow(args, api) {
|
|
|
3140
3207
|
}
|
|
3141
3208
|
|
|
3142
3209
|
// src/commands/task-control.ts
|
|
3143
|
-
var
|
|
3210
|
+
var import_core21 = require("@ordewell/core");
|
|
3144
3211
|
var PAST_TENSE = {
|
|
3145
3212
|
run: "Task started.",
|
|
3146
3213
|
"force-start": "Task force-started.",
|
|
@@ -3183,8 +3250,8 @@ async function confirmPastGate(subArgs, api, sessionId, taskId, confirm2) {
|
|
|
3183
3250
|
if (hasFlag(subArgs, "--yes")) return;
|
|
3184
3251
|
const unmerged = await api.getMergeGate(sessionId, taskId).catch(() => []);
|
|
3185
3252
|
if (unmerged.length === 0) return;
|
|
3186
|
-
const question = `${(0,
|
|
3187
|
-
if (!await confirm2(question)) fail("Not started
|
|
3253
|
+
const question = `${(0, import_core21.pastGateConfirmation)(unmerged.map((d) => (0, import_core21.titledTaskRef)(d)))} Start it anyway?`;
|
|
3254
|
+
if (!await confirm2(question)) fail("Not started: nothing was changed. Pass --yes to start it without a prompt.");
|
|
3188
3255
|
}
|
|
3189
3256
|
function makeHandler(action, command = action) {
|
|
3190
3257
|
const yes = PASSES_GATE.has(action) ? " [--yes]" : "";
|
|
@@ -3232,7 +3299,7 @@ async function handleCheckpoint(subArgs, injectedApi) {
|
|
|
3232
3299
|
process.exit(1);
|
|
3233
3300
|
}
|
|
3234
3301
|
if (answer === "approve" && reason) {
|
|
3235
|
-
console.error(`Approving takes no note
|
|
3302
|
+
console.error(`Approving takes no note: reject with a reason to tell the task something.
|
|
3236
3303
|
${usage}`);
|
|
3237
3304
|
process.exit(1);
|
|
3238
3305
|
}
|
|
@@ -3293,7 +3360,7 @@ async function handleSkip(subArgs, api) {
|
|
|
3293
3360
|
async (client, sessionId, taskId) => {
|
|
3294
3361
|
try {
|
|
3295
3362
|
await client.skipTask(sessionId, taskId, flag(subArgs, "--reason"));
|
|
3296
|
-
console.log("Task skipped: none of its work lands, and the tasks that depend on it go ahead without it.");
|
|
3363
|
+
console.log("Task skipped: none of its work lands, and the tasks that depend on it go ahead without it once the work it waited on is done.");
|
|
3297
3364
|
} catch (err) {
|
|
3298
3365
|
console.error(`Failed to skip task: ${err.message}`);
|
|
3299
3366
|
process.exit(1);
|
|
@@ -3401,7 +3468,7 @@ async function handleSessions(subArgs, injectedApi) {
|
|
|
3401
3468
|
}
|
|
3402
3469
|
const { meta } = session;
|
|
3403
3470
|
saveLastSession(meta.id, meta.goal, meta.runners || [], workspace);
|
|
3404
|
-
console.log(`Loaded session ${meta.id} ("${meta.goal}")
|
|
3471
|
+
console.log(`Loaded session ${meta.id} ("${meta.goal}"): ${meta.taskCount} tasks, status ${meta.status}.`);
|
|
3405
3472
|
console.log(" `ordewell status` for detail, `ordewell run` to execute.");
|
|
3406
3473
|
return;
|
|
3407
3474
|
}
|
|
@@ -3425,7 +3492,7 @@ async function handleSessions(subArgs, injectedApi) {
|
|
|
3425
3492
|
}
|
|
3426
3493
|
|
|
3427
3494
|
// src/commands/conversation.ts
|
|
3428
|
-
var REWIND_USAGE = "Usage: ordewell rewind [<n>] [--session-id <id>] [--workspace /path]
|
|
3495
|
+
var REWIND_USAGE = "Usage: ordewell rewind [<n>] [--session-id <id>] [--workspace /path]: with no <n>, lists the messages you can rewind to.";
|
|
3429
3496
|
async function adopted(subArgs, injectedApi) {
|
|
3430
3497
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
3431
3498
|
const sessionId = resolveSessionId(subArgs);
|
|
@@ -3447,7 +3514,7 @@ async function handleFork(subArgs, injectedApi) {
|
|
|
3447
3514
|
fail(`Failed to fork: ${err.message}`);
|
|
3448
3515
|
}
|
|
3449
3516
|
saveLastSession(fork.sessionId, fork.goal, fork.plan.runners ?? [], workspace);
|
|
3450
|
-
console.log(`Forked ${sessionId} into ${fork.sessionId}
|
|
3517
|
+
console.log(`Forked ${sessionId} into ${fork.sessionId}: the fork is now the current session.`);
|
|
3451
3518
|
console.log(` \`ordewell sessions load ${sessionId}\` to go back to the original.`);
|
|
3452
3519
|
}
|
|
3453
3520
|
async function handleCompact(subArgs, injectedApi) {
|
|
@@ -3456,7 +3523,7 @@ async function handleCompact(subArgs, injectedApi) {
|
|
|
3456
3523
|
try {
|
|
3457
3524
|
result = await api.compactConversation(sessionId);
|
|
3458
3525
|
} catch (err) {
|
|
3459
|
-
fail(`Failed to condense: ${err.message}`);
|
|
3526
|
+
fail(`Failed to condense: ${headlessPlannerRefusal(err) ?? err.message}`);
|
|
3460
3527
|
}
|
|
3461
3528
|
console.log(`Condensed ${sessionId}. The last two exchanges were kept as they were; the tasks are unchanged.
|
|
3462
3529
|
`);
|
|
@@ -3474,7 +3541,7 @@ async function handleRewind(subArgs, injectedApi) {
|
|
|
3474
3541
|
fail(`Failed to list messages: ${err.message}`);
|
|
3475
3542
|
}
|
|
3476
3543
|
if (targets.length === 0) {
|
|
3477
|
-
console.log("Nothing to rewind to yet
|
|
3544
|
+
console.log("Nothing to rewind to yet: the only message so far is the goal.");
|
|
3478
3545
|
return;
|
|
3479
3546
|
}
|
|
3480
3547
|
console.log("Your messages, most recent first:\n");
|
|
@@ -3489,31 +3556,31 @@ async function handleRewind(subArgs, injectedApi) {
|
|
|
3489
3556
|
fail(`Failed to rewind: ${err.message}`);
|
|
3490
3557
|
}
|
|
3491
3558
|
saveLastSession(fork.sessionId, fork.goal, fork.plan.runners ?? [], workspace);
|
|
3492
|
-
console.log(`Forked ${sessionId} into ${fork.sessionId} from just before message ${arg}
|
|
3559
|
+
console.log(`Forked ${sessionId} into ${fork.sessionId} from just before message ${arg}. The fork is now the current session.`);
|
|
3493
3560
|
console.log(` \`ordewell sessions load ${sessionId}\` to go back to the original.`);
|
|
3494
3561
|
console.log("\nThe message you rewound to, to resend or edit:\n");
|
|
3495
3562
|
console.log(fork.rewoundMessage);
|
|
3496
3563
|
}
|
|
3497
3564
|
|
|
3498
3565
|
// src/tui/transcript.ts
|
|
3499
|
-
var
|
|
3566
|
+
var import_core22 = require("@ordewell/core");
|
|
3500
3567
|
function show(state, { view: conversation, gate: turnGate }) {
|
|
3501
3568
|
const gated = turnGate === state.turnGate ? state : { ...state, turnGate };
|
|
3502
3569
|
if (conversation === state.conversation) return gated;
|
|
3503
3570
|
return { ...gated, conversation };
|
|
3504
3571
|
}
|
|
3505
3572
|
function fold(state, input) {
|
|
3506
|
-
return show(state, { view: (0,
|
|
3573
|
+
return show(state, { view: (0, import_core22.reduceConversation)(state.conversation, input), gate: state.turnGate });
|
|
3507
3574
|
}
|
|
3508
3575
|
function say(state, role, text) {
|
|
3509
3576
|
const said = fold(state, { type: "local_entry", role, text });
|
|
3510
3577
|
return said.conversation.nextId > state.conversation.nextId ? { ...said, scroll: 0 } : said;
|
|
3511
3578
|
}
|
|
3512
3579
|
function hear(state, message) {
|
|
3513
|
-
return show(state, (0,
|
|
3580
|
+
return show(state, (0, import_core22.followTurn)(state.conversation, state.turnGate, message));
|
|
3514
3581
|
}
|
|
3515
3582
|
function cutTurn(state) {
|
|
3516
|
-
return show(state, (0,
|
|
3583
|
+
return show(state, (0, import_core22.stopTurn)(state.conversation, state.turnGate));
|
|
3517
3584
|
}
|
|
3518
3585
|
function wiped(view) {
|
|
3519
3586
|
return { ...view, blocks: view.blocks.filter((b) => b.type === "usage") };
|
|
@@ -3522,16 +3589,16 @@ function wiped(view) {
|
|
|
3522
3589
|
// src/tui/handoff.ts
|
|
3523
3590
|
var HANDOFF_ACTIONS = [
|
|
3524
3591
|
{ id: "review", label: "Review diff", hint: "the branch against the commit it forked from" },
|
|
3525
|
-
{ id: "merge", label: "Merge", hint: "into the branch you have checked out
|
|
3526
|
-
{ id: "discard", label: "Discard", hint: "the run, its worktrees and this branch
|
|
3592
|
+
{ id: "merge", label: "Merge", hint: "into the branch you have checked out (asks first)" },
|
|
3593
|
+
{ id: "discard", label: "Discard", hint: "the run, its worktrees and this branch (asks first)" },
|
|
3527
3594
|
{ id: "cleanup", label: "Clean up", hint: "remove worktrees and task branches, keep this branch" }
|
|
3528
3595
|
];
|
|
3529
3596
|
function handoffActions(handoff) {
|
|
3530
3597
|
if (!isRepoGroup(handoff)) return HANDOFF_ACTIONS;
|
|
3531
3598
|
return [
|
|
3532
3599
|
{ id: "review", label: "Review diff", hint: "each repository's branch against the commit it forked from" },
|
|
3533
|
-
{ id: "merge", label: "Merge all", hint: "every repository's branch into what you have checked out there
|
|
3534
|
-
{ id: "discard", label: "Discard", hint: "the run, its worktrees and every repository's branch
|
|
3600
|
+
{ id: "merge", label: "Merge all", hint: "every repository's branch into what you have checked out there (asks first)" },
|
|
3601
|
+
{ id: "discard", label: "Discard", hint: "the run, its worktrees and every repository's branch (asks first)" },
|
|
3535
3602
|
{ id: "cleanup", label: "Clean up", hint: "remove worktrees and task branches everywhere, keep the integration branches" }
|
|
3536
3603
|
];
|
|
3537
3604
|
}
|
|
@@ -3544,7 +3611,7 @@ function currentHandoff(state) {
|
|
|
3544
3611
|
}
|
|
3545
3612
|
var midRun = (state) => state.status === "executing";
|
|
3546
3613
|
function handoffCommand(state, arg) {
|
|
3547
|
-
if (!state.sessionId || !currentHandoff(state)) return fail2(state, "No isolated run to hand off
|
|
3614
|
+
if (!state.sessionId || !currentHandoff(state)) return fail2(state, "No isolated run to hand off: a plan that ran in worktrees leaves one here.");
|
|
3548
3615
|
if (arg === void 0) return { state: { ...state, overlay: { kind: "handoff", index: 0, diff: null } }, effects: [] };
|
|
3549
3616
|
const action = HANDOFF_ACTIONS.find((a) => a.id === arg.toLowerCase());
|
|
3550
3617
|
if (!action) return fail2(state, `Usage: ${HANDOFF_USAGE}`);
|
|
@@ -3557,13 +3624,13 @@ function handoffArrived(state, handoff) {
|
|
|
3557
3624
|
const tasks = `${landed} task${landed === 1 ? "" : "s"} landed`;
|
|
3558
3625
|
const received = { ...state, handoff };
|
|
3559
3626
|
if (handoff.deferredTo === "supervisor") {
|
|
3560
|
-
return say(received, "system", `Run finished on ${branch}${where}
|
|
3627
|
+
return say(received, "system", `Run finished on ${branch}${where}: ${tasks}. The supervisor may merge it; /handoff to review it yourself.`);
|
|
3561
3628
|
}
|
|
3562
3629
|
const returned = state.handoff?.deferredTo === "supervisor" && handoffBranch(state.handoff) === branch;
|
|
3563
3630
|
const told = say(
|
|
3564
3631
|
received,
|
|
3565
3632
|
"system",
|
|
3566
|
-
returned ? `The supervisor left ${branch}${where} to you
|
|
3633
|
+
returned ? `The supervisor left ${branch}${where} to you: ${tasks}. /handoff to review and land it.` : `Run finished on ${branch}${where}: ${tasks}. /handoff to review and land it.`
|
|
3567
3634
|
);
|
|
3568
3635
|
return state.overlay ? told : { ...told, overlay: { kind: "handoff", index: 0, diff: null } };
|
|
3569
3636
|
}
|
|
@@ -3601,12 +3668,12 @@ function runHandoffAction(state, id) {
|
|
|
3601
3668
|
overlay: isRepoGroup(handoff) ? {
|
|
3602
3669
|
kind: "confirm",
|
|
3603
3670
|
title: "Merge all into your branches?",
|
|
3604
|
-
message: `Merge ${handoffBranch(handoff)} into whatever each of ${reposWithWork(handoff).join(", ")} has checked out. It checks every repository first and merges none unless every repository can take it; on git older than 2.38 it goes repository by repository and stops at the first that fails. A merge that conflicts is aborted, and your trees stay as they were. Once every repository has merged, the run's worktrees and branches are removed
|
|
3671
|
+
message: `Merge ${handoffBranch(handoff)} into whatever each of ${reposWithWork(handoff).join(", ")} has checked out. It checks every repository first and merges none unless every repository can take it; on git older than 2.38 it goes repository by repository and stops at the first that fails. A merge that conflicts is aborted, and your trees stay as they were. Once every repository has merged, the run's worktrees and branches are removed unless a task still has work in its worktree that has not landed: then the run stays, and so does that work.${repairedNotice(handoff)}`,
|
|
3605
3672
|
action: { kind: "merge-run" }
|
|
3606
3673
|
} : {
|
|
3607
3674
|
kind: "confirm",
|
|
3608
3675
|
title: "Merge into your branch?",
|
|
3609
|
-
message: `Merge ${handoffBranch(handoff)} into whatever you have checked out. If it conflicts the merge is aborted and your tree stays as it was. Once it has merged, the run's worktrees and branches are removed
|
|
3676
|
+
message: `Merge ${handoffBranch(handoff)} into whatever you have checked out. If it conflicts the merge is aborted and your tree stays as it was. Once it has merged, the run's worktrees and branches are removed unless a task still has work in its worktree that has not landed: then the run stays, and so does that work.${repairedNotice(handoff)}`,
|
|
3610
3677
|
action: { kind: "merge-run" }
|
|
3611
3678
|
}
|
|
3612
3679
|
},
|
|
@@ -3619,7 +3686,7 @@ function runHandoffAction(state, id) {
|
|
|
3619
3686
|
overlay: {
|
|
3620
3687
|
kind: "confirm",
|
|
3621
3688
|
title: "Discard this run?",
|
|
3622
|
-
message: `Removes the run's worktrees and task branches and deletes ${handoffBranch(handoff)}${isRepoGroup(handoff) ? ` in ${reposOf2(handoff)}` : ""}. Tasks keep their status
|
|
3689
|
+
message: `Removes the run's worktrees and task branches and deletes ${handoffBranch(handoff)}${isRepoGroup(handoff) ? ` in ${reposOf2(handoff)}` : ""}. Tasks keep their status: mark one not done if you did not keep its work.`,
|
|
3623
3690
|
action: { kind: "discard-run" }
|
|
3624
3691
|
}
|
|
3625
3692
|
},
|
|
@@ -3714,7 +3781,7 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
3714
3781
|
if (action === void 0) {
|
|
3715
3782
|
console.log(`${USAGE4}
|
|
3716
3783
|
`);
|
|
3717
|
-
for (const a of HANDOFF_ACTIONS) console.log(` ${a.id.padEnd(8)} ${a.label}
|
|
3784
|
+
for (const a of HANDOFF_ACTIONS) console.log(` ${a.id.padEnd(8)} ${a.label}: ${a.hint}`);
|
|
3718
3785
|
return;
|
|
3719
3786
|
}
|
|
3720
3787
|
if (!HANDOFF_ACTIONS.some((a) => a.id === action)) fail(`Unknown handoff action "${action}".`, USAGE4);
|
|
@@ -3727,7 +3794,7 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
3727
3794
|
const asked = hasFlag(subArgs, "--yes");
|
|
3728
3795
|
const confirmed = async (question) => {
|
|
3729
3796
|
if (asked || await confirm2(question)) return;
|
|
3730
|
-
fail("Not confirmed
|
|
3797
|
+
fail("Not confirmed: nothing was changed. Pass --yes to confirm without a prompt.");
|
|
3731
3798
|
};
|
|
3732
3799
|
const attempt = async (work) => {
|
|
3733
3800
|
try {
|
|
@@ -3762,16 +3829,7 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
3762
3829
|
}
|
|
3763
3830
|
|
|
3764
3831
|
// src/commands/planner.ts
|
|
3765
|
-
var
|
|
3766
|
-
|
|
3767
|
-
// src/plannerModelSwitch.ts
|
|
3768
|
-
function plannerSwitchRecall(settings) {
|
|
3769
|
-
if (settings.switchRecall) return settings.switchRecall;
|
|
3770
|
-
const model = settings.orchestratorModel;
|
|
3771
|
-
return model ? { model, effort: settings.plannerThinkingEffort, source: "remembered" } : { model: "", effort: "", source: "none" };
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
// src/commands/planner.ts
|
|
3832
|
+
var import_core23 = require("@ordewell/core");
|
|
3775
3833
|
async function handlePlanner(subArgs, injectedApi) {
|
|
3776
3834
|
const api = await connect(subArgs, injectedApi);
|
|
3777
3835
|
const [wanted] = positionals(subArgs);
|
|
@@ -3780,20 +3838,25 @@ async function handlePlanner(subArgs, injectedApi) {
|
|
|
3780
3838
|
return;
|
|
3781
3839
|
}
|
|
3782
3840
|
const provider = wanted.toLowerCase();
|
|
3783
|
-
const meta = (0,
|
|
3841
|
+
const meta = (0, import_core23.getProviderMeta)(provider);
|
|
3784
3842
|
if (!meta) {
|
|
3785
3843
|
fail(`Unknown planner: ${provider}`, "Run `ordewell planner` with no arguments to see the list.");
|
|
3786
3844
|
}
|
|
3845
|
+
if ((0, import_core23.isCliProvider)(provider)) {
|
|
3846
|
+
const installed = (await api.getRunners()).runners.map((r) => r.id);
|
|
3847
|
+
const refusal = uninstalledPlannerRefusal(provider, installed, import_core23.HEADLESS_CLI_COMMANDS.pickPlanner);
|
|
3848
|
+
if (refusal) fail(refusal);
|
|
3849
|
+
}
|
|
3787
3850
|
const settings = await persistEnv(api, { AI_PROVIDER: provider });
|
|
3788
3851
|
const recall2 = plannerSwitchRecall(settings);
|
|
3789
3852
|
writeEnv({ ORCHESTRATOR_MODEL: recall2.model, ORDEWELL_PLANNER_EFFORT: recall2.effort });
|
|
3790
3853
|
const catalog = await fetchCatalog(api);
|
|
3791
3854
|
console.log(
|
|
3792
|
-
(0,
|
|
3855
|
+
(0, import_core23.isCliProvider)(provider) ? `Planning with ${meta.label}: no API key needed, it uses that agent's own subscription.` : `Planner set to ${meta.label}.`
|
|
3793
3856
|
);
|
|
3794
3857
|
console.log(plannerModelLine(recall2));
|
|
3795
|
-
if ((0,
|
|
3796
|
-
console.log(` No ${meta.apiKeyEnvVar} configured yet
|
|
3858
|
+
if ((0, import_core23.isApiProvider)(provider) && !catalog.providers.includes(provider)) {
|
|
3859
|
+
console.log(` No ${meta.apiKeyEnvVar} configured yet: set one with \`ordewell key set ${provider} <key>\`.`);
|
|
3797
3860
|
}
|
|
3798
3861
|
}
|
|
3799
3862
|
function plannerModelLine(recall2) {
|
|
@@ -3801,7 +3864,7 @@ function plannerModelLine(recall2) {
|
|
|
3801
3864
|
case "remembered":
|
|
3802
3865
|
return ` Restored its planner model: ${recall2.model}.`;
|
|
3803
3866
|
case "catalog-default":
|
|
3804
|
-
return ` Using ${recall2.model}, its default model
|
|
3867
|
+
return ` Using ${recall2.model}, its default model: pick another with \`ordewell model set <id>\`.`;
|
|
3805
3868
|
case "none":
|
|
3806
3869
|
return " Pick a model with `ordewell model set <id>`, or leave it on the default.";
|
|
3807
3870
|
}
|
|
@@ -3809,16 +3872,16 @@ function plannerModelLine(recall2) {
|
|
|
3809
3872
|
async function showPlanners(api) {
|
|
3810
3873
|
const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
|
|
3811
3874
|
const current = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
3812
|
-
console.log("\nCoding agents (no API key
|
|
3813
|
-
for (const id of
|
|
3814
|
-
const runner = (0,
|
|
3875
|
+
console.log("\nCoding agents (no API key; they use their own subscription)");
|
|
3876
|
+
for (const id of import_core23.CLI_PROVIDERS) {
|
|
3877
|
+
const runner = (0, import_core23.runnerForProvider)(id);
|
|
3815
3878
|
const discovered = runner ? catalog.models.some((m) => m.runners?.includes(runner)) : false;
|
|
3816
|
-
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${(0,
|
|
3879
|
+
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${(0, import_core23.getProviderMeta)(id)?.label ?? id}${discovered ? "" : " (not detected)"}`);
|
|
3817
3880
|
}
|
|
3818
3881
|
console.log("\nAPI providers");
|
|
3819
|
-
for (const id of
|
|
3882
|
+
for (const id of import_core23.PROVIDER_PRIORITY) {
|
|
3820
3883
|
const configured = catalog.providers.includes(id);
|
|
3821
|
-
const meta = (0,
|
|
3884
|
+
const meta = (0, import_core23.getProviderMeta)(id);
|
|
3822
3885
|
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${meta.label}${configured ? "" : ` (no ${meta.apiKeyEnvVar})`}`);
|
|
3823
3886
|
}
|
|
3824
3887
|
console.log(`
|
|
@@ -3835,7 +3898,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
3835
3898
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
3836
3899
|
const modelId = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
|
|
3837
3900
|
const currentEffort = typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : "";
|
|
3838
|
-
const runner = (0,
|
|
3901
|
+
const runner = (0, import_core23.runnerForProvider)(provider);
|
|
3839
3902
|
if (!runner) {
|
|
3840
3903
|
fail(
|
|
3841
3904
|
"Thinking effort applies to a coding-agent planner.",
|
|
@@ -3847,7 +3910,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
3847
3910
|
if (!wanted || wanted === "show") {
|
|
3848
3911
|
console.log(`Planner effort: ${currentEffort || "runner default"}`);
|
|
3849
3912
|
if (!model) {
|
|
3850
|
-
console.log(` No planner model selected for ${runner}
|
|
3913
|
+
console.log(` No planner model selected for ${runner}: pick one with \`ordewell model set <id>\`.`);
|
|
3851
3914
|
} else if (variants.length === 0) {
|
|
3852
3915
|
console.log(` ${model.label} exposes no effort levels; it always runs at the agent's default.`);
|
|
3853
3916
|
} else {
|
|
@@ -3875,7 +3938,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
3875
3938
|
}
|
|
3876
3939
|
|
|
3877
3940
|
// src/commands/parallel.ts
|
|
3878
|
-
var
|
|
3941
|
+
var import_core24 = require("@ordewell/core");
|
|
3879
3942
|
var USAGE5 = "Usage: ordewell parallel [<number of tasks, 1 or more>]";
|
|
3880
3943
|
async function handleParallel(subArgs, injectedApi) {
|
|
3881
3944
|
const [wanted] = positionals(subArgs);
|
|
@@ -3885,14 +3948,14 @@ async function handleParallel(subArgs, injectedApi) {
|
|
|
3885
3948
|
console.log(`Up to ${String(settings.maxParallel ?? "?")} AI tasks run at once. Change with: ordewell parallel <n>`);
|
|
3886
3949
|
return;
|
|
3887
3950
|
}
|
|
3888
|
-
const limit = (0,
|
|
3951
|
+
const limit = (0, import_core24.parseMaxParallel)(wanted);
|
|
3889
3952
|
if (limit === null) fail(`"${wanted}" is not a number of tasks (1 or more).`, USAGE5);
|
|
3890
3953
|
await persistEnv(api, { ORDEWELL_MAX_PARALLEL: String(limit) });
|
|
3891
3954
|
console.log(`Up to ${limit} AI task${limit === 1 ? "" : "s"} now run at once.`);
|
|
3892
3955
|
}
|
|
3893
3956
|
|
|
3894
3957
|
// src/commands/runSettings.ts
|
|
3895
|
-
var
|
|
3958
|
+
var import_core25 = require("@ordewell/core");
|
|
3896
3959
|
var SUPERVISOR_USAGE = "Usage: ordewell supervisor [off|watch|act] \xB7 ordewell supervisor retries <n> \xB7 ordewell supervisor reads <n>";
|
|
3897
3960
|
var AUTOMERGE_USAGE = "Usage: ordewell automerge [on|off]";
|
|
3898
3961
|
var AUTORUN_USAGE = "Usage: ordewell autorun [on|off]";
|
|
@@ -3903,28 +3966,28 @@ async function handleSupervisor(subArgs, injectedApi) {
|
|
|
3903
3966
|
const [arg, value] = positionals(subArgs);
|
|
3904
3967
|
let changes = null;
|
|
3905
3968
|
if (arg === "off" || arg === "watch" || arg === "act") changes = { supervisorLevel: arg };
|
|
3906
|
-
else if (arg === "retries") changes = maybe((0,
|
|
3907
|
-
else if (arg === "reads") changes = maybe((0,
|
|
3969
|
+
else if (arg === "retries") changes = maybe((0, import_core25.parseCount)(value, 0), (n) => ({ supervisorRetryLimit: n }));
|
|
3970
|
+
else if (arg === "reads") changes = maybe((0, import_core25.parseCount)(value, 1), (n) => ({ wakeUpReadBudget: n }));
|
|
3908
3971
|
if (arg !== void 0 && changes === null) fail(SUPERVISOR_USAGE);
|
|
3909
3972
|
const api = await connect(subArgs, injectedApi);
|
|
3910
3973
|
if (!changes) {
|
|
3911
|
-
console.log((0,
|
|
3974
|
+
console.log((0, import_core25.supervisorSummary)((await api.getSettings()).run));
|
|
3912
3975
|
console.log(` ${SUPERVISOR_USAGE}`);
|
|
3913
3976
|
return;
|
|
3914
3977
|
}
|
|
3915
3978
|
await save(api, changes);
|
|
3916
|
-
if (changes.supervisorLevel) console.log(`Supervisor: ${
|
|
3917
|
-
if (changes.supervisorLevel === "act") console.log(
|
|
3979
|
+
if (changes.supervisorLevel) console.log(`Supervisor: ${import_core25.SUPERVISOR_LEVEL_LABEL[changes.supervisorLevel]}, from each session's next run.`);
|
|
3980
|
+
if (changes.supervisorLevel === "act") console.log(import_core25.ACT_RISK);
|
|
3918
3981
|
if (changes.supervisorRetryLimit !== void 0) console.log(`Supervisor retry limit: ${changes.supervisorRetryLimit} per task.`);
|
|
3919
3982
|
if (changes.wakeUpReadBudget !== void 0) console.log(`Wake-up read budget: ${changes.wakeUpReadBudget} reads.`);
|
|
3920
3983
|
}
|
|
3921
3984
|
async function handleAutomerge(subArgs, injectedApi) {
|
|
3922
3985
|
const [arg] = positionals(subArgs);
|
|
3923
|
-
const enabled = arg === void 0 ? null : (0,
|
|
3986
|
+
const enabled = arg === void 0 ? null : (0, import_core25.parseSwitch)(arg);
|
|
3924
3987
|
if (arg !== void 0 && enabled === null) fail(AUTOMERGE_USAGE);
|
|
3925
3988
|
const api = await connect(subArgs, injectedApi);
|
|
3926
3989
|
if (enabled === null) {
|
|
3927
|
-
console.log((0,
|
|
3990
|
+
console.log((0, import_core25.automergeSummary)((await api.getSettings()).run));
|
|
3928
3991
|
console.log(` ${AUTOMERGE_USAGE}`);
|
|
3929
3992
|
return;
|
|
3930
3993
|
}
|
|
@@ -3933,24 +3996,24 @@ async function handleAutomerge(subArgs, injectedApi) {
|
|
|
3933
3996
|
}
|
|
3934
3997
|
async function handleAutorun(subArgs, injectedApi) {
|
|
3935
3998
|
const [arg] = positionals(subArgs);
|
|
3936
|
-
const enabled = arg === void 0 ? null : (0,
|
|
3999
|
+
const enabled = arg === void 0 ? null : (0, import_core25.parseSwitch)(arg);
|
|
3937
4000
|
if (arg !== void 0 && enabled === null) fail(AUTORUN_USAGE);
|
|
3938
4001
|
const api = await connect(subArgs, injectedApi);
|
|
3939
4002
|
if (enabled === null) {
|
|
3940
|
-
console.log((0,
|
|
4003
|
+
console.log((0, import_core25.autorunSummary)((await api.getSettings()).run));
|
|
3941
4004
|
console.log(` ${AUTORUN_USAGE}`);
|
|
3942
4005
|
return;
|
|
3943
4006
|
}
|
|
3944
4007
|
await save(api, { autorun: enabled });
|
|
3945
|
-
console.log(`Autorun: ${enabled ? "on
|
|
3946
|
-
if (enabled) console.log(
|
|
4008
|
+
console.log(`Autorun: ${enabled ? "on: each plan the planner submits starts at once" : "off: plans wait for your review"}.`);
|
|
4009
|
+
if (enabled) console.log(import_core25.AUTORUN_RISK);
|
|
3947
4010
|
}
|
|
3948
4011
|
function maybe(n, wrap2) {
|
|
3949
4012
|
return n === null ? null : wrap2(n);
|
|
3950
4013
|
}
|
|
3951
4014
|
|
|
3952
4015
|
// src/commands/key.ts
|
|
3953
|
-
var
|
|
4016
|
+
var import_core26 = require("@ordewell/core");
|
|
3954
4017
|
var USAGE6 = "Usage: ordewell key [list | set <provider> <key>]";
|
|
3955
4018
|
async function handleKey(subArgs, injectedApi) {
|
|
3956
4019
|
const [action, provider, ...keyParts] = positionals(subArgs);
|
|
@@ -3961,8 +4024,8 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
3961
4024
|
if (action !== "set") {
|
|
3962
4025
|
const catalog = await fetchCatalog(api);
|
|
3963
4026
|
console.log("\nAPI provider keys\n");
|
|
3964
|
-
for (const id2 of
|
|
3965
|
-
const meta2 = (0,
|
|
4027
|
+
for (const id2 of import_core26.PROVIDER_PRIORITY) {
|
|
4028
|
+
const meta2 = (0, import_core26.getProviderMeta)(id2);
|
|
3966
4029
|
const configured = catalog.providers.includes(id2);
|
|
3967
4030
|
console.log(` ${configured ? "\u2713" : " "} ${id2.padEnd(14)} ${meta2.apiKeyEnvVar.padEnd(28)} ${meta2.label}`);
|
|
3968
4031
|
}
|
|
@@ -3972,10 +4035,10 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
3972
4035
|
}
|
|
3973
4036
|
if (!provider) fail(USAGE6);
|
|
3974
4037
|
const id = provider.toLowerCase();
|
|
3975
|
-
if (!(0,
|
|
4038
|
+
if (!(0, import_core26.isApiProvider)(id)) {
|
|
3976
4039
|
fail(`Unknown provider: ${provider}`, "Run `ordewell key` to see the list.");
|
|
3977
4040
|
}
|
|
3978
|
-
const meta = (0,
|
|
4041
|
+
const meta = (0, import_core26.getProviderMeta)(id);
|
|
3979
4042
|
const key = keyParts.join(" ").trim();
|
|
3980
4043
|
if (!key) fail(`Usage: ordewell key set ${id} <key>`);
|
|
3981
4044
|
await persistEnv(api, { [meta.apiKeyEnvVar]: key });
|
|
@@ -3983,7 +4046,7 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
3983
4046
|
}
|
|
3984
4047
|
|
|
3985
4048
|
// src/commands/runners.ts
|
|
3986
|
-
var
|
|
4049
|
+
var import_core27 = require("@ordewell/core");
|
|
3987
4050
|
var USAGE7 = "Usage: ordewell runners [<runner-id> on|off]";
|
|
3988
4051
|
async function handleRunners(subArgs, injectedApi) {
|
|
3989
4052
|
const [runner, toggle] = positionals(subArgs);
|
|
@@ -3994,7 +4057,7 @@ async function handleRunners(subArgs, injectedApi) {
|
|
|
3994
4057
|
for (const r of state2.runners) {
|
|
3995
4058
|
console.log(` ${r.enabled ? "\u2713" : " "} ${r.id.padEnd(14)} ${r.name}${r.enabled ? "" : " (disabled)"}`);
|
|
3996
4059
|
}
|
|
3997
|
-
const notice = (0,
|
|
4060
|
+
const notice = (0, import_core27.codexSandboxNotice)((await fetchCatalog(api).catch(() => null))?.modesByRunner);
|
|
3998
4061
|
if (notice) console.log(`
|
|
3999
4062
|
${notice.line}
|
|
4000
4063
|
${notice.docs}`);
|
|
@@ -4015,17 +4078,17 @@ async function handleRunners(subArgs, injectedApi) {
|
|
|
4015
4078
|
var AUTO_USAGE = "Usage: ordewell auto [full|guarded]";
|
|
4016
4079
|
function handleAuto(subArgs) {
|
|
4017
4080
|
const [arg] = positionals(subArgs);
|
|
4018
|
-
const settings = new
|
|
4081
|
+
const settings = new import_core27.SettingsService();
|
|
4019
4082
|
const current = settings.autonomousMode();
|
|
4020
4083
|
if (!arg) {
|
|
4021
|
-
console.log(`Autonomy level: ${(0,
|
|
4084
|
+
console.log(`Autonomy level: ${(0, import_core27.autonomyLevelLabel)(current)}`);
|
|
4022
4085
|
console.log(` ${AUTO_USAGE}`);
|
|
4023
4086
|
return;
|
|
4024
4087
|
}
|
|
4025
|
-
const enabled = (0,
|
|
4088
|
+
const enabled = (0, import_core27.parseAutonomyLevel)(arg);
|
|
4026
4089
|
if (enabled === null) fail(AUTO_USAGE);
|
|
4027
4090
|
settings.setAutonomousMode(enabled);
|
|
4028
|
-
console.log(settings.autonomousModeOverridden() ? `${(0,
|
|
4091
|
+
console.log(settings.autonomousModeOverridden() ? `${(0, import_core27.autonomyLevelLabel)(enabled)} is saved, but ORDEWELL_AUTONOMOUS_MODE in the environment decides the level until it is unset.` : `Autonomy level: ${(0, import_core27.autonomyLevelLabel)(enabled)} for new plans.`);
|
|
4029
4092
|
}
|
|
4030
4093
|
async function handleRefresh(subArgs, injectedApi) {
|
|
4031
4094
|
const api = await connect(subArgs, injectedApi);
|
|
@@ -4050,12 +4113,12 @@ async function handleApprove(subArgs, injectedApi) {
|
|
|
4050
4113
|
console.error(`Failed to approve: ${err.message}`);
|
|
4051
4114
|
process.exit(1);
|
|
4052
4115
|
}
|
|
4053
|
-
console.error("Plan approved
|
|
4116
|
+
console.error("Plan approved: continuing execution.");
|
|
4054
4117
|
});
|
|
4055
4118
|
}
|
|
4056
4119
|
|
|
4057
4120
|
// src/commands/task-assign.ts
|
|
4058
|
-
var
|
|
4121
|
+
var import_core29 = require("@ordewell/core");
|
|
4059
4122
|
var import_plan_utils = require("@ordewell/core/plan-utils");
|
|
4060
4123
|
|
|
4061
4124
|
// src/tui/taskAssignment.ts
|
|
@@ -4095,7 +4158,7 @@ function modesForTask(modesByRunner, task) {
|
|
|
4095
4158
|
}
|
|
4096
4159
|
|
|
4097
4160
|
// src/tui/state.ts
|
|
4098
|
-
var
|
|
4161
|
+
var import_core28 = require("@ordewell/core");
|
|
4099
4162
|
|
|
4100
4163
|
// src/tui/editor.ts
|
|
4101
4164
|
function emptyEditor() {
|
|
@@ -4253,7 +4316,7 @@ function queuedTarget(tv) {
|
|
|
4253
4316
|
for (let i = queued.length - 1; i >= 0; i--) if (!queued[i].forced) return i;
|
|
4254
4317
|
return -1;
|
|
4255
4318
|
}
|
|
4256
|
-
var anyTaskRunning = (state) => state.tasks.some(
|
|
4319
|
+
var anyTaskRunning = (state) => state.tasks.some(import_core28.isTaskRunning);
|
|
4257
4320
|
var plannerInFlight = (state) => state.planner !== "idle";
|
|
4258
4321
|
var planPaneOpen = (state) => state.focus === "plan" && !state.overlay && (!!state.taskEditor || !!state.expandedTaskId);
|
|
4259
4322
|
function planRows(state) {
|
|
@@ -4299,10 +4362,10 @@ function findTask(tasks, id) {
|
|
|
4299
4362
|
return void 0;
|
|
4300
4363
|
}
|
|
4301
4364
|
function refFor(state, task) {
|
|
4302
|
-
return (0,
|
|
4365
|
+
return (0, import_core28.taskRefOf)(state.tasks, task.id) ?? (0, import_core28.taskRef)(task);
|
|
4303
4366
|
}
|
|
4304
4367
|
function titledRefFor(state, task) {
|
|
4305
|
-
return (0,
|
|
4368
|
+
return (0, import_core28.titledTaskRefOf)(state.tasks, task.id) ?? (0, import_core28.titledTaskRef)(task);
|
|
4306
4369
|
}
|
|
4307
4370
|
function asRunTasks(tasks) {
|
|
4308
4371
|
return tasks.map((task) => ({ ...task, subtasks: asRunTasks(task.subtasks ?? []) }));
|
|
@@ -4310,7 +4373,7 @@ function asRunTasks(tasks) {
|
|
|
4310
4373
|
function seedRun(overrides) {
|
|
4311
4374
|
if (overrides.run) return overrides.run;
|
|
4312
4375
|
return {
|
|
4313
|
-
...
|
|
4376
|
+
...import_core28.EMPTY_RUN,
|
|
4314
4377
|
tasks: asRunTasks(overrides.tasks ?? []),
|
|
4315
4378
|
handoff: overrides.handoff ?? null,
|
|
4316
4379
|
runMerge: overrides.runMerge ? { waitingAtGate: overrides.runMerge.waitingAtGate, paused: overrides.runMerge.paused, repos: overrides.runMerge.handoff.repos, landed: overrides.runMerge.handoff.landed } : null
|
|
@@ -4319,9 +4382,9 @@ function seedRun(overrides) {
|
|
|
4319
4382
|
function initialState(overrides = {}) {
|
|
4320
4383
|
return {
|
|
4321
4384
|
editor: emptyEditor(),
|
|
4322
|
-
conversation:
|
|
4385
|
+
conversation: import_core28.EMPTY_CONVERSATION,
|
|
4323
4386
|
taskView: null,
|
|
4324
|
-
turnGate:
|
|
4387
|
+
turnGate: import_core28.NO_TURN,
|
|
4325
4388
|
detailAll: false,
|
|
4326
4389
|
status: "idle",
|
|
4327
4390
|
planner: "idle",
|
|
@@ -4347,11 +4410,15 @@ function initialState(overrides = {}) {
|
|
|
4347
4410
|
plannerProvider: "",
|
|
4348
4411
|
plannerEffort: "",
|
|
4349
4412
|
plannerModels: {},
|
|
4350
|
-
maxParallel:
|
|
4413
|
+
maxParallel: import_core28.DEFAULT_MAX_PARALLEL,
|
|
4351
4414
|
configuredProviders: [],
|
|
4352
4415
|
allowlist: {},
|
|
4353
4416
|
autonomous: true,
|
|
4354
|
-
runSettings: { ...
|
|
4417
|
+
runSettings: { ...import_core28.RUN_SETTINGS_DEFAULTS },
|
|
4418
|
+
runSettingsKnown: false,
|
|
4419
|
+
plannerKnown: false,
|
|
4420
|
+
runnersKnown: false,
|
|
4421
|
+
providersKnown: false,
|
|
4355
4422
|
runAutoMerge: null,
|
|
4356
4423
|
runSupervisor: null,
|
|
4357
4424
|
mouseCapture: true,
|
|
@@ -4361,7 +4428,7 @@ function initialState(overrides = {}) {
|
|
|
4361
4428
|
runMerge: null,
|
|
4362
4429
|
overlay: null,
|
|
4363
4430
|
pendingApprovals: [],
|
|
4364
|
-
queuedPrompts:
|
|
4431
|
+
queuedPrompts: import_core28.EMPTY_HOLD,
|
|
4365
4432
|
stopArmed: false,
|
|
4366
4433
|
stopArmToken: 0,
|
|
4367
4434
|
stopRequested: false,
|
|
@@ -4411,7 +4478,7 @@ Runner \xB7 ${namedOf(tasks, task)}
|
|
|
4411
4478
|
fail(`Unknown runner: ${runner}`, `This daemon knows: ${state.runners.map((r) => r.id).join(", ")}`);
|
|
4412
4479
|
}
|
|
4413
4480
|
await api.updateTask(sessionId, task.id, { assignedRunner: runner });
|
|
4414
|
-
console.log(`Task ${refOf(tasks, task)} runner set to ${runner}
|
|
4481
|
+
console.log(`Task ${refOf(tasks, task)} runner set to ${runner}: its model, effort and mode were re-picked for it.`);
|
|
4415
4482
|
});
|
|
4416
4483
|
}
|
|
4417
4484
|
var MODEL_USAGE = "Usage: ordewell task-model <task-id-or-order> [<model-id>] [--session-id <id>]";
|
|
@@ -4423,11 +4490,11 @@ async function handleTaskModel(subArgs, injectedApi) {
|
|
|
4423
4490
|
console.log(`
|
|
4424
4491
|
Model \xB7 ${namedOf(tasks, task)}${task.assignedRunner ? ` (${task.assignedRunner})` : ""}
|
|
4425
4492
|
`);
|
|
4426
|
-
if (options.length === 0) console.log(" No models discovered for this runner
|
|
4493
|
+
if (options.length === 0) console.log(" No models discovered for this runner: try `ordewell refresh`.");
|
|
4427
4494
|
for (const m of options) {
|
|
4428
4495
|
const detail = [m.provider, m.variants?.length ? `${m.variants.length} effort levels` : "runner default effort"].filter(Boolean).join(" \xB7 ");
|
|
4429
4496
|
console.log(` ${m.id === task.assignedModel?.modelId ? "*" : " "} ${m.id}`);
|
|
4430
|
-
console.log(` ${m.label}
|
|
4497
|
+
console.log(` ${m.label}: ${detail}`);
|
|
4431
4498
|
}
|
|
4432
4499
|
console.log(`
|
|
4433
4500
|
${MODEL_USAGE}`);
|
|
@@ -4493,9 +4560,9 @@ Mode \xB7 ${namedOf(tasks, task)}${task.assignedRunner ? ` (${task.assignedRunne
|
|
|
4493
4560
|
console.log(" This task has no runner, or its runner declares no modes.");
|
|
4494
4561
|
}
|
|
4495
4562
|
for (const m of modes) {
|
|
4496
|
-
console.log(` ${m.id === task.taskMode ? "*" : " "} ${m.id.padEnd(14)} ${m.label}${m.unavailable ? "
|
|
4563
|
+
console.log(` ${m.id === task.taskMode ? "*" : " "} ${m.id.padEnd(14)} ${m.label}${m.unavailable ? ": unavailable on this host" : m.description ? `: ${m.description}` : ""}`);
|
|
4497
4564
|
}
|
|
4498
|
-
const notice = (0,
|
|
4565
|
+
const notice = (0, import_core29.codexSandboxNotice)(catalog.modesByRunner);
|
|
4499
4566
|
if (notice) console.log(`
|
|
4500
4567
|
${notice.line}
|
|
4501
4568
|
${notice.docs}`);
|
|
@@ -4544,7 +4611,7 @@ var SKILLS_USAGE = "Usage: ordewell task-skills <task-id-or-order> [<name> [<nam
|
|
|
4544
4611
|
async function handleTaskSkills(subArgs, injectedApi) {
|
|
4545
4612
|
await withTask(subArgs, SKILLS_USAGE, injectedApi, async (api, sessionId, task, tasks, value) => {
|
|
4546
4613
|
if (task.type !== "ai") fail("Manual tasks do not run an executor, so they take no skills.");
|
|
4547
|
-
const catalog = (0,
|
|
4614
|
+
const catalog = (0, import_core29.createSkillsService)(flag(subArgs, "--workspace") || process.cwd(), new import_core29.EnvConfig().workspaceRepos).listSkills().filter((s) => s.appliesTo === "task");
|
|
4548
4615
|
if (!value) {
|
|
4549
4616
|
console.log(`
|
|
4550
4617
|
Skills \xB7 ${namedOf(tasks, task)}
|
|
@@ -4581,7 +4648,7 @@ ${namedOf(tasks, task)} is ${task.ops ? "an ops task: it runs in your checkout o
|
|
|
4581
4648
|
}
|
|
4582
4649
|
const to = value.toLowerCase();
|
|
4583
4650
|
if (to !== "on" && to !== "off") fail(OPS_USAGE);
|
|
4584
|
-
if (task.type !== "ai") fail("Only an AI task can be an ops task
|
|
4651
|
+
if (task.type !== "ai") fail("Only an AI task can be an ops task: a manual task already runs outside any worktree.");
|
|
4585
4652
|
if (!tasks.some((t) => t.id === task.id)) fail("A subtask runs with its parent; make the parent an ops task instead.");
|
|
4586
4653
|
await api.updateTask(sessionId, task.id, { ops: to === "on" });
|
|
4587
4654
|
console.log(to === "on" ? `Task ${refOf(tasks, task)} is an ops task: it runs in your checkout once the work it depends on is merged.` : `Task ${refOf(tasks, task)} is a change task: it runs in its own worktree.`);
|
|
@@ -4589,10 +4656,10 @@ ${namedOf(tasks, task)} is ${task.ops ? "an ops task: it runs in your checkout o
|
|
|
4589
4656
|
}
|
|
4590
4657
|
|
|
4591
4658
|
// src/tui/index.ts
|
|
4592
|
-
var
|
|
4659
|
+
var import_core51 = require("@ordewell/core");
|
|
4593
4660
|
|
|
4594
4661
|
// src/tui/slash.ts
|
|
4595
|
-
var
|
|
4662
|
+
var import_core30 = require("@ordewell/core");
|
|
4596
4663
|
var SLASH_COMMANDS = [
|
|
4597
4664
|
// Planning. There is deliberately no `/plan`: typing the goal *is* the way to
|
|
4598
4665
|
// start planning, and an alias for it only shadowed `/planner` on completion.
|
|
@@ -4618,9 +4685,9 @@ var SLASH_COMMANDS = [
|
|
|
4618
4685
|
{ name: "task-mode", usage: "/task-mode <id> [mode]", description: "Choose or set a task's runner mode", category: "tasks" },
|
|
4619
4686
|
{ name: "task-deps", usage: "/task-deps <id>", description: "Edit which earlier tasks a task waits for", category: "tasks" },
|
|
4620
4687
|
{ name: "task-skills", usage: "/task-skills <id> [a,b|none]", description: "Choose or set the skills attached to a task; none clears them", category: "tasks" },
|
|
4621
|
-
{ name: "task-ops", usage: "/task-ops <id> [on|off]", description: "
|
|
4688
|
+
{ name: "task-ops", usage: "/task-ops <id> [on|off]", description: "Choose between an ops task (runs in your checkout once its dependencies are merged) and a change task", category: "tasks" },
|
|
4622
4689
|
// Models & providers
|
|
4623
|
-
{ name: "planner", usage: "/planner [<provider>]", description: "Pick the planner
|
|
4690
|
+
{ name: "planner", usage: "/planner [<provider>]", description: "Pick the planner: harness, model and effort in one go", category: "models" },
|
|
4624
4691
|
{ name: "model", usage: "/model [set <id>]", description: "Show or set the planner model", category: "models" },
|
|
4625
4692
|
{ name: "planner-effort", usage: "/planner-effort [<level>]", description: "Thinking effort for a coding-agent planner", category: "models" },
|
|
4626
4693
|
{ name: "key", usage: "/key [set <provider> <key>]", description: "Configure API provider keys", category: "models" },
|
|
@@ -4711,7 +4778,7 @@ function parseRunnerCommand(text) {
|
|
|
4711
4778
|
return match ? { name: match[1], args: (match[2] ?? "").trim() } : null;
|
|
4712
4779
|
}
|
|
4713
4780
|
function runnerCommandFor(commands, name) {
|
|
4714
|
-
return (0,
|
|
4781
|
+
return (0, import_core30.findRunnerCommand)(commands, name) ?? (0, import_core30.findRunnerCommand)(commands, name.toLowerCase());
|
|
4715
4782
|
}
|
|
4716
4783
|
function composerCompletions(token, runner) {
|
|
4717
4784
|
if (runner) return token.start === 0 ? runner.filter((c) => c.name.toLowerCase().startsWith(token.name)).map((c) => c.name) : [];
|
|
@@ -4758,10 +4825,10 @@ function chatEditorRoomFor(state, active) {
|
|
|
4758
4825
|
}
|
|
4759
4826
|
|
|
4760
4827
|
// src/tui/layout.ts
|
|
4761
|
-
var
|
|
4828
|
+
var import_core33 = require("@ordewell/core");
|
|
4762
4829
|
|
|
4763
4830
|
// src/tui/reducers/pickers.ts
|
|
4764
|
-
var
|
|
4831
|
+
var import_core31 = require("@ordewell/core");
|
|
4765
4832
|
var import_plan_utils2 = require("@ordewell/core/plan-utils");
|
|
4766
4833
|
var DEFAULT_EFFORT = "__runner_default__";
|
|
4767
4834
|
function picker(title, items, action, extra = {}) {
|
|
@@ -4780,11 +4847,11 @@ function modelDetail(m) {
|
|
|
4780
4847
|
function providerErrorHint(state) {
|
|
4781
4848
|
const failed = Object.keys(state.providerErrors ?? {});
|
|
4782
4849
|
if (failed.length === 0) return void 0;
|
|
4783
|
-
const names = failed.map((p) => (0,
|
|
4784
|
-
return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")}
|
|
4850
|
+
const names = failed.map((p) => (0, import_core31.getProviderMeta)(p)?.label ?? p);
|
|
4851
|
+
return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")}. Showing working providers only.`;
|
|
4785
4852
|
}
|
|
4786
4853
|
function plannerModelItems(state, provider, selectedId) {
|
|
4787
|
-
const runner = (0,
|
|
4854
|
+
const runner = (0, import_core31.runnerForProvider)(provider);
|
|
4788
4855
|
if (!runner) {
|
|
4789
4856
|
return state.orchestratorModels.map((m) => ({ id: m.id, label: m.label, detail: modelDetail(m), selected: m.id === selectedId }));
|
|
4790
4857
|
}
|
|
@@ -4800,11 +4867,11 @@ function plannerModelItems(state, provider, selectedId) {
|
|
|
4800
4867
|
return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
|
|
4801
4868
|
}
|
|
4802
4869
|
function plannerHarnessModel(state, provider, modelId) {
|
|
4803
|
-
const runner = (0,
|
|
4870
|
+
const runner = (0, import_core31.runnerForProvider)(provider);
|
|
4804
4871
|
return runner ? state.models.find((m) => m.id === modelId && m.runners?.includes(runner)) : void 0;
|
|
4805
4872
|
}
|
|
4806
4873
|
function plannerEffortItems(state, provider, modelId, selectedEffort) {
|
|
4807
|
-
if (!(0,
|
|
4874
|
+
if (!(0, import_core31.runnerForProvider)(provider)) {
|
|
4808
4875
|
return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
|
|
4809
4876
|
}
|
|
4810
4877
|
const model = plannerHarnessModel(state, provider, modelId);
|
|
@@ -4922,19 +4989,21 @@ function pickerItemsFor(state, action) {
|
|
|
4922
4989
|
function plannerItems(state) {
|
|
4923
4990
|
const installed = new Set(state.runners.map((r) => r.id));
|
|
4924
4991
|
const usability = {};
|
|
4925
|
-
for (const id of
|
|
4926
|
-
const runner = (0,
|
|
4992
|
+
for (const id of import_core31.CLI_PROVIDERS) {
|
|
4993
|
+
const runner = (0, import_core31.runnerForProvider)(id);
|
|
4927
4994
|
usability[runner] = installed.has(runner) ? { usable: true } : { usable: false, reason: "CLI not installed or not on PATH" };
|
|
4928
4995
|
}
|
|
4929
|
-
const
|
|
4930
|
-
|
|
4996
|
+
const offered2 = (0, import_core31.plannerBackendEntries)(usability, state.configuredProviders).filter((entry) => entry.usable);
|
|
4997
|
+
if (offered2.length === 0) {
|
|
4998
|
+
return [{ id: "", label: "No planner available", detail: "install a coding agent or add a key with /key", disabled: true }];
|
|
4999
|
+
}
|
|
5000
|
+
const items = offered2.map((entry) => ({
|
|
4931
5001
|
id: entry.id,
|
|
4932
5002
|
label: entry.label,
|
|
4933
5003
|
detail: entry.reason,
|
|
4934
|
-
selected: state.plannerProvider === entry.id
|
|
4935
|
-
disabled: !entry.usable
|
|
5004
|
+
selected: state.plannerProvider === entry.id
|
|
4936
5005
|
}));
|
|
4937
|
-
return
|
|
5006
|
+
return offered2.some((entry) => entry.kind === "vendor") ? items : [...items, { id: "", label: "No API providers configured", detail: "add a key with /key", disabled: true }];
|
|
4938
5007
|
}
|
|
4939
5008
|
|
|
4940
5009
|
// src/version.ts
|
|
@@ -4951,14 +5020,14 @@ function cliVersion() {
|
|
|
4951
5020
|
}
|
|
4952
5021
|
|
|
4953
5022
|
// src/tui/planKeys.ts
|
|
4954
|
-
var
|
|
5023
|
+
var import_core32 = require("@ordewell/core");
|
|
4955
5024
|
var PLAN_KEYS = {
|
|
4956
5025
|
move: { keys: "up / down", meaning: "move between tasks", group: "Navigation" },
|
|
4957
5026
|
scroll: { keys: "pgup / pgdn", meaning: "scroll the plan", group: "Navigation" },
|
|
4958
5027
|
expand: { keys: "\u2192", meaning: "reveal a task's subtasks, then edit its prompt", group: "Navigation", hint: "\u2192 expand" },
|
|
4959
5028
|
log: {
|
|
4960
5029
|
keys: "enter",
|
|
4961
|
-
meaning: "open the task's log and message its runner
|
|
5030
|
+
meaning: "open the task's log and message its runner or, at a checkpoint, answer it",
|
|
4962
5031
|
group: "Navigation",
|
|
4963
5032
|
hint: (task) => task?.status === "awaiting_user" && task.awaitingReason === "checkpoint" ? "enter answer" : "enter log"
|
|
4964
5033
|
},
|
|
@@ -4978,7 +5047,7 @@ var PLAN_KEYS = {
|
|
|
4978
5047
|
meaning: "cancel the task's runner, or keep a task paused on a usage limit from resuming on its own",
|
|
4979
5048
|
group: "Run",
|
|
4980
5049
|
// `c` cancels the resume while the task has one; it has no runner to cancel then.
|
|
4981
|
-
hint: (task) => task && (0,
|
|
5050
|
+
hint: (task) => task && (0, import_core32.resumeLabel)(task) ? "c don't resume" : "c cancel"
|
|
4982
5051
|
},
|
|
4983
5052
|
resume: { keys: "r", meaning: "resume a task paused on a usage limit now, once the limit has reset", group: "Run", hint: "r resume now" },
|
|
4984
5053
|
resolve: { keys: "x", meaning: "resolve a merge conflict", group: "Run", hint: "x resolve conflict" },
|
|
@@ -4988,7 +5057,7 @@ var PLAN_KEYS = {
|
|
|
4988
5057
|
meaning: "mark the task done, or take it back",
|
|
4989
5058
|
group: "Status",
|
|
4990
5059
|
// `m` toggles, so the hint names the direction it will go for this task.
|
|
4991
|
-
hint: (task) => task && (0,
|
|
5060
|
+
hint: (task) => task && (0, import_core32.markAction)(task) === "uncomplete" ? "m undone" : "m done"
|
|
4992
5061
|
},
|
|
4993
5062
|
skip: { keys: "s", meaning: "skip the task: none of its work lands, and what depends on it goes ahead", group: "Status", hint: "s skip" },
|
|
4994
5063
|
remove: { keys: "d", meaning: "remove the task", group: "Status" },
|
|
@@ -5015,7 +5084,7 @@ function hintOf(id, task) {
|
|
|
5015
5084
|
}
|
|
5016
5085
|
function taskKeyIds(task, runActive) {
|
|
5017
5086
|
if (!task) return ["add"];
|
|
5018
|
-
const row = (0,
|
|
5087
|
+
const row = (0, import_core32.taskRowView)(task);
|
|
5019
5088
|
if (row.conflict) return ["resolve", "log"];
|
|
5020
5089
|
if (row.actions.includes("resume-now") || row.resumes) {
|
|
5021
5090
|
return [...row.actions.includes("resume-now") ? ["resume"] : [], ...row.resumes ? ["cancel"] : [], "skip", "log"];
|
|
@@ -5043,7 +5112,7 @@ function planFooterHints(task, runActive) {
|
|
|
5043
5112
|
}
|
|
5044
5113
|
|
|
5045
5114
|
// src/tui/layout.ts
|
|
5046
|
-
var
|
|
5115
|
+
var import_core34 = require("@ordewell/core");
|
|
5047
5116
|
var CHROME_ROWS = 1;
|
|
5048
5117
|
function chatInputWrap(state) {
|
|
5049
5118
|
const active = state.focus === "chat" && !state.overlay;
|
|
@@ -5069,7 +5138,7 @@ function footerHints(state) {
|
|
|
5069
5138
|
}
|
|
5070
5139
|
return [...escHint ? [escHint] : [], ...planFooterHints(selected, state.status === "executing")];
|
|
5071
5140
|
}
|
|
5072
|
-
const detailHint = (0,
|
|
5141
|
+
const detailHint = (0, import_core34.hasHiddenDetail)(state.taskView ? state.taskView.view.blocks : state.conversation.blocks) ? [state.detailAll ? "ctrl-o collapse all" : "ctrl-o expand all"] : [];
|
|
5073
5142
|
return [
|
|
5074
5143
|
"/help",
|
|
5075
5144
|
...checkpointHints,
|
|
@@ -5128,12 +5197,12 @@ function queuedTaskBubble(message, target, sends, cols) {
|
|
|
5128
5197
|
}
|
|
5129
5198
|
function taskActivity(task, working) {
|
|
5130
5199
|
if (task?.status === "completed" || task?.status === "failed") return task.status;
|
|
5131
|
-
const approvals = (0,
|
|
5200
|
+
const approvals = (0, import_core34.approvalLabel)(task?.awaitingApproval);
|
|
5132
5201
|
if (approvals) return inRow(approvals);
|
|
5133
5202
|
if (working) return "working";
|
|
5134
5203
|
if (task?.status === "awaiting_user") {
|
|
5135
|
-
const resumes = (0,
|
|
5136
|
-
return [inRow((0,
|
|
5204
|
+
const resumes = (0, import_core34.resumeLabel)(task);
|
|
5205
|
+
return [inRow((0, import_core34.awaitingLabel)(task) ?? "Waiting for your input"), resumes ? inRow(resumes) : ""].filter(Boolean).join(" \xB7 ");
|
|
5137
5206
|
}
|
|
5138
5207
|
return task?.status ?? "";
|
|
5139
5208
|
}
|
|
@@ -5146,7 +5215,7 @@ function taskHeaderLines(state, tv, cols) {
|
|
|
5146
5215
|
taskActivity(task, tv.view.working)
|
|
5147
5216
|
].filter(Boolean);
|
|
5148
5217
|
const approval = waitingApproval(tv);
|
|
5149
|
-
const hint = approval ? ["ctrl-y allow", ...approval.allowForTask ? ["ctrl-t allow for task"] : [], "ctrl-g deny (composer text is the note)"] : waitingCheckpoint(task) ? ["enter reply", "ctrl-y approve", "ctrl-g reject", "ctrl-x stop runner"] : task && (0,
|
|
5218
|
+
const hint = approval ? ["ctrl-y allow", ...approval.allowForTask ? ["ctrl-t allow for task"] : [], "ctrl-g deny (composer text is the note)"] : waitingCheckpoint(task) ? ["enter reply", "ctrl-y approve", "ctrl-g reject", "ctrl-x stop runner"] : task && (0, import_core34.resumeLabel)(task) ? ["ctrl-x don't resume"] : [...runnerStoppable(state) ? ["ctrl-x stop runner"] : [], "ctrl-r remove queued", "ctrl-s send now"];
|
|
5150
5219
|
if (approval && waitingCheckpoint(task)) hint.push("then the checkpoint");
|
|
5151
5220
|
const index = tv.attempts.indexOf(tv.attempt);
|
|
5152
5221
|
if (tv.attempts.length > 1) hint.push(`alt\u2190/\u2192 attempt ${index >= 0 ? index + 1 : 1}/${tv.attempts.length}`);
|
|
@@ -5176,15 +5245,15 @@ function chatActivity(state) {
|
|
|
5176
5245
|
const own = findTask(state.tasks, tv.taskId);
|
|
5177
5246
|
const task = state.tasks.find((t) => t.subtasks?.some((s) => s.id === tv.taskId)) ?? own;
|
|
5178
5247
|
if (!task) return null;
|
|
5179
|
-
if ((0,
|
|
5248
|
+
if ((0, import_core28.isTaskRunning)(task) || own && (0, import_core28.isTaskRunning)(own)) return { label: "Working\u2026", busy: true };
|
|
5180
5249
|
const at = task.status === "awaiting_user" && task.awaitingReason === "usage-limit" && task.resumeAt ? new Date(task.resumeAt) : null;
|
|
5181
|
-
if (at && !Number.isNaN(at.getTime())) return { label: `paused \xB7 resumes ${(0,
|
|
5250
|
+
if (at && !Number.isNaN(at.getTime())) return { label: `paused \xB7 resumes ${(0, import_core34.resumeTime)(at)}`, busy: false };
|
|
5182
5251
|
return null;
|
|
5183
5252
|
}
|
|
5184
5253
|
function runHints(state) {
|
|
5185
5254
|
const waiting = state.tasks.filter((t) => t.status === "awaiting_user").length;
|
|
5186
5255
|
if (waiting > 0) return [waiting === 1 ? "1 task waits for you" : `${waiting} tasks wait for you`, `${waiting} waiting`];
|
|
5187
|
-
return state.runMerge?.paused ? ["paused for Merge all
|
|
5256
|
+
return state.runMerge?.paused ? ["paused for Merge all: /handoff merge", "paused \xB7 /handoff merge", "merge paused"] : [];
|
|
5188
5257
|
}
|
|
5189
5258
|
var runHint = (state) => runHints(state)[0] ?? "";
|
|
5190
5259
|
function packHints(hints, cols) {
|
|
@@ -5240,7 +5309,7 @@ function taskUsageFooter(state, tv, blocks2, cols) {
|
|
|
5240
5309
|
const reported = task?.attemptUsage ?? [];
|
|
5241
5310
|
const onScreen = tv.attempt > 0 ? reported.find((a) => a.attempt === tv.attempt) : reported.at(-1);
|
|
5242
5311
|
if (!task || !onScreen) return tokenRow(blocks2, cols);
|
|
5243
|
-
const usage = (0,
|
|
5312
|
+
const usage = (0, import_core34.taskRowView)({ ...task, attemptUsage: [onScreen] }).usage;
|
|
5244
5313
|
if (!usage) return tokenRow(blocks2, cols);
|
|
5245
5314
|
const models = usage.byModel ?? [];
|
|
5246
5315
|
return [
|
|
@@ -5253,17 +5322,17 @@ function chatScrollMax(state) {
|
|
|
5253
5322
|
}
|
|
5254
5323
|
function plannerSetup(state) {
|
|
5255
5324
|
const provider = state.plannerProvider;
|
|
5256
|
-
const registration = (0,
|
|
5325
|
+
const registration = (0, import_core34.getProviderMeta)(provider);
|
|
5257
5326
|
if (!registration) return { label: "not set", ready: false };
|
|
5258
|
-
const runner = (0,
|
|
5259
|
-
const ready = runner ? state.runners.some((r) => r.id === runner) : (0,
|
|
5327
|
+
const runner = (0, import_core34.runnerForProvider)(provider);
|
|
5328
|
+
const ready = runner ? state.runners.some((r) => r.id === runner) : (0, import_core34.isApiProvider)(provider) && state.configuredProviders.includes(provider);
|
|
5260
5329
|
return { label: registration.label, ready };
|
|
5261
5330
|
}
|
|
5262
5331
|
var SETUP_KEY_COLS = 10;
|
|
5263
5332
|
var SETUP_VALUE_COLS = 32;
|
|
5264
5333
|
function runSettingsValue(state) {
|
|
5265
5334
|
const { supervisorLevel, mergeAtGatesAutomatically, autorun } = state.runSettings;
|
|
5266
|
-
const level = `supervisor ${
|
|
5335
|
+
const level = `supervisor ${import_core33.SUPERVISOR_LEVEL_LABEL[supervisorLevel]}`;
|
|
5267
5336
|
return [
|
|
5268
5337
|
supervisorLevel === "act" ? style.yellow(level) : level,
|
|
5269
5338
|
`auto-merge ${mergeAtGatesAutomatically ? "on" : "off"}`,
|
|
@@ -5343,18 +5412,22 @@ var twoLines = ({ orde, well }) => [...orde, ...well.map((row) => style.brand(ro
|
|
|
5343
5412
|
function welcomeLogo(restRows, rows, cols) {
|
|
5344
5413
|
return [bannerLines(), oneLine2(BANNER_WORDMARK), twoLines(BANNER_WORDMARK)].find((lines) => lines.every((line) => width(line) <= cols - 1) && lines.length + restRows <= rows) ?? [];
|
|
5345
5414
|
}
|
|
5415
|
+
var DETECTING = "Detecting coding agents\u2026";
|
|
5346
5416
|
function welcomeLines(state, rows, cols) {
|
|
5347
5417
|
const planner = plannerSetup(state);
|
|
5348
5418
|
const enabledRunners = state.runners.filter((r) => r.enabled).map((r) => r.name);
|
|
5349
5419
|
const canPlan = planner.ready || state.runners.length > 0 || state.configuredProviders.length > 0;
|
|
5350
|
-
const
|
|
5420
|
+
const detecting = !state.plannerKnown || !state.runnersKnown;
|
|
5421
|
+
const plannerValue = detecting ? style.grey(DETECTING) : planner.ready ? [planner.label, state.orchestratorModel || style.yellow("no model"), plannerEffortLabel(state)].filter(Boolean).join(style.grey(" \xB7 ")) : style.yellow(planner.label === "not set" ? "not set" : `${planner.label} (unavailable)`);
|
|
5351
5422
|
const prompt = wrap(style.bold("Describe a goal to start planning."), Math.max(1, cols - 1));
|
|
5423
|
+
const approvalText = !state.runSettingsKnown ? "" : state.runSettings.autorun ? style.yellow("Autorun is on. A plan starts as soon as the planner submits it. Change it in /settings.") : style.grey("Nothing runs until you approve the plan.");
|
|
5424
|
+
const approval = approvalText ? wrap(approvalText, Math.max(1, cols - 1)) : [];
|
|
5352
5425
|
const setup = [
|
|
5353
5426
|
style.bold("Setup"),
|
|
5354
5427
|
setupRow("Planner", plannerValue, "/planner", cols),
|
|
5355
5428
|
setupRow(
|
|
5356
5429
|
"Runners",
|
|
5357
|
-
enabledRunners.length > 0 ? enabledRunners.join(", ") : style.yellow("none enabled"),
|
|
5430
|
+
detecting ? style.grey(DETECTING) : enabledRunners.length > 0 ? enabledRunners.join(", ") : style.yellow("none enabled"),
|
|
5358
5431
|
"/runners",
|
|
5359
5432
|
cols
|
|
5360
5433
|
),
|
|
@@ -5362,15 +5435,15 @@ function welcomeLines(state, rows, cols) {
|
|
|
5362
5435
|
setupRow("Run", runSettingsValue(state), "/settings", cols),
|
|
5363
5436
|
""
|
|
5364
5437
|
];
|
|
5365
|
-
const notice = canPlan ? [] : [
|
|
5438
|
+
const notice = canPlan || detecting || !state.providersKnown ? [] : [
|
|
5366
5439
|
...wrap(
|
|
5367
|
-
style.yellow(`
|
|
5440
|
+
style.yellow(`Install a coding agent (${runnerNamesOr()}) and pick it with /planner. No API key needed. Or add an API key with /key and choose your provider.`),
|
|
5368
5441
|
Math.max(1, cols - 1)
|
|
5369
5442
|
),
|
|
5370
5443
|
""
|
|
5371
5444
|
];
|
|
5372
5445
|
const tail = [...notice, style.grey("/help all commands"), workspaceLine(state.workspace, cols)];
|
|
5373
|
-
const rest = ["", ...prompt, "", ...setup, ...tail];
|
|
5446
|
+
const rest = ["", ...prompt, ...approval, "", ...setup, ...tail];
|
|
5374
5447
|
const logo = welcomeLogo(rest.length, rows, cols);
|
|
5375
5448
|
return logo.length > 0 ? [...logo, ...rest] : rest.slice(1);
|
|
5376
5449
|
}
|
|
@@ -5398,7 +5471,7 @@ var STATUS_ICON = {
|
|
|
5398
5471
|
var inRow = (label) => label.charAt(0).toLowerCase() + label.slice(1);
|
|
5399
5472
|
function planLayout(state, rows, cols) {
|
|
5400
5473
|
const done = state.tasks.filter((t) => t.status === "completed").length;
|
|
5401
|
-
const totalUsage = (0,
|
|
5474
|
+
const totalUsage = (0, import_core34.planUsage)(state.tasks);
|
|
5402
5475
|
const unmerged = state.runMerge?.handoff.landed.length ?? 0;
|
|
5403
5476
|
const count = `${done}/${state.tasks.length}`;
|
|
5404
5477
|
const hints = runHints(state);
|
|
@@ -5408,7 +5481,7 @@ function planLayout(state, rows, cols) {
|
|
|
5408
5481
|
totalUsage.attempts ? style.grey(truncate(`Tasks total: ${taskUsageText(totalUsage)}`, cols)) : ""
|
|
5409
5482
|
];
|
|
5410
5483
|
if (unmerged > 0) {
|
|
5411
|
-
const landed = style.dim(truncate(`${unmerged} landed task${unmerged === 1 ? "" : "s"} not merged
|
|
5484
|
+
const landed = style.dim(truncate(`${unmerged} landed task${unmerged === 1 ? "" : "s"} not merged: /handoff merge`, cols));
|
|
5412
5485
|
if (totalUsage.attempts) lines.push(landed);
|
|
5413
5486
|
else lines[1] = landed;
|
|
5414
5487
|
}
|
|
@@ -5425,10 +5498,21 @@ function planLayout(state, rows, cols) {
|
|
|
5425
5498
|
lines.push(...renderedTask.lines);
|
|
5426
5499
|
rowSpans.push({ start: taskStart, end: lines.length - 1 });
|
|
5427
5500
|
});
|
|
5501
|
+
lines.push(...heldEditLines(state, cols));
|
|
5428
5502
|
const selected = rowSpans[state.selectedTask] ?? { start: firstRowLine, end: firstRowLine };
|
|
5429
5503
|
const anchor = editorLine === void 0 ? selected : { start: editorLine, end: editorLine };
|
|
5430
5504
|
return { lines, rowSpans, anchor, rows, maxScroll: Math.max(0, lines.length - rows) };
|
|
5431
5505
|
}
|
|
5506
|
+
var heldMark = (mark) => mark === "added" ? style.green("+") : mark === "changed" ? style.yellow("~") : style.red("-");
|
|
5507
|
+
function heldEditLines(state, cols) {
|
|
5508
|
+
const rows = (state.run.heldEdit?.tasks ?? []).flatMap(({ label, mark, task }) => {
|
|
5509
|
+
if (!mark) return [];
|
|
5510
|
+
const title = mark === "removed" ? style.strike(task.title) : task.title;
|
|
5511
|
+
return [truncate(` ${heldMark(mark)} ${style.grey(label.padStart(2))} ${title}`, cols)];
|
|
5512
|
+
});
|
|
5513
|
+
if (rows.length === 0) return [];
|
|
5514
|
+
return ["", style.dim(truncate("Proposed: lands when this reply ends", cols)), ...rows];
|
|
5515
|
+
}
|
|
5432
5516
|
var clampOffset = (layout, offset) => Math.max(0, Math.min(layout.maxScroll, offset));
|
|
5433
5517
|
function revealOffset(layout, offset, span) {
|
|
5434
5518
|
const at = clampOffset(layout, offset);
|
|
@@ -5476,7 +5560,7 @@ function planScrollExtent(state) {
|
|
|
5476
5560
|
}
|
|
5477
5561
|
function taskLines(state, planRow, index, cols) {
|
|
5478
5562
|
const { task, parent } = planRow;
|
|
5479
|
-
const row = (0,
|
|
5563
|
+
const row = (0, import_core34.taskRowView)(task, { parent, tasks: state.tasks, modes: modesForTask(state.modesByRunner, task) });
|
|
5480
5564
|
const selected = state.focus === "plan" && index === state.selectedTask;
|
|
5481
5565
|
const expanded = state.expandedTaskId === task.id;
|
|
5482
5566
|
const rawIcon = row.status === "running" ? SPINNER[state.spinnerFrame % SPINNER.length] : STATUS_ICON[row.status];
|
|
@@ -5504,10 +5588,9 @@ function taskLines(state, planRow, index, cols) {
|
|
|
5504
5588
|
const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}${row.autonomous ? style.yellow(" \u26A1") : ""}` : "";
|
|
5505
5589
|
const runner = task.assignedRunner ?? "";
|
|
5506
5590
|
const bodyPad = parent ? " " : " ";
|
|
5507
|
-
const activity = row.approvals ? `${inRow(row.approvals)}
|
|
5591
|
+
const activity = row.approvals ? `${inRow(row.approvals)}: enter opens it` : row.awaiting ? inRow(row.awaiting) : "";
|
|
5508
5592
|
const skills = (task.skills ?? []).join(" \xB7 ");
|
|
5509
|
-
const
|
|
5510
|
-
const meta = [activity, origin, runner, model];
|
|
5593
|
+
const meta = [activity, runner, model];
|
|
5511
5594
|
const variant = task.type === "ai" ? sanitize(task.assignedModel?.thinkingEffort ?? "") : "";
|
|
5512
5595
|
const withVariant = variant ? [...meta, variant] : meta;
|
|
5513
5596
|
const metaLine = variant && width(`${continuation}${withVariant.filter(Boolean).join(" \xB7 ")}`) <= cols ? withVariant : meta;
|
|
@@ -5516,7 +5599,7 @@ function taskLines(state, planRow, index, cols) {
|
|
|
5516
5599
|
if (row.conflict) {
|
|
5517
5600
|
const where = row.conflict.repo ? ` in ${row.conflict.repo}` : "";
|
|
5518
5601
|
const files = row.conflict.files ? ` (${row.conflict.files})` : "";
|
|
5519
|
-
lines.push(style.red(truncate(`${bodyPad}\u26A0 merge conflict${where}${files}
|
|
5602
|
+
lines.push(style.red(truncate(`${bodyPad}\u26A0 merge conflict${where}${files}: its work is kept on its own branch`, cols)));
|
|
5520
5603
|
}
|
|
5521
5604
|
if (row.repairing) {
|
|
5522
5605
|
const files = row.repairing.files ? ` in ${row.repairing.files}` : "";
|
|
@@ -5524,22 +5607,25 @@ function taskLines(state, planRow, index, cols) {
|
|
|
5524
5607
|
lines.push(style.yellow(truncate(`${bodyPad}\u21BB repairing conflict${files}${attempt}`, cols)));
|
|
5525
5608
|
}
|
|
5526
5609
|
if (row.mergeGate) {
|
|
5527
|
-
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 waits for Merge all
|
|
5610
|
+
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 waits for Merge all: ${row.mergeGate.join(", ")} not merged into your branch yet`, cols)));
|
|
5611
|
+
}
|
|
5612
|
+
if (row.waitsForApproval) {
|
|
5613
|
+
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 ${inRow(row.waitsForApproval)}: added after you approved the plan; f starts it, or /run runs the plan`, cols)));
|
|
5528
5614
|
}
|
|
5529
5615
|
if (row.blockedBy) {
|
|
5530
|
-
lines.push(style.red(truncate(`${bodyPad}\u2298 ${inRow((0,
|
|
5616
|
+
lines.push(style.red(truncate(`${bodyPad}\u2298 ${inRow((0, import_core34.blockedByLabel)(row.blockedBy))}: runs once ${row.blockedBy.length === 1 ? "it is retried and passes" : "they are retried and pass"}`, cols)));
|
|
5531
5617
|
}
|
|
5532
5618
|
if (row.status === "skipped") {
|
|
5533
|
-
lines.push(style.grey(truncate(`${bodyPad}\xBB ${inRow((0,
|
|
5619
|
+
lines.push(style.grey(truncate(`${bodyPad}\xBB ${inRow((0, import_core34.skippedLabel)(row.skipReason))}: nothing of it landed; m done lands its kept work`, cols)));
|
|
5534
5620
|
}
|
|
5535
5621
|
const resumable = row.actions.includes("resume-now");
|
|
5536
5622
|
if (row.resumes) {
|
|
5537
|
-
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 ${inRow(row.resumes)}, once the usage limit resets
|
|
5623
|
+
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 ${inRow(row.resumes)}, once the usage limit resets: ${resumable ? "r resume now \xB7 " : ""}c don't resume`, cols)));
|
|
5538
5624
|
} else if (resumable) {
|
|
5539
|
-
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 waits for the usage limit to reset
|
|
5625
|
+
lines.push(style.yellow(truncate(`${bodyPad}\u23F8 waits for the usage limit to reset: r resume now`, cols)));
|
|
5540
5626
|
}
|
|
5541
5627
|
if (row.opsChangedFiles) {
|
|
5542
|
-
lines.push(style.yellow(truncate(`${bodyPad}\u26A0 an ops task changed tracked files
|
|
5628
|
+
lines.push(style.yellow(truncate(`${bodyPad}\u26A0 an ops task changed tracked files: check them, then m done or /retry`, cols)));
|
|
5543
5629
|
}
|
|
5544
5630
|
if (row.repaired?.landed) {
|
|
5545
5631
|
lines.push(style.grey(truncate(`${bodyPad}\u21BB landed after repairing conflict in ${row.repaired.files}`, cols)));
|
|
@@ -5639,14 +5725,14 @@ function commandSheetBody(cols) {
|
|
|
5639
5725
|
body.push(
|
|
5640
5726
|
style.grey("tab switches panes \xB7 pgup/pgdn scroll \xB7 ctrl-o toggles full detail \xB7 esc takes back a queued prompt, otherwise esc twice stops \xB7 ctrl-l clears \xB7 ctrl-c quits"),
|
|
5641
5727
|
style.grey("alt-enter inserts a newline \xB7 ? lists the plan pane's keys, once tab has put you there"),
|
|
5642
|
-
style.grey(" in the planner chat, ctrl-s sends now: stops the running planner turn and sends the composer text next
|
|
5728
|
+
style.grey(" in the planner chat, ctrl-s sends now: stops the running planner turn and sends the composer text next: or, with it empty, the newest queued prompt")
|
|
5643
5729
|
);
|
|
5644
5730
|
body.push(
|
|
5645
5731
|
"",
|
|
5646
5732
|
style.bold("In a task view (enter on a task)"),
|
|
5647
|
-
style.grey(" ctrl-s sends now: interrupts the running step, then delivers the composer text
|
|
5733
|
+
style.grey(" ctrl-s sends now: interrupts the running step, then delivers the composer text or, with it empty, the highlighted pending message"),
|
|
5648
5734
|
style.grey(" ctrl-n/ctrl-p pick a pending message (the newest until you do) \xB7 ctrl-r removes a queued one"),
|
|
5649
|
-
style.grey(" ctrl-x stops the runner's running turn, and it then waits for your next message
|
|
5735
|
+
style.grey(" ctrl-x stops the runner's running turn, and it then waits for your next message: on a task paused on a usage limit, it keeps it from resuming"),
|
|
5650
5736
|
style.grey(" ctrl-y allows a tool request (its keys sit under it) \xB7 ctrl-t allows it for the task \xB7 ctrl-g denies it, with the composer text as the note"),
|
|
5651
5737
|
style.grey(" at a checkpoint, enter replies with what you typed, ctrl-y approves, ctrl-g rejects (the text is its reason) \xB7 /checkpoint <id> approve|reject [reason] from anywhere"),
|
|
5652
5738
|
style.grey(" alt\u2190/\u2192 changes attempt \xB7 esc returns")
|
|
@@ -5665,8 +5751,8 @@ function helpScrollMax(state) {
|
|
|
5665
5751
|
}
|
|
5666
5752
|
|
|
5667
5753
|
// src/tui/render.ts
|
|
5668
|
-
var
|
|
5669
|
-
var
|
|
5754
|
+
var import_core35 = require("@ordewell/core");
|
|
5755
|
+
var import_core36 = require("@ordewell/core");
|
|
5670
5756
|
function render(state) {
|
|
5671
5757
|
const { rows, cols } = state;
|
|
5672
5758
|
const rowsForBody = bodyRows(state);
|
|
@@ -5765,8 +5851,8 @@ function runSettingsBadge(state) {
|
|
|
5765
5851
|
const { supervisorLevel, mergeAtGatesAutomatically, autorun } = state.runSettings;
|
|
5766
5852
|
const paint = (text, warn) => warn ? style.yellow(text) : style.grey(text);
|
|
5767
5853
|
const run = state.status === "executing" ? state.runSupervisor : null;
|
|
5768
|
-
const actual = run?.unsupervised ? "unsupervised" : run && run.level !== supervisorLevel ?
|
|
5769
|
-
const level = `supervisor ${
|
|
5854
|
+
const actual = run?.unsupervised ? "unsupervised" : run && run.level !== supervisorLevel ? import_core36.SUPERVISOR_LEVEL_LABEL[run.level] : null;
|
|
5855
|
+
const level = `supervisor ${import_core36.SUPERVISOR_LEVEL_LABEL[supervisorLevel]}${actual ? ` \u2192 ${actual}` : ""}`;
|
|
5770
5856
|
return [
|
|
5771
5857
|
paint(level, supervisorLevel === "act"),
|
|
5772
5858
|
...mergeAtGatesAutomatically ? [style.grey("auto-merge")] : [],
|
|
@@ -5960,16 +6046,16 @@ function renderHandoff(state, overlay, rows, cols) {
|
|
|
5960
6046
|
const { lines, scroll } = overlay.diff;
|
|
5961
6047
|
const shown = lines.slice(scroll, scroll + room).map((line) => paintDiffLine(truncate(line, Math.max(1, cols - 2))));
|
|
5962
6048
|
const more = scroll + room < lines.length ? "\u2191\u2193 pgup/pgdn scroll \xB7 " : "";
|
|
5963
|
-
return frame(`Diff
|
|
6049
|
+
return frame(`Diff: ${handoffBranch(handoff)}`, [...shown, "", style.grey(`${more}enter or esc goes back`)], rows, cols);
|
|
5964
6050
|
}
|
|
5965
6051
|
const landed = handoff.landed.length === 0 ? [style.grey("Nothing landed on it.")] : handoff.landed.map((task) => {
|
|
5966
|
-
const repaired = task.repairedFiles?.length ? style.yellow(
|
|
5967
|
-
return ` ${style.green("\u2713")} ${(0,
|
|
6052
|
+
const repaired = task.repairedFiles?.length ? style.yellow(`: repaired (${(0, import_core35.capConflictFiles)(task.repairedFiles)})`) : "";
|
|
6053
|
+
return ` ${style.green("\u2713")} ${(0, import_core35.titledTaskRefOf)(state.tasks, task.taskId) ?? (0, import_core35.titledTaskRef)(task)}${repaired}`;
|
|
5968
6054
|
});
|
|
5969
6055
|
const actions = handoffActions(handoff).map((action, index) => {
|
|
5970
6056
|
const active = index === overlay.index;
|
|
5971
6057
|
const caret = active ? style.brand("\u276F") : " ";
|
|
5972
|
-
return `${caret} ${active ? style.bold(action.label) : action.label}${style.grey(
|
|
6058
|
+
return `${caret} ${active ? style.bold(action.label) : action.label}${style.grey(`: ${action.hint}`)}`;
|
|
5973
6059
|
});
|
|
5974
6060
|
const perRepo = isRepoGroup(handoff) ? ["Per repository:", ...handoff.repos.map((repo, i) => ` ${repo.landed.length > 0 ? style.green("\u2713") : style.grey("\xB7")} ${repoResultLines(handoff)[i]}`), ""] : [];
|
|
5975
6061
|
return frame(
|
|
@@ -6138,10 +6224,13 @@ function frame(title, body, rows, cols) {
|
|
|
6138
6224
|
}
|
|
6139
6225
|
|
|
6140
6226
|
// src/tui/reducer.ts
|
|
6141
|
-
var
|
|
6227
|
+
var import_core49 = require("@ordewell/core");
|
|
6142
6228
|
|
|
6143
6229
|
// src/tui/reducers/overlays.ts
|
|
6144
|
-
var
|
|
6230
|
+
var import_core48 = require("@ordewell/core");
|
|
6231
|
+
|
|
6232
|
+
// src/tui/reducers/approvals.ts
|
|
6233
|
+
var import_core37 = require("@ordewell/core");
|
|
6145
6234
|
|
|
6146
6235
|
// src/tui/reducers/shared.ts
|
|
6147
6236
|
function step(state, effects = []) {
|
|
@@ -6157,7 +6246,7 @@ function clampSelection(index, rows) {
|
|
|
6157
6246
|
}
|
|
6158
6247
|
function withSession(state, run) {
|
|
6159
6248
|
if (!state.sessionId) {
|
|
6160
|
-
return fail3(state, "No active plan
|
|
6249
|
+
return fail3(state, "No active plan: describe a goal first, or load a session with /sessions.");
|
|
6161
6250
|
}
|
|
6162
6251
|
return run(state.sessionId);
|
|
6163
6252
|
}
|
|
@@ -6186,6 +6275,13 @@ function dropApproval(state, approvalId) {
|
|
|
6186
6275
|
if (open?.id !== approvalId) return { ...state, pendingApprovals: pending };
|
|
6187
6276
|
return showNextApproval({ ...state, pendingApprovals: pending, overlay: null });
|
|
6188
6277
|
}
|
|
6278
|
+
function syncApprovals(state, waiting) {
|
|
6279
|
+
const ids = new Set(waiting.map((request) => request.id));
|
|
6280
|
+
const open = state.overlay?.kind === "approval" ? [state.overlay.request] : [];
|
|
6281
|
+
const answered = [...open, ...state.pendingApprovals].filter((request) => !ids.has(request.id));
|
|
6282
|
+
const dropped = answered.reduce((current, request) => dropApproval(current, request.id), { ...state, conversation: (0, import_core37.reconcileApprovals)(state.conversation, waiting) });
|
|
6283
|
+
return waiting.reduce((current, { id, kind, subject, scope, detail }) => kind === "runner_tool" ? current : enqueueApproval(current, { id, kind, subject, scope, ...detail ? { detail } : {} }), dropped);
|
|
6284
|
+
}
|
|
6189
6285
|
function showNextApproval(state) {
|
|
6190
6286
|
const [next, ...rest] = state.pendingApprovals;
|
|
6191
6287
|
if (!next) return { ...state, overlay: null };
|
|
@@ -6200,10 +6296,10 @@ function handleApprovalKey(state, overlay, key) {
|
|
|
6200
6296
|
}
|
|
6201
6297
|
|
|
6202
6298
|
// src/tui/reducers/commands.ts
|
|
6203
|
-
var
|
|
6299
|
+
var import_core47 = require("@ordewell/core");
|
|
6204
6300
|
|
|
6205
6301
|
// src/tui/reducers/plannerStepper.ts
|
|
6206
|
-
var
|
|
6302
|
+
var import_core38 = require("@ordewell/core");
|
|
6207
6303
|
var offered = (items) => new Set(items.filter((i) => !i.disabled).map((i) => i.id));
|
|
6208
6304
|
function preferredModel(state, provider) {
|
|
6209
6305
|
const listed = offered(plannerModelItems(state, provider, ""));
|
|
@@ -6222,7 +6318,7 @@ var effortLabel = (label) => /\beffort$/i.test(label) ? label : `${label} effort
|
|
|
6222
6318
|
var plannerStepper = {
|
|
6223
6319
|
title: "Planner",
|
|
6224
6320
|
advanceOnEnter: true,
|
|
6225
|
-
hint: "Who researches your goal and writes the plan. Coding agents use their own subscription
|
|
6321
|
+
hint: "Who researches your goal and writes the plan. Coding agents use their own subscription. No API key.",
|
|
6226
6322
|
steps: [
|
|
6227
6323
|
{
|
|
6228
6324
|
id: "harness",
|
|
@@ -6251,7 +6347,7 @@ var plannerStepper = {
|
|
|
6251
6347
|
const model = plannerModelItems(state, provider, "").find((m) => m.id === modelId);
|
|
6252
6348
|
const level = plannerEffortItems(state, provider, modelId, "").find((e) => e.id === effort);
|
|
6253
6349
|
const parts = [
|
|
6254
|
-
(0,
|
|
6350
|
+
(0, import_core38.getProviderMeta)(provider)?.label ?? provider,
|
|
6255
6351
|
model?.label ?? modelId,
|
|
6256
6352
|
...effort ? [effortLabel(level?.label ?? effort)] : []
|
|
6257
6353
|
];
|
|
@@ -6266,7 +6362,7 @@ var plannerStepper = {
|
|
|
6266
6362
|
};
|
|
6267
6363
|
|
|
6268
6364
|
// src/tui/reducers/stepper.ts
|
|
6269
|
-
var
|
|
6365
|
+
var import_core39 = require("@ordewell/core");
|
|
6270
6366
|
function openStepper(state, definition) {
|
|
6271
6367
|
const { choices, lists } = settle(state, definition, {});
|
|
6272
6368
|
const first = lists.findIndex((list2) => list2 !== null);
|
|
@@ -6289,7 +6385,7 @@ function handleStepperKey(state, stepper, key) {
|
|
|
6289
6385
|
const reopen = (next) => step({ ...state, overlay: { kind: "stepper", stepper: next } });
|
|
6290
6386
|
const { definition, current, entry, choices } = stepper;
|
|
6291
6387
|
const def = definition.steps[current];
|
|
6292
|
-
const typed = def.number && entry ? (0,
|
|
6388
|
+
const typed = def.number && entry ? (0, import_core39.parseCount)(entry.text, def.number.min) : null;
|
|
6293
6389
|
const { lists } = settle(state, definition, choices);
|
|
6294
6390
|
const items = matchingItems(stepper.items, stepper.filter);
|
|
6295
6391
|
const before = lastIndexBefore(lists, current);
|
|
@@ -6326,7 +6422,7 @@ function handleStepperKey(state, stepper, key) {
|
|
|
6326
6422
|
function confirm(state, stepper, item) {
|
|
6327
6423
|
if (!item) return step(state);
|
|
6328
6424
|
if (item.disabled) {
|
|
6329
|
-
return step(say(state, "error", item.detail ? `${item.label}
|
|
6425
|
+
return step(say(state, "error", item.detail ? `${item.label}: ${item.detail}` : `${item.label} is not available.`));
|
|
6330
6426
|
}
|
|
6331
6427
|
return choose(state, stepper, item.id);
|
|
6332
6428
|
}
|
|
@@ -6355,7 +6451,7 @@ function settle(state, definition, wanted) {
|
|
|
6355
6451
|
const lists = definition.steps.map((def) => {
|
|
6356
6452
|
if (def.skip?.(state, { ...choices })) return null;
|
|
6357
6453
|
if (def.number) {
|
|
6358
|
-
choices[def.id] = String((0,
|
|
6454
|
+
choices[def.id] = String((0, import_core39.parseCount)(wanted[def.id], def.number.min) ?? def.number.current(state, { ...choices }));
|
|
6359
6455
|
return [];
|
|
6360
6456
|
}
|
|
6361
6457
|
const items = def.items(state, { ...choices });
|
|
@@ -6394,7 +6490,7 @@ function lastIndexBefore(lists, index) {
|
|
|
6394
6490
|
}
|
|
6395
6491
|
|
|
6396
6492
|
// src/tui/reducers/planPane.ts
|
|
6397
|
-
var
|
|
6493
|
+
var import_core42 = require("@ordewell/core");
|
|
6398
6494
|
|
|
6399
6495
|
// src/tui/reducers/pointer.ts
|
|
6400
6496
|
var WHEEL_NOTCH = 3;
|
|
@@ -6472,13 +6568,13 @@ function handleMouseSelect(state, key) {
|
|
|
6472
6568
|
}
|
|
6473
6569
|
|
|
6474
6570
|
// src/tui/reducers/taskView.ts
|
|
6475
|
-
var
|
|
6571
|
+
var import_core40 = require("@ordewell/core");
|
|
6476
6572
|
function openTaskView(state, sessionId, taskId) {
|
|
6477
6573
|
const task = findTask(state.tasks, taskId);
|
|
6478
6574
|
if (!task) return step(say(state, "system", `No task ${taskId} in this plan.`));
|
|
6479
6575
|
const taskView = {
|
|
6480
6576
|
taskId,
|
|
6481
|
-
view:
|
|
6577
|
+
view: import_core40.EMPTY_TASK_LOG,
|
|
6482
6578
|
attempts: [],
|
|
6483
6579
|
attempt: 0,
|
|
6484
6580
|
pending: [],
|
|
@@ -6508,13 +6604,13 @@ function applyLive(tv, attempt, events) {
|
|
|
6508
6604
|
}
|
|
6509
6605
|
next = {
|
|
6510
6606
|
...next,
|
|
6511
|
-
view:
|
|
6607
|
+
view: import_core40.EMPTY_TASK_LOG,
|
|
6512
6608
|
attempt,
|
|
6513
6609
|
attempts: next.attempts.includes(attempt) ? next.attempts : [...next.attempts, attempt]
|
|
6514
6610
|
};
|
|
6515
6611
|
}
|
|
6516
6612
|
if (attempt < next.attempt) return next;
|
|
6517
|
-
return clampQueueIndex({ ...next, view: events.reduce(
|
|
6613
|
+
return clampQueueIndex({ ...next, view: events.reduce(import_core40.reduceTaskLog, next.view) });
|
|
6518
6614
|
}
|
|
6519
6615
|
function taskLogAbandoned(state) {
|
|
6520
6616
|
const tv = state.taskView;
|
|
@@ -6535,14 +6631,14 @@ function announceApprovals(state, action) {
|
|
|
6535
6631
|
if (state.taskView?.taskId === action.taskId) return state;
|
|
6536
6632
|
const task = findTask(state.tasks, action.taskId);
|
|
6537
6633
|
const way = task ? `tab to the plan, select task ${task.order} and press enter (or /open ${task.order})` : "tab to the plan, select it and press enter";
|
|
6538
|
-
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" && !event.decided ? say(s, "system", `Task ${task?.order ?? "?"} waits for approval: ${(0,
|
|
6634
|
+
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" && !event.decided ? say(s, "system", `Task ${task?.order ?? "?"} waits for approval: ${(0, import_core40.runnerToolSubject)(event.tool, event.args)}: ${way}, then ctrl-y allows or ctrl-g denies`) : s, state);
|
|
6539
6635
|
return spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
|
|
6540
6636
|
}
|
|
6541
6637
|
function announceCheckpoint(state, action) {
|
|
6542
6638
|
const task = findTask(state.tasks, action.taskId) ?? state.tasks.find((t) => t.title === action.title);
|
|
6543
6639
|
const label = task ? `Task ${task.order}` : action.title;
|
|
6544
6640
|
const where = task ? `tab to the plan, select task ${task.order} and press enter (or /open ${task.order}) to answer` : "tab to the plan, select it and press enter to answer";
|
|
6545
|
-
const spoken = say(state, "system", `${label} asks: ${(0,
|
|
6641
|
+
const spoken = say(state, "system", `${label} asks: ${(0, import_core40.truncateCheckpointSummary)(action.summary)}: ${where}`);
|
|
6546
6642
|
return { ...spoken, scroll: state.scroll };
|
|
6547
6643
|
}
|
|
6548
6644
|
function taskLogLoaded(state, action) {
|
|
@@ -6550,7 +6646,7 @@ function taskLogLoaded(state, action) {
|
|
|
6550
6646
|
if (!tv || tv.taskId !== action.taskId) return state;
|
|
6551
6647
|
const loaded = action.attempt >= 1 ? {
|
|
6552
6648
|
...tv,
|
|
6553
|
-
view: (0,
|
|
6649
|
+
view: (0, import_core40.replayTaskLog)(action.events),
|
|
6554
6650
|
attempts: action.attempts ?? tv.attempts,
|
|
6555
6651
|
attempt: action.attempt
|
|
6556
6652
|
} : { ...tv, attempts: action.attempts ?? tv.attempts };
|
|
@@ -6565,7 +6661,7 @@ function answerApproval(state, tv, sessionId, decision) {
|
|
|
6565
6661
|
const approval = waitingApproval(tv);
|
|
6566
6662
|
if (!approval?.approvalId) return step(say(state, "system", "Nothing is waiting for approval."));
|
|
6567
6663
|
if (decision === "allowForTask" && !approval.allowForTask) {
|
|
6568
|
-
return step(say(state, "system", "The runner offered no grant for the rest of this task
|
|
6664
|
+
return step(say(state, "system", "The runner offered no grant for the rest of this task: ctrl-y allows this call."));
|
|
6569
6665
|
}
|
|
6570
6666
|
const note = decision === "deny" ? state.editor.text.trim() : "";
|
|
6571
6667
|
const answer = decision === "deny" ? { decision, ...note ? { note } : {} } : { decision };
|
|
@@ -6589,7 +6685,7 @@ function sendNow(state, tv, sessionId) {
|
|
|
6589
6685
|
if (text) {
|
|
6590
6686
|
const task = findTask(state.tasks, tv.taskId);
|
|
6591
6687
|
if (continuesTask(task)) {
|
|
6592
|
-
return step(say(state, "system", resumesTask(task) ? "The task is paused on a usage limit, so there is no turn to interrupt
|
|
6688
|
+
return step(say(state, "system", resumesTask(task) ? "The task is paused on a usage limit, so there is no turn to interrupt: enter resumes it." : "The task has finished, so there is no turn to interrupt: enter continues it."));
|
|
6593
6689
|
}
|
|
6594
6690
|
return step({ ...state, editor: commit(state.editor) }, [{ type: "forceSendTaskMessage", sessionId, taskId: tv.taskId, text }]);
|
|
6595
6691
|
}
|
|
@@ -6624,7 +6720,7 @@ function handleTaskViewKey(state, key) {
|
|
|
6624
6720
|
if (key.name === "ctrl-s") return sendNow(state, tv, state.sessionId);
|
|
6625
6721
|
if (key.name === "ctrl-x") {
|
|
6626
6722
|
const task = findTask(state.tasks, tv.taskId);
|
|
6627
|
-
if (task && (0,
|
|
6723
|
+
if (task && (0, import_core40.resumeLabel)(task)) return step(state, [{ type: "taskAction", sessionId: state.sessionId, taskId: tv.taskId, action: "cancel-resume" }]);
|
|
6628
6724
|
return step(state, [{ type: "interruptTask", sessionId: state.sessionId, taskId: tv.taskId }]);
|
|
6629
6725
|
}
|
|
6630
6726
|
if (key.name === "ctrl-y") return answerWaiting(state, tv, state.sessionId, "allow");
|
|
@@ -6642,7 +6738,7 @@ function handleTaskViewKey(state, key) {
|
|
|
6642
6738
|
}
|
|
6643
6739
|
|
|
6644
6740
|
// src/tui/reducers/taskEdits.ts
|
|
6645
|
-
var
|
|
6741
|
+
var import_core41 = require("@ordewell/core");
|
|
6646
6742
|
var import_plan_utils3 = require("@ordewell/core/plan-utils");
|
|
6647
6743
|
function openTaskRunnerPicker(state, task) {
|
|
6648
6744
|
if (task.type !== "ai") return fail3(state, "Manual tasks do not run on an executor, so they have no runner.");
|
|
@@ -6672,7 +6768,7 @@ function confirmRemoveTask(state, task) {
|
|
|
6672
6768
|
}
|
|
6673
6769
|
function toggleTaskOps(state, task, subtask, to = !task.ops) {
|
|
6674
6770
|
if (!state.sessionId) return fail3(state, "No active plan.");
|
|
6675
|
-
if (task.type !== "ai") return fail3(state, "Only an AI task can be an ops task
|
|
6771
|
+
if (task.type !== "ai") return fail3(state, "Only an AI task can be an ops task: a manual task already runs outside any worktree.");
|
|
6676
6772
|
if (subtask) return fail3(state, "A subtask runs with its parent; make the parent an ops task instead.");
|
|
6677
6773
|
if (!!task.ops === to) return fail3(state, `${refFor(state, task)} is already ${to ? "an ops" : "a change"} task.`);
|
|
6678
6774
|
return step(state, [{
|
|
@@ -6753,7 +6849,7 @@ function openTaskModePicker(state, task) {
|
|
|
6753
6849
|
overlay: {
|
|
6754
6850
|
kind: "picker",
|
|
6755
6851
|
picker: picker(`Mode \xB7 ${refFor(state, task)}`, pickerItemsFor(state, action), action, {
|
|
6756
|
-
hint: [`Modes declared by ${task.assignedRunner}.`, (0,
|
|
6852
|
+
hint: [`Modes declared by ${task.assignedRunner}.`, (0, import_core41.codexSandboxNotice)(state.modesByRunner)?.line].filter(Boolean).join(" ")
|
|
6757
6853
|
})
|
|
6758
6854
|
}
|
|
6759
6855
|
}
|
|
@@ -6905,7 +7001,7 @@ function taskOpsCommand(state, args) {
|
|
|
6905
7001
|
}
|
|
6906
7002
|
function addTask(state, title) {
|
|
6907
7003
|
if (!state.sessionId) {
|
|
6908
|
-
return fail3(state, "No active plan to add a task to
|
|
7004
|
+
return fail3(state, "No active plan to add a task to: describe a goal first, then /add-task <title>.");
|
|
6909
7005
|
}
|
|
6910
7006
|
if (!title) {
|
|
6911
7007
|
return step({
|
|
@@ -6981,11 +7077,11 @@ function handlePlanKey(state, key) {
|
|
|
6981
7077
|
if (!state.sessionId || key.name !== "char") return step(state);
|
|
6982
7078
|
if (key.char === "E") return step(state, [{ type: "execute", sessionId: state.sessionId }]);
|
|
6983
7079
|
if (key.char === "S") return step(state, [{ type: "stopExecution", sessionId: state.sessionId }]);
|
|
6984
|
-
const shortcut = key.char === "c" && (0,
|
|
7080
|
+
const shortcut = key.char === "c" && (0, import_core42.resumeLabel)(task) ? "cancel-resume" : PLAN_SHORTCUTS[key.char ?? ""];
|
|
6985
7081
|
if (key.char === "r") {
|
|
6986
|
-
return (0,
|
|
7082
|
+
return (0, import_core42.taskRowActions)(task).includes("resume-now") ? step(state, [taskActionEffect(state, state.sessionId, task.id, "resume-now")]) : step(state);
|
|
6987
7083
|
}
|
|
6988
|
-
const action = key.char === "m" ? (0,
|
|
7084
|
+
const action = key.char === "m" ? (0, import_core42.markAction)(task) : shortcut;
|
|
6989
7085
|
if (action === "force-start" && task.mergeGate?.length) return confirmForceStartPastGate(state, task);
|
|
6990
7086
|
if (action) {
|
|
6991
7087
|
return step(state, [taskActionEffect(state, state.sessionId, task.id, action)]);
|
|
@@ -7025,9 +7121,9 @@ function commitTaskEdit(state, task, editor) {
|
|
|
7025
7121
|
}
|
|
7026
7122
|
|
|
7027
7123
|
// src/tui/reducers/turnQueue.ts
|
|
7028
|
-
var
|
|
7124
|
+
var import_core43 = require("@ordewell/core");
|
|
7029
7125
|
function drainQueue(state, settled) {
|
|
7030
|
-
const next = (0,
|
|
7126
|
+
const next = (0, import_core43.drainNext)(state.queuedPrompts);
|
|
7031
7127
|
const ended = disarmStop({ ...settled, stopRequested: false, forceSendPending: null });
|
|
7032
7128
|
if (!next) return step(ended);
|
|
7033
7129
|
const spoken = say(ended, "user", next.text);
|
|
@@ -7042,7 +7138,7 @@ function restoreForced(state) {
|
|
|
7042
7138
|
if (!forced) return state;
|
|
7043
7139
|
const cleared = { ...state, forceSendPending: null };
|
|
7044
7140
|
if (forced.queueIndex === void 0) {
|
|
7045
|
-
const text = (0,
|
|
7141
|
+
const text = (0, import_core43.aheadOfDraft)(forced.text, state.editor.text);
|
|
7046
7142
|
return { ...cleared, editor: { ...state.editor, text, cursor: text.length } };
|
|
7047
7143
|
}
|
|
7048
7144
|
const queued = [...state.queuedPrompts];
|
|
@@ -7058,14 +7154,14 @@ function stopPlanning(state, sessionId) {
|
|
|
7058
7154
|
return step(queueToEditor(cutTurn({ ...dismissed, stopArmed: false, stopRequested: true, forceSendPending: null })), [{ type: "cancelPlanning", sessionId }]);
|
|
7059
7155
|
}
|
|
7060
7156
|
function queueToEditor(state) {
|
|
7061
|
-
const all = (0,
|
|
7157
|
+
const all = (0, import_core43.unsendAll)(state.queuedPrompts);
|
|
7062
7158
|
if (!all) return state;
|
|
7063
7159
|
return { ...state, queuedPrompts: all.rest, editor: { ...state.editor, text: all.text, cursor: all.text.length } };
|
|
7064
7160
|
}
|
|
7065
7161
|
function unsendLatest(state) {
|
|
7066
|
-
const latest = (0,
|
|
7162
|
+
const latest = (0, import_core43.unsendLatest)(state.queuedPrompts);
|
|
7067
7163
|
if (!latest) return step(state);
|
|
7068
|
-
const text = (0,
|
|
7164
|
+
const text = (0, import_core43.aheadOfDraft)(latest.text, state.editor.text);
|
|
7069
7165
|
return step({
|
|
7070
7166
|
...state,
|
|
7071
7167
|
queuedPrompts: latest.rest,
|
|
@@ -7081,7 +7177,7 @@ function forceSendPlanner(state) {
|
|
|
7081
7177
|
{ type: "forceSendPlannerMessage", sessionId: state.sessionId, message: text }
|
|
7082
7178
|
]);
|
|
7083
7179
|
}
|
|
7084
|
-
const latest = (0,
|
|
7180
|
+
const latest = (0, import_core43.unsendLatest)(state.queuedPrompts);
|
|
7085
7181
|
if (!latest) return step(say(state, "system", "Nothing to send now: type a message, or queue one with enter."));
|
|
7086
7182
|
const forced = { text: latest.text, queueIndex: latest.rest.length };
|
|
7087
7183
|
return step({ ...state, queuedPrompts: latest.rest, stopRequested: false, forceSendPending: forced }, [
|
|
@@ -7097,7 +7193,7 @@ function plannerEscape(state) {
|
|
|
7097
7193
|
}
|
|
7098
7194
|
|
|
7099
7195
|
// src/tui/reducers/incoming.ts
|
|
7100
|
-
var
|
|
7196
|
+
var import_core44 = require("@ordewell/core");
|
|
7101
7197
|
var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
|
|
7102
7198
|
var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
|
|
7103
7199
|
var asRecord = (value) => typeof value === "object" && value !== null ? value : {};
|
|
@@ -7136,12 +7232,13 @@ function runTaskToView(t) {
|
|
|
7136
7232
|
...Array.isArray(model.availableVariants) ? { availableVariants: model.availableVariants.map((variant) => planLabel(variant)) } : {}
|
|
7137
7233
|
} : void 0,
|
|
7138
7234
|
isolation: t.isolation,
|
|
7139
|
-
awaitingReason: t.status === "awaiting_user" && (0,
|
|
7235
|
+
awaitingReason: t.status === "awaiting_user" && (0, import_core44.isAwaitingReason)(t.awaitingReason) ? t.awaitingReason : void 0,
|
|
7140
7236
|
...t.status === "awaiting_user" && t.awaitingReason === "usage-limit" && typeof t.resumeAt === "string" ? { resumeAt: t.resumeAt } : {},
|
|
7141
7237
|
...t.status === "skipped" && typeof t.skipReason === "string" ? { skipReason: sanitize(t.skipReason) } : {},
|
|
7142
7238
|
checkpoint: typeof t.checkpoint === "string" ? sanitize(t.checkpoint) : void 0,
|
|
7143
7239
|
continuable: t.continuable === true,
|
|
7144
7240
|
awaitingApproval: t.awaitingApproval,
|
|
7241
|
+
...t.waitsForApproval === true ? { waitsForApproval: true } : {},
|
|
7145
7242
|
...t.ops === true ? { ops: true } : {},
|
|
7146
7243
|
...Array.isArray(t.skills) && t.skills.length > 0 ? { skills: t.skills.map((name) => planLabel(name)) } : {},
|
|
7147
7244
|
...t.addedBy ? { addedBy: t.addedBy } : {},
|
|
@@ -7155,7 +7252,7 @@ function supervisorOfRun(runSettings) {
|
|
|
7155
7252
|
const unsupervised = typeof runSettings.unsupervised === "string" ? runSettings.unsupervised : void 0;
|
|
7156
7253
|
return {
|
|
7157
7254
|
runSupervisor: {
|
|
7158
|
-
level: (0,
|
|
7255
|
+
level: (0, import_core44.isSupervisorLevel)(runSettings.supervisorLevel) ? runSettings.supervisorLevel : "off",
|
|
7159
7256
|
...unsupervised ? { unsupervised } : {}
|
|
7160
7257
|
}
|
|
7161
7258
|
};
|
|
@@ -7172,7 +7269,7 @@ function withRun(state, run) {
|
|
|
7172
7269
|
};
|
|
7173
7270
|
}
|
|
7174
7271
|
function foldRun(state, input) {
|
|
7175
|
-
const run = (0,
|
|
7272
|
+
const run = (0, import_core44.reduceRun)(state.run, input);
|
|
7176
7273
|
return run === state.run ? state : withRun(state, run);
|
|
7177
7274
|
}
|
|
7178
7275
|
function mapRunTasks(tasks, map) {
|
|
@@ -7202,11 +7299,13 @@ function applySettings(state, settings) {
|
|
|
7202
7299
|
...state,
|
|
7203
7300
|
orchestratorModel: typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : state.orchestratorModel,
|
|
7204
7301
|
plannerProvider: typeof settings.aiProvider === "string" ? settings.aiProvider : state.plannerProvider,
|
|
7302
|
+
plannerKnown: state.plannerKnown || typeof settings.aiProvider === "string",
|
|
7205
7303
|
plannerEffort: typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : state.plannerEffort,
|
|
7206
7304
|
plannerModels: settings.plannerModels && typeof settings.plannerModels === "object" ? settings.plannerModels : state.plannerModels,
|
|
7207
7305
|
maxParallel: typeof settings.maxParallel === "number" ? settings.maxParallel : state.maxParallel,
|
|
7208
7306
|
allowlist: settings.modelAllowlist && typeof settings.modelAllowlist === "object" ? settings.modelAllowlist : state.allowlist,
|
|
7209
|
-
runSettings: settings.run ?? state.runSettings
|
|
7307
|
+
runSettings: settings.run ?? state.runSettings,
|
|
7308
|
+
runSettingsKnown: state.runSettingsKnown || settings.run !== void 0
|
|
7210
7309
|
};
|
|
7211
7310
|
}
|
|
7212
7311
|
function followSession(state, message) {
|
|
@@ -7220,21 +7319,21 @@ function followSession(state, message) {
|
|
|
7220
7319
|
}
|
|
7221
7320
|
|
|
7222
7321
|
// src/tui/reducers/runSettings.ts
|
|
7223
|
-
var
|
|
7322
|
+
var import_core45 = require("@ordewell/core");
|
|
7224
7323
|
function runSettingsEffect(state, changes) {
|
|
7225
7324
|
return { type: "setRunSettings", changes, ...state.sessionId ? { sessionId: state.sessionId } : {} };
|
|
7226
7325
|
}
|
|
7227
7326
|
function supervisorCommand(state, args) {
|
|
7228
7327
|
const [arg, value] = args;
|
|
7229
7328
|
if (arg === void 0) {
|
|
7230
|
-
return step(say(state, "system", `${(0,
|
|
7329
|
+
return step(say(state, "system", `${(0, import_core45.supervisorSummary)(state.runSettings)}: /supervisor off|watch|act, /supervisor retries <n>, /supervisor reads <n>.`));
|
|
7231
7330
|
}
|
|
7232
7331
|
if (arg === "retries") {
|
|
7233
|
-
const n = (0,
|
|
7332
|
+
const n = (0, import_core45.parseCount)(value, 0);
|
|
7234
7333
|
return n === null ? fail3(state, "Usage: /supervisor retries <whole number, 0 or more>") : step(state, [runSettingsEffect(state, { supervisorRetryLimit: n })]);
|
|
7235
7334
|
}
|
|
7236
7335
|
if (arg === "reads") {
|
|
7237
|
-
const n = (0,
|
|
7336
|
+
const n = (0, import_core45.parseCount)(value, 1);
|
|
7238
7337
|
return n === null ? fail3(state, "Usage: /supervisor reads <whole number, 1 or more>") : step(state, [runSettingsEffect(state, { wakeUpReadBudget: n })]);
|
|
7239
7338
|
}
|
|
7240
7339
|
if (arg === "act") return confirmSupervisorAct(state);
|
|
@@ -7243,17 +7342,17 @@ function supervisorCommand(state, args) {
|
|
|
7243
7342
|
}
|
|
7244
7343
|
function automergeCommand(state, arg) {
|
|
7245
7344
|
if (arg === void 0) {
|
|
7246
|
-
return step(say(state, "system", `${(0,
|
|
7345
|
+
return step(say(state, "system", `${(0, import_core45.automergeSummary)(state.runSettings)}: /automerge on|off.`));
|
|
7247
7346
|
}
|
|
7248
|
-
const enabled = (0,
|
|
7347
|
+
const enabled = (0, import_core45.parseSwitch)(arg);
|
|
7249
7348
|
if (enabled === null) return fail3(state, "Usage: /automerge [on|off]");
|
|
7250
7349
|
return step(state, [runSettingsEffect(state, { mergeAtGatesAutomatically: enabled })]);
|
|
7251
7350
|
}
|
|
7252
7351
|
function autorunCommand(state, arg) {
|
|
7253
7352
|
if (arg === void 0) {
|
|
7254
|
-
return step(say(state, "system", `${(0,
|
|
7353
|
+
return step(say(state, "system", `${(0, import_core45.autorunSummary)(state.runSettings)}: /autorun on starts each plan the planner submits, without review.`));
|
|
7255
7354
|
}
|
|
7256
|
-
const enabled = (0,
|
|
7355
|
+
const enabled = (0, import_core45.parseSwitch)(arg);
|
|
7257
7356
|
if (enabled === null) return fail3(state, "Usage: /autorun [on|off]");
|
|
7258
7357
|
if (enabled) return confirmAutorun(state);
|
|
7259
7358
|
return step(state, [runSettingsEffect(state, { autorun: false })]);
|
|
@@ -7265,7 +7364,7 @@ function confirmSupervisorAct(state) {
|
|
|
7265
7364
|
kind: "confirm",
|
|
7266
7365
|
title: "Let the supervisor act?",
|
|
7267
7366
|
message: "At Act the supervisor runs, stops and retries tasks, answers their requests (never allowing), edits the plan and merges, without asking you.",
|
|
7268
|
-
note: `${
|
|
7367
|
+
note: `${import_core45.ACT_RISK}
|
|
7269
7368
|
It applies to this session's run now, and to every later run.`,
|
|
7270
7369
|
action: { kind: "supervisor-act" }
|
|
7271
7370
|
}
|
|
@@ -7278,14 +7377,14 @@ function confirmAutorun(state) {
|
|
|
7278
7377
|
kind: "confirm",
|
|
7279
7378
|
title: "Start plans without review?",
|
|
7280
7379
|
message: "With autorun on, each plan the planner submits starts at once, as if you had pressed Execute. The planner still asks when something is unclear.",
|
|
7281
|
-
note:
|
|
7380
|
+
note: import_core45.AUTORUN_RISK,
|
|
7282
7381
|
action: { kind: "autorun-on" }
|
|
7283
7382
|
}
|
|
7284
7383
|
});
|
|
7285
7384
|
}
|
|
7286
7385
|
|
|
7287
7386
|
// src/tui/reducers/settings.ts
|
|
7288
|
-
var
|
|
7387
|
+
var import_core46 = require("@ordewell/core");
|
|
7289
7388
|
var SUPERVISOR_DETAIL = {
|
|
7290
7389
|
off: "The planner speaks only when you write",
|
|
7291
7390
|
watch: "The planner follows your runs and suggests what to do",
|
|
@@ -7310,7 +7409,7 @@ var SETTINGS = {
|
|
|
7310
7409
|
{
|
|
7311
7410
|
id: "guarded",
|
|
7312
7411
|
label: "Guarded",
|
|
7313
|
-
detail: [`New plans run tasks in each runner's safer mode`, (0,
|
|
7412
|
+
detail: [`New plans run tasks in each runner's safer mode`, (0, import_core46.codexSandboxNotice)(state.modesByRunner)?.line].filter(Boolean).join(" \xB7 "),
|
|
7314
7413
|
selected: !state.autonomous
|
|
7315
7414
|
}
|
|
7316
7415
|
]
|
|
@@ -7320,7 +7419,7 @@ var SETTINGS = {
|
|
|
7320
7419
|
label: "Supervisor",
|
|
7321
7420
|
items: (state) => ["off", "watch", "act"].map((level) => ({
|
|
7322
7421
|
id: level,
|
|
7323
|
-
label:
|
|
7422
|
+
label: import_core46.SUPERVISOR_LEVEL_LABEL[level],
|
|
7324
7423
|
detail: SUPERVISOR_DETAIL[level],
|
|
7325
7424
|
selected: state.runSettings.supervisorLevel === level
|
|
7326
7425
|
}))
|
|
@@ -7374,12 +7473,12 @@ function saveChoices(state, choices) {
|
|
|
7374
7473
|
const autonomous = choices.autonomy === "full";
|
|
7375
7474
|
if (autonomous !== state.autonomous) {
|
|
7376
7475
|
save2.autonomous = autonomous;
|
|
7377
|
-
changed.push(`Autonomy ${(0,
|
|
7476
|
+
changed.push(`Autonomy ${(0, import_core46.autonomyLevelLabel)(autonomous)}`);
|
|
7378
7477
|
}
|
|
7379
7478
|
const level = choices.supervisor;
|
|
7380
7479
|
if (level !== state.runSettings.supervisorLevel) {
|
|
7381
7480
|
run.supervisorLevel = level;
|
|
7382
|
-
changed.push(`Supervisor ${
|
|
7481
|
+
changed.push(`Supervisor ${import_core46.SUPERVISOR_LEVEL_LABEL[level]}`);
|
|
7383
7482
|
}
|
|
7384
7483
|
if (choices.retries !== void 0 && Number(choices.retries) !== state.runSettings.supervisorRetryLimit) {
|
|
7385
7484
|
run.supervisorRetryLimit = Number(choices.retries);
|
|
@@ -7406,7 +7505,7 @@ function saveChoices(state, choices) {
|
|
|
7406
7505
|
}
|
|
7407
7506
|
if (changed.length === 0) return step(state);
|
|
7408
7507
|
const effect = { ...save2, message: `Settings: ${changed.join(" \xB7 ")}` };
|
|
7409
|
-
const risks = [...run.supervisorLevel === "act" ? [
|
|
7508
|
+
const risks = [...run.supervisorLevel === "act" ? [import_core46.ACT_RISK] : [], ...run.autorun ? [import_core46.AUTORUN_RISK] : []];
|
|
7410
7509
|
if (risks.length === 0) return saveSettings(state, effect);
|
|
7411
7510
|
return step({
|
|
7412
7511
|
...state,
|
|
@@ -7435,7 +7534,7 @@ function saveSettings(state, effect) {
|
|
|
7435
7534
|
}
|
|
7436
7535
|
|
|
7437
7536
|
// src/tui/reducers/commands.ts
|
|
7438
|
-
var unknownCommand = (state, name) => fail3(state, `Unknown command: /${name}
|
|
7537
|
+
var unknownCommand = (state, name) => fail3(state, `Unknown command: /${name}. Type /help to see what's available.`);
|
|
7439
7538
|
function runCommand(state, { name, args }) {
|
|
7440
7539
|
if (!findCommand(name)) return unknownCommand(state, name);
|
|
7441
7540
|
switch (name) {
|
|
@@ -7504,7 +7603,7 @@ function runCommand(state, { name, args }) {
|
|
|
7504
7603
|
case "save":
|
|
7505
7604
|
return withSession(state, (sessionId) => step(state, [{ type: "saveSession", sessionId }]));
|
|
7506
7605
|
case "load":
|
|
7507
|
-
return args[0] ? step(state, [{ type: "loadSession", sessionId: args[0] }]) : fail3(state, "Usage: /load <session-id
|
|
7606
|
+
return args[0] ? step(state, [{ type: "loadSession", sessionId: args[0] }]) : fail3(state, "Usage: /load <session-id>: or run /sessions to pick one.");
|
|
7508
7607
|
case "delete":
|
|
7509
7608
|
return args[0] ? step(state, [{ type: "deleteSession", sessionId: args[0] }]) : fail3(state, "Usage: /delete <session-id>");
|
|
7510
7609
|
case "add-task":
|
|
@@ -7528,7 +7627,7 @@ function runCommand(state, { name, args }) {
|
|
|
7528
7627
|
const answer = args[1]?.toLowerCase();
|
|
7529
7628
|
const reason = args.slice(2).join(" ").trim();
|
|
7530
7629
|
if (answer !== "approve" && answer !== "reject") return fail3(state, "Usage: /checkpoint <id> approve|reject [reason]");
|
|
7531
|
-
if (answer === "approve" && reason) return fail3(state, "Approving takes no note
|
|
7630
|
+
if (answer === "approve" && reason) return fail3(state, "Approving takes no note: reject with a reason to tell the task something.");
|
|
7532
7631
|
return answerCheckpoint(state, sessionId, taskId, answer, reason);
|
|
7533
7632
|
});
|
|
7534
7633
|
case "task-runner":
|
|
@@ -7566,7 +7665,7 @@ function runCommand(state, { name, args }) {
|
|
|
7566
7665
|
function withIdlePlanner(state, run) {
|
|
7567
7666
|
return withSession(
|
|
7568
7667
|
state,
|
|
7569
|
-
(sessionId) => plannerInFlight(state) ? fail3(state, "The planner is still answering
|
|
7668
|
+
(sessionId) => plannerInFlight(state) ? fail3(state, "The planner is still answering: wait for its reply, or /stop it first.") : run(sessionId)
|
|
7570
7669
|
);
|
|
7571
7670
|
}
|
|
7572
7671
|
function resolveToggle(arg, current) {
|
|
@@ -7597,8 +7696,8 @@ function newSession(state) {
|
|
|
7597
7696
|
goal: "",
|
|
7598
7697
|
taskView: null,
|
|
7599
7698
|
planApproved: false,
|
|
7600
|
-
conversation:
|
|
7601
|
-
turnGate:
|
|
7699
|
+
conversation: import_core47.EMPTY_CONVERSATION,
|
|
7700
|
+
turnGate: import_core47.NO_TURN,
|
|
7602
7701
|
selectedTask: 0,
|
|
7603
7702
|
expandedTaskId: null,
|
|
7604
7703
|
taskEditor: null,
|
|
@@ -7609,19 +7708,21 @@ function newSession(state) {
|
|
|
7609
7708
|
// Prompts belong to the session that raised them; the old planner is gone
|
|
7610
7709
|
// and its pending requests deny on their own timeout.
|
|
7611
7710
|
pendingApprovals: [],
|
|
7612
|
-
queuedPrompts:
|
|
7711
|
+
queuedPrompts: import_core47.EMPTY_HOLD,
|
|
7613
7712
|
stopArmed: false,
|
|
7614
7713
|
forceSendPending: null,
|
|
7615
7714
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
7616
|
-
},
|
|
7715
|
+
}, import_core47.EMPTY_RUN), closeEffects);
|
|
7617
7716
|
}
|
|
7618
7717
|
function setModel(state, args) {
|
|
7619
7718
|
if (args[0] === "set" && args[1]) {
|
|
7620
|
-
const runner = (0,
|
|
7719
|
+
const runner = (0, import_core47.runnerForProvider)(state.plannerProvider);
|
|
7621
7720
|
const known = runner ? modelsForRunner(state.models, runner) : [];
|
|
7622
7721
|
if (runner && known.length > 0 && !known.some((m) => m.id === args[1])) {
|
|
7623
7722
|
return fail3(state, `${args[1]} was not discovered for ${runner}.`);
|
|
7624
7723
|
}
|
|
7724
|
+
const refusal = known.length === 0 ? vendorModelRefusal(state.plannerProvider, args[1], import_core47.TERMINAL_UI_COMMANDS.pickPlanner) : null;
|
|
7725
|
+
if (refusal) return fail3(state, refusal);
|
|
7625
7726
|
return step(state, [{ type: "setModel", modelId: args[1] }]);
|
|
7626
7727
|
}
|
|
7627
7728
|
if (args.length > 0 && args[0] !== "set") return fail3(state, "Usage: /model [set <model-id>]");
|
|
@@ -7640,7 +7741,7 @@ function setModel(state, args) {
|
|
|
7640
7741
|
function setPlanner(state, args) {
|
|
7641
7742
|
if (args[0]) {
|
|
7642
7743
|
const id = args[0].toLowerCase();
|
|
7643
|
-
if (!(0,
|
|
7744
|
+
if (!(0, import_core47.isPlannerBackend)(id)) {
|
|
7644
7745
|
return fail3(state, `Unknown planner: ${id}. Run /planner with no arguments to see the list.`);
|
|
7645
7746
|
}
|
|
7646
7747
|
return step(state, [{ type: "setPlanner", provider: id }]);
|
|
@@ -7672,7 +7773,7 @@ function setPlannerEffort(state, args) {
|
|
|
7672
7773
|
function setKey(state, args) {
|
|
7673
7774
|
if (args[0] === "set" && args[1]) {
|
|
7674
7775
|
const provider = args[1].toLowerCase();
|
|
7675
|
-
if (!(0,
|
|
7776
|
+
if (!(0, import_core47.isApiProvider)(provider)) {
|
|
7676
7777
|
return fail3(state, `Unknown provider: ${provider}. Run /key with no arguments to see the list.`);
|
|
7677
7778
|
}
|
|
7678
7779
|
if (!args[2]) {
|
|
@@ -7683,16 +7784,16 @@ function setKey(state, args) {
|
|
|
7683
7784
|
}
|
|
7684
7785
|
return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
|
|
7685
7786
|
}
|
|
7686
|
-
const items =
|
|
7787
|
+
const items = import_core47.PROVIDER_PRIORITY.map((id) => ({
|
|
7687
7788
|
id,
|
|
7688
|
-
label: (0,
|
|
7689
|
-
detail: (0,
|
|
7789
|
+
label: (0, import_core47.getProviderMeta)(id).label,
|
|
7790
|
+
detail: (0, import_core47.getProviderMeta)(id).apiKeyEnvVar,
|
|
7690
7791
|
selected: state.configuredProviders.includes(id)
|
|
7691
7792
|
}));
|
|
7692
7793
|
return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
|
|
7693
7794
|
}
|
|
7694
7795
|
function keyPrompt(provider) {
|
|
7695
|
-
const meta = (0,
|
|
7796
|
+
const meta = (0, import_core47.getProviderMeta)(provider);
|
|
7696
7797
|
return {
|
|
7697
7798
|
kind: "prompt",
|
|
7698
7799
|
title: `${meta.label} API key`,
|
|
@@ -7768,14 +7869,14 @@ function runners(state, args) {
|
|
|
7768
7869
|
} });
|
|
7769
7870
|
}
|
|
7770
7871
|
function setMaxParallel(state, arg) {
|
|
7771
|
-
if (!arg) return step(say(state, "system", `Up to ${state.maxParallel} AI task${state.maxParallel === 1 ? "" : "s"} run at once
|
|
7772
|
-
const limit = (0,
|
|
7872
|
+
if (!arg) return step(say(state, "system", `Up to ${state.maxParallel} AI task${state.maxParallel === 1 ? "" : "s"} run at once: /parallel <n> changes it.`));
|
|
7873
|
+
const limit = (0, import_core47.parseMaxParallel)(arg);
|
|
7773
7874
|
if (limit === null) return fail3(state, "Usage: /parallel [<number of tasks, 1 or more>]");
|
|
7774
7875
|
return step({ ...state, maxParallel: limit }, [{ type: "setMaxParallel", limit }]);
|
|
7775
7876
|
}
|
|
7776
7877
|
function setAutonomous(state, arg) {
|
|
7777
|
-
if (arg === void 0) return step(say(state, "system", `Autonomy level: ${(0,
|
|
7778
|
-
const enabled = (0,
|
|
7878
|
+
if (arg === void 0) return step(say(state, "system", `Autonomy level: ${(0, import_core47.autonomyLevelLabel)(state.autonomous)}. /auto full or /auto guarded changes it for new plans.`));
|
|
7879
|
+
const enabled = (0, import_core47.parseAutonomyLevel)(arg);
|
|
7779
7880
|
if (enabled === null) return fail3(state, "Usage: /auto [full|guarded]");
|
|
7780
7881
|
return step({ ...state, autonomous: enabled }, [{ type: "setAutonomous", enabled }]);
|
|
7781
7882
|
}
|
|
@@ -7788,7 +7889,7 @@ function setMouseCapture(state, arg) {
|
|
|
7788
7889
|
// move it.
|
|
7789
7890
|
{ ...state, mouseCapture: enabled, selection: null },
|
|
7790
7891
|
"system",
|
|
7791
|
-
enabled ? "Mouse capture on
|
|
7892
|
+
enabled ? "Mouse capture on: the wheel scrolls, and dragging selects within one pane and copies on release." : "Mouse capture off: the terminal's own drag-select is back, across both panes; scroll with pgup/pgdn."
|
|
7792
7893
|
);
|
|
7793
7894
|
return step(noted, [{ type: "setMouseCapture", enabled }]);
|
|
7794
7895
|
}
|
|
@@ -7800,7 +7901,7 @@ function rewind(state, arg) {
|
|
|
7800
7901
|
[{ type: "loadRewindTargets", sessionId }]
|
|
7801
7902
|
);
|
|
7802
7903
|
}
|
|
7803
|
-
if (!/^\d+$/.test(arg)) return fail3(state, "Usage: /rewind [<message>]
|
|
7904
|
+
if (!/^\d+$/.test(arg)) return fail3(state, "Usage: /rewind [<message>]: or /rewind alone to pick one.");
|
|
7804
7905
|
return step(state, [{ type: "loadRewindTargets", sessionId, pick: Number(arg) }]);
|
|
7805
7906
|
});
|
|
7806
7907
|
}
|
|
@@ -7811,7 +7912,7 @@ var REWIND_OPTIONS = [
|
|
|
7811
7912
|
function pickRewindTarget(state, index) {
|
|
7812
7913
|
return withIdlePlanner({ ...state, overlay: null }, () => {
|
|
7813
7914
|
const target = state.rewindTargets?.find((t) => t.index === index);
|
|
7814
|
-
if (!target) return fail3({ ...state, overlay: null }, `No message ${index} to rewind to
|
|
7915
|
+
if (!target) return fail3({ ...state, overlay: null }, `No message ${index} to rewind to: /rewind alone lists them.`);
|
|
7815
7916
|
return step({
|
|
7816
7917
|
...state,
|
|
7817
7918
|
overlay: {
|
|
@@ -7903,7 +8004,7 @@ function handlePromptKey(state, overlay, key) {
|
|
|
7903
8004
|
if (overlay.action.kind === "api-key") {
|
|
7904
8005
|
return step(closed, [{ type: "setApiKey", provider: overlay.action.provider, key: value2 }]);
|
|
7905
8006
|
}
|
|
7906
|
-
if (!state.sessionId) return fail3(closed, "No active plan to add a task to
|
|
8007
|
+
if (!state.sessionId) return fail3(closed, "No active plan to add a task to: describe a goal first, then /add-task <title>.");
|
|
7907
8008
|
return step(closed, [{ type: "addTask", sessionId: state.sessionId, title: value2 }]);
|
|
7908
8009
|
}
|
|
7909
8010
|
const value = editText(overlay.value, key);
|
|
@@ -7948,7 +8049,7 @@ function choose2(state, picker2, item) {
|
|
|
7948
8049
|
return step(closed, [{
|
|
7949
8050
|
type: "setRunners",
|
|
7950
8051
|
changes,
|
|
7951
|
-
message: names.length > 0 ? `Runners enabled: ${names.join(", ")}.` : "No runners enabled
|
|
8052
|
+
message: names.length > 0 ? `Runners enabled: ${names.join(", ")}.` : "No runners enabled: the planner has nothing to assign work to."
|
|
7952
8053
|
}]);
|
|
7953
8054
|
}
|
|
7954
8055
|
if (picker2.action.kind === "set-task-deps") {
|
|
@@ -7970,7 +8071,7 @@ function choose2(state, picker2, item) {
|
|
|
7970
8071
|
}
|
|
7971
8072
|
if (!item) return step(state);
|
|
7972
8073
|
if (item.disabled) {
|
|
7973
|
-
return step(say(state, "error", item.detail ? `${item.label}
|
|
8074
|
+
return step(say(state, "error", item.detail ? `${item.label}: ${item.detail}` : `${item.label} is not available.`));
|
|
7974
8075
|
}
|
|
7975
8076
|
switch (picker2.action.kind) {
|
|
7976
8077
|
case "set-model":
|
|
@@ -7984,7 +8085,7 @@ function choose2(state, picker2, item) {
|
|
|
7984
8085
|
case "delete-session":
|
|
7985
8086
|
return step(closed, [{ type: "deleteSession", sessionId: item.id }]);
|
|
7986
8087
|
case "set-key":
|
|
7987
|
-
return (0,
|
|
8088
|
+
return (0, import_core48.isApiProvider)(item.id) ? step({ ...state, overlay: keyPrompt(item.id) }) : step(closed);
|
|
7988
8089
|
case "choose-allowlist-runner": {
|
|
7989
8090
|
const action = { kind: "set-allowlist", runner: item.id };
|
|
7990
8091
|
const items = pickerItemsFor(state, action);
|
|
@@ -8060,7 +8161,7 @@ function reduceAction(state, action) {
|
|
|
8060
8161
|
// (`task-1`), so nothing of the last one's — a merge gate, a checkpoint, its
|
|
8061
8162
|
// tasks — may carry over into this.
|
|
8062
8163
|
case "sessionStarted":
|
|
8063
|
-
return step(withRun({ ...state, sessionId: action.sessionId, goal: action.goal, taskView: null },
|
|
8164
|
+
return step(withRun({ ...state, sessionId: action.sessionId, goal: action.goal, taskView: null }, import_core49.EMPTY_RUN));
|
|
8064
8165
|
// The daemon registers a session only once planning succeeds; when it does
|
|
8065
8166
|
// not, holding on to the id would send every next message to a session the
|
|
8066
8167
|
// server never had.
|
|
@@ -8071,18 +8172,18 @@ function reduceAction(state, action) {
|
|
|
8071
8172
|
goal: "",
|
|
8072
8173
|
taskView: null,
|
|
8073
8174
|
pendingApprovals: [],
|
|
8074
|
-
queuedPrompts:
|
|
8175
|
+
queuedPrompts: import_core49.EMPTY_HOLD,
|
|
8075
8176
|
stopArmed: false,
|
|
8076
8177
|
forceSendPending: null,
|
|
8077
8178
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
8078
|
-
},
|
|
8179
|
+
}, import_core49.EMPTY_RUN));
|
|
8079
8180
|
// Deleting the session on screen stops it in the daemon; a view left on it
|
|
8080
8181
|
// would answer every next message with a session-not-found.
|
|
8081
8182
|
case "sessionDeleted":
|
|
8082
8183
|
return action.sessionId === state.sessionId ? newSession(state) : step(state);
|
|
8083
8184
|
case "chatRestored":
|
|
8084
8185
|
if (stale(state, action.sessionId)) return step(state);
|
|
8085
|
-
return step({ ...state, conversation: (0,
|
|
8186
|
+
return step({ ...state, conversation: (0, import_core49.fromTranscript)(action.history, action.researchLog, action.plannerUsage, action.pendingTurns), scroll: 0 });
|
|
8086
8187
|
case "planUpdated": {
|
|
8087
8188
|
if (stale(state, action.sessionId)) return step(state);
|
|
8088
8189
|
const settled = { ...showPlan(state, action.plan), planner: "idle" };
|
|
@@ -8103,10 +8204,14 @@ function reduceAction(state, action) {
|
|
|
8103
8204
|
// its status line with it, as a status update would have set it.
|
|
8104
8205
|
case "runSnapshot": {
|
|
8105
8206
|
if (stale(state, action.sessionId)) return step(state);
|
|
8106
|
-
const
|
|
8207
|
+
const run = withRun(state, action.view);
|
|
8208
|
+
const next = action.approvals ? syncApprovals(run, action.approvals) : run;
|
|
8107
8209
|
const status = runStatus(next, next.tasks);
|
|
8108
8210
|
return step({ ...next, status });
|
|
8109
8211
|
}
|
|
8212
|
+
case "heldEdit":
|
|
8213
|
+
if (stale(state, action.sessionId)) return step(state);
|
|
8214
|
+
return step(foldRun(state, { type: "plan_edit_held", held: action.held }));
|
|
8110
8215
|
case "taskLog":
|
|
8111
8216
|
if (stale(state, action.sessionId)) return step(state);
|
|
8112
8217
|
return step(announceApprovals(taskLogArrived(state, action), action));
|
|
@@ -8122,15 +8227,15 @@ function reduceAction(state, action) {
|
|
|
8122
8227
|
case "taskStarted": {
|
|
8123
8228
|
if (stale(state, action.sessionId)) return step(state);
|
|
8124
8229
|
const existing = findTask(state.tasks, action.taskId);
|
|
8125
|
-
if (existing && (0,
|
|
8230
|
+
if (existing && (0, import_core28.isTaskRunning)(existing)) return step(state);
|
|
8126
8231
|
const folded = foldRun(state, {
|
|
8127
8232
|
type: "task_started",
|
|
8128
8233
|
taskId: action.taskId,
|
|
8129
8234
|
order: action.order ?? 0,
|
|
8130
8235
|
title: action.title,
|
|
8131
|
-
runner: action.runner ??
|
|
8236
|
+
runner: action.runner ?? import_core49.DEFAULT_RUNNERS[0]
|
|
8132
8237
|
});
|
|
8133
|
-
const spoken = say(folded, "system", (0,
|
|
8238
|
+
const spoken = say(folded, "system", (0, import_core49.taskStartedNotice)(action.title, action.runner));
|
|
8134
8239
|
return step({ ...spoken, status: "executing" });
|
|
8135
8240
|
}
|
|
8136
8241
|
case "taskCheckpoint":
|
|
@@ -8176,13 +8281,13 @@ function reduceAction(state, action) {
|
|
|
8176
8281
|
completed: state.tasks.filter((t) => t.status === "completed").length,
|
|
8177
8282
|
total: state.tasks.length,
|
|
8178
8283
|
failed: state.tasks.filter((t) => t.status === "failed").length,
|
|
8179
|
-
blocked: (0,
|
|
8284
|
+
blocked: (0, import_core49.blockingFailures)(state.tasks).size,
|
|
8180
8285
|
skipped: state.tasks.filter((t) => t.status === "skipped").length
|
|
8181
8286
|
};
|
|
8182
8287
|
const failures = `${failed > 0 ? ` \xB7 ${failed} failed` : ""}${blocked ? ` \xB7 ${blocked} blocked` : ""}${skipped ? ` \xB7 ${skipped} skipped` : ""}`;
|
|
8183
8288
|
const verb = action.stopped ? "stopped" : "finished";
|
|
8184
8289
|
return step({
|
|
8185
|
-
...say(state, "system", `Execution ${verb}
|
|
8290
|
+
...say(state, "system", `Execution ${verb}: ${completed}/${total} tasks complete${failures}.`),
|
|
8186
8291
|
status: "idle"
|
|
8187
8292
|
});
|
|
8188
8293
|
}
|
|
@@ -8201,7 +8306,7 @@ function reduceAction(state, action) {
|
|
|
8201
8306
|
case "executionLost": {
|
|
8202
8307
|
if (stale(state, action.sessionId)) return step(state);
|
|
8203
8308
|
return step({
|
|
8204
|
-
...say(state, "error", "Lost the connection to the daemon
|
|
8309
|
+
...say(state, "error", "Lost the connection to the daemon: this view is no longer following the run, but the daemon may still be running it. /load this session again to see where it stands."),
|
|
8205
8310
|
status: "idle"
|
|
8206
8311
|
});
|
|
8207
8312
|
}
|
|
@@ -8215,6 +8320,7 @@ function reduceAction(state, action) {
|
|
|
8215
8320
|
models: action.models,
|
|
8216
8321
|
orchestratorModels: action.orchestratorModels ?? state.orchestratorModels,
|
|
8217
8322
|
configuredProviders: action.providers ?? state.configuredProviders,
|
|
8323
|
+
providersKnown: state.providersKnown || action.providers !== void 0,
|
|
8218
8324
|
providerErrors: action.providerErrors ?? state.providerErrors,
|
|
8219
8325
|
modesByRunner: action.modesByRunner ?? state.modesByRunner
|
|
8220
8326
|
},
|
|
@@ -8240,11 +8346,11 @@ function reduceAction(state, action) {
|
|
|
8240
8346
|
planner: "idle",
|
|
8241
8347
|
planApproved: false,
|
|
8242
8348
|
pendingApprovals: [],
|
|
8243
|
-
queuedPrompts:
|
|
8349
|
+
queuedPrompts: import_core49.EMPTY_HOLD,
|
|
8244
8350
|
stopArmed: false,
|
|
8245
8351
|
forceSendPending: null,
|
|
8246
8352
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
8247
|
-
},
|
|
8353
|
+
}, import_core49.EMPTY_RUN));
|
|
8248
8354
|
// The rewound message comes back as a draft to edit and resend, so it lands
|
|
8249
8355
|
// as if just typed: history is not being browsed, and nothing is parked.
|
|
8250
8356
|
case "inputPrefilled":
|
|
@@ -8258,6 +8364,7 @@ function reduceAction(state, action) {
|
|
|
8258
8364
|
return step(refreshStepper(refillPicker({
|
|
8259
8365
|
...state,
|
|
8260
8366
|
runners: action.runners,
|
|
8367
|
+
runnersKnown: true,
|
|
8261
8368
|
orchestratorModel: action.orchestratorModel ?? state.orchestratorModel
|
|
8262
8369
|
}, ["set-task-runner"])));
|
|
8263
8370
|
case "failed": {
|
|
@@ -8316,7 +8423,7 @@ function showPlan(state, plan) {
|
|
|
8316
8423
|
});
|
|
8317
8424
|
}
|
|
8318
8425
|
function runStatus(state, tasks) {
|
|
8319
|
-
if (tasks.some(
|
|
8426
|
+
if (tasks.some(import_core28.isTaskRunning)) return "executing";
|
|
8320
8427
|
if (tasks.some((t) => t.status === "awaiting_user") || state.runMerge?.paused) return "executing";
|
|
8321
8428
|
return "idle";
|
|
8322
8429
|
}
|
|
@@ -8391,13 +8498,13 @@ function submit(state) {
|
|
|
8391
8498
|
const command = parseRunnerCommand(text);
|
|
8392
8499
|
const offered2 = runnerCommands(state.taskView);
|
|
8393
8500
|
if (command && offered2.length > 0 && !runnerCommandFor(offered2, command.name)) {
|
|
8394
|
-
const notice = `/${command.name} is not a command ${task?.assignedRunner ?? "the runner"} offers this task
|
|
8501
|
+
const notice = `/${command.name} is not a command ${task?.assignedRunner ?? "the runner"} offers this task: type / to see them.`;
|
|
8395
8502
|
return step({ ...cleared, taskView: { ...state.taskView, notice } });
|
|
8396
8503
|
}
|
|
8397
8504
|
if (state.taskView.notice) cleared.taskView = { ...state.taskView, notice: void 0 };
|
|
8398
8505
|
if (!command && waitingCheckpoint(task)) {
|
|
8399
8506
|
if (waitingApproval(state.taskView)) {
|
|
8400
|
-
return step(say(state, "system", "A tool request is waiting ahead of the checkpoint
|
|
8507
|
+
return step(say(state, "system", "A tool request is waiting ahead of the checkpoint: answer it first (ctrl-y to allow, ctrl-g to deny), then enter replies to the checkpoint."));
|
|
8401
8508
|
}
|
|
8402
8509
|
return answerCheckpoint(cleared, state.sessionId, taskId, "reject", text);
|
|
8403
8510
|
}
|
|
@@ -8408,7 +8515,7 @@ function submit(state) {
|
|
|
8408
8515
|
const builtIn = builtInCommand(text);
|
|
8409
8516
|
if (builtIn) return runCommand(cleared, builtIn);
|
|
8410
8517
|
if (plannerInFlight(state)) {
|
|
8411
|
-
return step({ ...cleared, queuedPrompts: (0,
|
|
8518
|
+
return step({ ...cleared, queuedPrompts: (0, import_core49.holdPrompt)(state.queuedPrompts, text) });
|
|
8412
8519
|
}
|
|
8413
8520
|
const spoken = say(cleared, "user", text);
|
|
8414
8521
|
const effect = state.sessionId ? { type: "sendMessage", sessionId: state.sessionId, message: text } : { type: "startConversation", goal: text };
|
|
@@ -8483,7 +8590,7 @@ function createApp(deps) {
|
|
|
8483
8590
|
|
|
8484
8591
|
// src/tui/effects.ts
|
|
8485
8592
|
var import_child_process3 = require("child_process");
|
|
8486
|
-
var
|
|
8593
|
+
var import_core50 = require("@ordewell/core");
|
|
8487
8594
|
|
|
8488
8595
|
// src/tui/inbound.ts
|
|
8489
8596
|
var CONVERSATION_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -8534,6 +8641,52 @@ function identity(event) {
|
|
|
8534
8641
|
function replyKey(content, timestamp) {
|
|
8535
8642
|
return timestamp ? `planner_message:${timestamp}:${content}` : `planner_message:content:${content}`;
|
|
8536
8643
|
}
|
|
8644
|
+
function stampOf(frame2) {
|
|
8645
|
+
return frame2.stream !== void 0 && frame2.seq !== void 0 ? { stream: frame2.stream, seq: frame2.seq } : null;
|
|
8646
|
+
}
|
|
8647
|
+
var COVERED = /* @__PURE__ */ new Set(["plan_edit_held", "status_update", "task_updated", "approval_request", "approval_settled"]);
|
|
8648
|
+
var Delivered = class {
|
|
8649
|
+
constructor(window) {
|
|
8650
|
+
this.window = window;
|
|
8651
|
+
}
|
|
8652
|
+
window;
|
|
8653
|
+
stream = null;
|
|
8654
|
+
highest = 0;
|
|
8655
|
+
snapshot = 0;
|
|
8656
|
+
seen = /* @__PURE__ */ new Set();
|
|
8657
|
+
first({ stream, seq }) {
|
|
8658
|
+
this.follow(stream);
|
|
8659
|
+
if (seq <= this.highest - this.window || this.seen.has(seq)) return false;
|
|
8660
|
+
this.seen.add(seq);
|
|
8661
|
+
if (seq > this.highest) {
|
|
8662
|
+
this.highest = seq;
|
|
8663
|
+
for (const old of this.seen) if (old <= seq - this.window) this.seen.delete(old);
|
|
8664
|
+
}
|
|
8665
|
+
return true;
|
|
8666
|
+
}
|
|
8667
|
+
covered({ seq }) {
|
|
8668
|
+
return seq <= this.snapshot;
|
|
8669
|
+
}
|
|
8670
|
+
/** Whether a snapshot is as new as everything delivered, and so adopted; an older one would undo what came after it. */
|
|
8671
|
+
adopt({ stream, seq }) {
|
|
8672
|
+
this.follow(stream);
|
|
8673
|
+
if (seq < this.highest) return false;
|
|
8674
|
+
this.snapshot = seq;
|
|
8675
|
+
this.highest = seq;
|
|
8676
|
+
return true;
|
|
8677
|
+
}
|
|
8678
|
+
// A new stream is a restarted daemon: its numbers start over and mean nothing against the last one's.
|
|
8679
|
+
follow(stream) {
|
|
8680
|
+
if (stream === this.stream) return;
|
|
8681
|
+
this.stream = stream;
|
|
8682
|
+
this.highest = 0;
|
|
8683
|
+
this.snapshot = 0;
|
|
8684
|
+
this.seen.clear();
|
|
8685
|
+
}
|
|
8686
|
+
};
|
|
8687
|
+
function isClear(event) {
|
|
8688
|
+
return event.type === "plan_edit_held" && event.held === null || event.type === "approval_settled";
|
|
8689
|
+
}
|
|
8537
8690
|
var Copies = class {
|
|
8538
8691
|
constructor(cap) {
|
|
8539
8692
|
this.cap = cap;
|
|
@@ -8604,6 +8757,7 @@ function inboundFor(api, dispatch, sessionId) {
|
|
|
8604
8757
|
return inbound;
|
|
8605
8758
|
}
|
|
8606
8759
|
function createInbound(dispatch, sessionId) {
|
|
8760
|
+
const delivered = new Delivered(256);
|
|
8607
8761
|
const copies = new Copies(64);
|
|
8608
8762
|
const shown = new WindowSet(64);
|
|
8609
8763
|
const pending = new WindowSet(16);
|
|
@@ -8612,6 +8766,7 @@ function createInbound(dispatch, sessionId) {
|
|
|
8612
8766
|
let timer = null;
|
|
8613
8767
|
let holdingSnapshot = false;
|
|
8614
8768
|
let heldSnapshot = null;
|
|
8769
|
+
let heldAfter = [];
|
|
8615
8770
|
const flush = () => {
|
|
8616
8771
|
if (timer !== null) {
|
|
8617
8772
|
clearTimeout(timer);
|
|
@@ -8636,8 +8791,17 @@ function createInbound(dispatch, sessionId) {
|
|
|
8636
8791
|
shown.add(message.content);
|
|
8637
8792
|
dispatch({ type: "sessionMessage", message, sessionId });
|
|
8638
8793
|
};
|
|
8794
|
+
const fresh = (id, event) => {
|
|
8795
|
+
const stamp = stampOf(event);
|
|
8796
|
+
if (stamp) return delivered.first(stamp) && !(COVERED.has(event.type) && delivered.covered(stamp));
|
|
8797
|
+
return isClear(event) || copies.first(id, identity(event));
|
|
8798
|
+
};
|
|
8639
8799
|
const acceptReply = (id, event) => {
|
|
8640
8800
|
const key = replyKey(event.content, event.timestamp);
|
|
8801
|
+
if (stampOf(event)) {
|
|
8802
|
+
if (fresh(id, event) && !pending.take(key)) showReply(event);
|
|
8803
|
+
return;
|
|
8804
|
+
}
|
|
8641
8805
|
if (pending.take(key)) {
|
|
8642
8806
|
copies.credit(id, key);
|
|
8643
8807
|
return;
|
|
@@ -8645,12 +8809,15 @@ function createInbound(dispatch, sessionId) {
|
|
|
8645
8809
|
if (!copies.first(id, key)) return;
|
|
8646
8810
|
showReply(event);
|
|
8647
8811
|
};
|
|
8812
|
+
const deliverSnapshot = ({ view, approvals }) => {
|
|
8813
|
+
dispatch({ type: "runSnapshot", view, ...approvals ? { approvals } : {}, sessionId });
|
|
8814
|
+
};
|
|
8648
8815
|
const deliverConversation = (id, event) => {
|
|
8649
8816
|
if (event.type === "planner_message") {
|
|
8650
8817
|
acceptReply(id, event);
|
|
8651
8818
|
return;
|
|
8652
8819
|
}
|
|
8653
|
-
if (!
|
|
8820
|
+
if (!fresh(id, event)) return;
|
|
8654
8821
|
if (event.type === "plan_generated") {
|
|
8655
8822
|
dispatch({ type: "sessionMessage", message: event, sessionId });
|
|
8656
8823
|
dispatch({ type: "planBroadcast", plan: event.plan, sessionId });
|
|
@@ -8661,12 +8828,18 @@ function createInbound(dispatch, sessionId) {
|
|
|
8661
8828
|
const receive = (kind, id, event) => {
|
|
8662
8829
|
if (event.type === "run_snapshot") {
|
|
8663
8830
|
flush();
|
|
8664
|
-
|
|
8665
|
-
|
|
8831
|
+
const stamp = stampOf(event);
|
|
8832
|
+
if (stamp && !delivered.adopt(stamp)) return;
|
|
8833
|
+
if (holdingSnapshot) heldSnapshot = event;
|
|
8834
|
+
else deliverSnapshot(event);
|
|
8835
|
+
return;
|
|
8836
|
+
}
|
|
8837
|
+
if (holdingSnapshot) {
|
|
8838
|
+
heldAfter.push(() => receive(kind, id, event));
|
|
8666
8839
|
return;
|
|
8667
8840
|
}
|
|
8668
8841
|
if (isStreamed(event)) {
|
|
8669
|
-
if (kind === "planning") hold(event);
|
|
8842
|
+
if (kind === "planning" && (!stampOf(event) || fresh(id, event))) hold(event);
|
|
8670
8843
|
return;
|
|
8671
8844
|
}
|
|
8672
8845
|
flush();
|
|
@@ -8674,7 +8847,7 @@ function createInbound(dispatch, sessionId) {
|
|
|
8674
8847
|
deliverConversation(id, event);
|
|
8675
8848
|
return;
|
|
8676
8849
|
}
|
|
8677
|
-
if (!
|
|
8850
|
+
if (!fresh(id, event)) return;
|
|
8678
8851
|
dispatchLifecycle(dispatch, event, sessionId);
|
|
8679
8852
|
};
|
|
8680
8853
|
let subscription = 0;
|
|
@@ -8701,12 +8874,17 @@ function createInbound(dispatch, sessionId) {
|
|
|
8701
8874
|
holdSnapshot: () => {
|
|
8702
8875
|
holdingSnapshot = true;
|
|
8703
8876
|
heldSnapshot = null;
|
|
8877
|
+
heldAfter = [];
|
|
8704
8878
|
},
|
|
8705
8879
|
releaseSnapshot: (deliver) => {
|
|
8706
8880
|
holdingSnapshot = false;
|
|
8707
|
-
const
|
|
8881
|
+
const snapshot = heldSnapshot;
|
|
8882
|
+
const after = heldAfter;
|
|
8708
8883
|
heldSnapshot = null;
|
|
8709
|
-
|
|
8884
|
+
heldAfter = [];
|
|
8885
|
+
if (!deliver) return;
|
|
8886
|
+
if (snapshot) deliverSnapshot(snapshot);
|
|
8887
|
+
for (const next of after) next();
|
|
8710
8888
|
}
|
|
8711
8889
|
};
|
|
8712
8890
|
}
|
|
@@ -8730,7 +8908,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
8730
8908
|
dispatch({ type: "taskCheckpoint", taskId: String(event.taskId), title: String(event.taskTitle), summary: event.summary, sessionId });
|
|
8731
8909
|
return;
|
|
8732
8910
|
case "review_needed":
|
|
8733
|
-
dispatch({ type: "notice", message: "Plan needs your sign-off
|
|
8911
|
+
dispatch({ type: "notice", message: "Plan needs your sign-off: /approve to continue." });
|
|
8734
8912
|
return;
|
|
8735
8913
|
case "review_approved":
|
|
8736
8914
|
dispatch({ type: "notice", message: "Plan approved." });
|
|
@@ -8750,6 +8928,10 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
8750
8928
|
case "notice":
|
|
8751
8929
|
dispatch({ type: "notice", message: event.message, level: event.level });
|
|
8752
8930
|
return;
|
|
8931
|
+
// The pane draws it apart from the plan's rows; the reducer folds it into the run view.
|
|
8932
|
+
case "plan_edit_held":
|
|
8933
|
+
dispatch({ type: "heldEdit", held: event.held, sessionId });
|
|
8934
|
+
return;
|
|
8753
8935
|
case "isolation_handoff":
|
|
8754
8936
|
dispatch({ type: "isolationHandoff", handoff: { repos: event.repos, landed: event.landed, ...event.deferredTo ? { deferredTo: event.deferredTo } : {} }, sessionId });
|
|
8755
8937
|
return;
|
|
@@ -8814,7 +8996,7 @@ function isPlannerTurn(effect) {
|
|
|
8814
8996
|
}
|
|
8815
8997
|
function explain(err) {
|
|
8816
8998
|
if (err instanceof DaemonError && err.code === "session_not_found") {
|
|
8817
|
-
return "This server is no longer holding that session
|
|
8999
|
+
return "This server is no longer holding that session: it was deleted, or the server restarted. Reload it with /sessions if it is still listed.";
|
|
8818
9000
|
}
|
|
8819
9001
|
return err instanceof Error ? err.message : String(err);
|
|
8820
9002
|
}
|
|
@@ -8830,7 +9012,7 @@ function plannerModelNotice(recall2) {
|
|
|
8830
9012
|
case "remembered":
|
|
8831
9013
|
return `Restored ${recall2.model}.`;
|
|
8832
9014
|
case "catalog-default":
|
|
8833
|
-
return `Using its default model, ${recall2.model}
|
|
9015
|
+
return `Using its default model, ${recall2.model}. Pick another with /model.`;
|
|
8834
9016
|
case "none":
|
|
8835
9017
|
return "Pick a model with /model.";
|
|
8836
9018
|
}
|
|
@@ -8892,13 +9074,13 @@ async function perform(effect, deps) {
|
|
|
8892
9074
|
await refreshPlan(deps, sessionId);
|
|
8893
9075
|
return;
|
|
8894
9076
|
}
|
|
8895
|
-
const request = action === "cancel" || action === "retry" || action === "force-start" ? () => api.taskControl(sessionId, taskId, action) : action === "resume-now" ? () => api.resumeNow(sessionId, taskId) : action === "skip" ? () => api.skipTask(sessionId, taskId) : (0,
|
|
9077
|
+
const request = action === "cancel" || action === "retry" || action === "force-start" ? () => api.taskControl(sessionId, taskId, action) : action === "resume-now" ? () => api.resumeNow(sessionId, taskId) : action === "skip" ? () => api.skipTask(sessionId, taskId) : (0, import_core50.markRequestFor)(action) === "uncomplete" ? () => api.markTaskIncomplete(sessionId, taskId) : () => api.markTaskComplete(sessionId, taskId);
|
|
8896
9078
|
await (effect.watch ? withExecutionStream(deps, sessionId, request) : request());
|
|
8897
9079
|
await refreshPlan(deps, sessionId);
|
|
8898
9080
|
return;
|
|
8899
9081
|
}
|
|
8900
9082
|
case "addTask": {
|
|
8901
|
-
const fields = (0,
|
|
9083
|
+
const fields = (0, import_core50.newTaskFields)(effect.title);
|
|
8902
9084
|
if (!fields) return;
|
|
8903
9085
|
await api.addTask(effect.sessionId, { ...fields });
|
|
8904
9086
|
await refreshPlan(deps, effect.sessionId);
|
|
@@ -8923,7 +9105,7 @@ async function perform(effect, deps) {
|
|
|
8923
9105
|
try {
|
|
8924
9106
|
await api.respondToApproval(effect.sessionId, effect.approvalId, effect.granted);
|
|
8925
9107
|
} catch {
|
|
8926
|
-
dispatch({ type: "failed", message: "Could not deliver the approval answer
|
|
9108
|
+
dispatch({ type: "failed", message: "Could not deliver the approval answer: the planner will treat it as denied." });
|
|
8927
9109
|
}
|
|
8928
9110
|
return;
|
|
8929
9111
|
}
|
|
@@ -8932,7 +9114,7 @@ async function perform(effect, deps) {
|
|
|
8932
9114
|
try {
|
|
8933
9115
|
await api.respondToApproval(effect.sessionId, effect.approvalId, effect.answer);
|
|
8934
9116
|
} catch {
|
|
8935
|
-
dispatch({ type: "failed", message: "Could not deliver the answer
|
|
9117
|
+
dispatch({ type: "failed", message: "Could not deliver the answer: the task is still waiting for approval." });
|
|
8936
9118
|
}
|
|
8937
9119
|
return;
|
|
8938
9120
|
}
|
|
@@ -9006,8 +9188,13 @@ async function perform(effect, deps) {
|
|
|
9006
9188
|
return;
|
|
9007
9189
|
}
|
|
9008
9190
|
case "setPlanner": {
|
|
9009
|
-
const meta = (0,
|
|
9191
|
+
const meta = (0, import_core50.getProviderMeta)(effect.provider);
|
|
9010
9192
|
if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
|
|
9193
|
+
if ((0, import_core50.isCliProvider)(effect.provider)) {
|
|
9194
|
+
const installed = ((await api.getRunners()).runners ?? []).map((r) => r.id);
|
|
9195
|
+
const refusal = uninstalledPlannerRefusal(effect.provider, installed, import_core50.TERMINAL_UI_COMMANDS.pickPlanner);
|
|
9196
|
+
if (refusal) throw new Error(refusal);
|
|
9197
|
+
}
|
|
9011
9198
|
const env = { AI_PROVIDER: effect.provider };
|
|
9012
9199
|
const settings = await api.updateSettings({ env });
|
|
9013
9200
|
const recall2 = plannerSwitchRecall(settings);
|
|
@@ -9018,7 +9205,7 @@ async function perform(effect, deps) {
|
|
|
9018
9205
|
});
|
|
9019
9206
|
dispatch({
|
|
9020
9207
|
type: "notice",
|
|
9021
|
-
message: `${(0,
|
|
9208
|
+
message: `${(0, import_core50.isCliProvider)(effect.provider) ? `Planning with ${meta.label}: no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
|
|
9022
9209
|
});
|
|
9023
9210
|
await loadModels(deps);
|
|
9024
9211
|
return;
|
|
@@ -9046,7 +9233,7 @@ async function perform(effect, deps) {
|
|
|
9046
9233
|
dispatch({ type: "notice", message: `Planner effort set to ${effect.effort || "the runner default"}.` });
|
|
9047
9234
|
return;
|
|
9048
9235
|
case "setApiKey": {
|
|
9049
|
-
const meta = (0,
|
|
9236
|
+
const meta = (0, import_core50.isApiProvider)(effect.provider) ? (0, import_core50.getProviderMeta)(effect.provider) : void 0;
|
|
9050
9237
|
if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
|
|
9051
9238
|
await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
|
|
9052
9239
|
deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
|
|
@@ -9089,12 +9276,12 @@ async function perform(effect, deps) {
|
|
|
9089
9276
|
}
|
|
9090
9277
|
if (effect.maxParallel !== void 0) await saveMaxParallel(deps, effect.maxParallel);
|
|
9091
9278
|
const overridden = effect.autonomous !== void 0 && deps.saveAutonomy(effect.autonomous).overridden;
|
|
9092
|
-
dispatch({ type: "notice", message: overridden ? `${effect.message}
|
|
9279
|
+
dispatch({ type: "notice", message: overridden ? `${effect.message}: ORDEWELL_AUTONOMOUS_MODE in the environment decides the autonomy level until it is unset.` : effect.message });
|
|
9093
9280
|
return;
|
|
9094
9281
|
}
|
|
9095
9282
|
case "setAutonomous": {
|
|
9096
9283
|
const { overridden } = deps.saveAutonomy(effect.enabled);
|
|
9097
|
-
const level = (0,
|
|
9284
|
+
const level = (0, import_core50.autonomyLevelLabel)(effect.enabled);
|
|
9098
9285
|
dispatch({ type: "notice", message: overridden ? `${level} is saved, but ORDEWELL_AUTONOMOUS_MODE in the environment decides the level until it is unset.` : `Autonomy level: ${level} for new plans.` });
|
|
9099
9286
|
return;
|
|
9100
9287
|
}
|
|
@@ -9132,8 +9319,8 @@ async function perform(effect, deps) {
|
|
|
9132
9319
|
}
|
|
9133
9320
|
const { plan, goal } = adopted2;
|
|
9134
9321
|
dispatch({ type: "sessionStarted", sessionId: effect.sessionId, goal });
|
|
9135
|
-
inbound.releaseSnapshot(true);
|
|
9136
9322
|
dispatch(restoredChat(plan, effect.sessionId));
|
|
9323
|
+
inbound.releaseSnapshot(true);
|
|
9137
9324
|
dispatch({ type: "planUpdated", plan, sessionId: effect.sessionId });
|
|
9138
9325
|
dispatch({ type: "notice", message: `Loaded "${goal || effect.sessionId}".` });
|
|
9139
9326
|
return;
|
|
@@ -9146,7 +9333,7 @@ async function perform(effect, deps) {
|
|
|
9146
9333
|
dispatch({ type: "sessionForked", sessionId: fork.sessionId, goal: fork.goal });
|
|
9147
9334
|
dispatch(restoredChat(fork.plan, fork.sessionId));
|
|
9148
9335
|
dispatch({ type: "planUpdated", plan: fork.plan, sessionId: fork.sessionId });
|
|
9149
|
-
dispatch({ type: "notice", message: `Forked ${effect.sessionId} into ${fork.sessionId}
|
|
9336
|
+
dispatch({ type: "notice", message: `Forked ${effect.sessionId} into ${fork.sessionId}: you are in the fork now. /sessions to go back.` });
|
|
9150
9337
|
return;
|
|
9151
9338
|
}
|
|
9152
9339
|
case "loadRewindTargets":
|
|
@@ -9167,7 +9354,7 @@ async function perform(effect, deps) {
|
|
|
9167
9354
|
dispatch(restoredChat(fork.plan, fork.sessionId));
|
|
9168
9355
|
dispatch({ type: "planUpdated", plan: fork.plan, sessionId: fork.sessionId });
|
|
9169
9356
|
dispatch({ type: "inputPrefilled", text: fork.rewoundMessage, sessionId: fork.sessionId });
|
|
9170
|
-
dispatch({ type: "notice", message: `Forked ${effect.sessionId} into ${fork.sessionId} from before that message
|
|
9357
|
+
dispatch({ type: "notice", message: `Forked ${effect.sessionId} into ${fork.sessionId} from before that message: the original is kept (/sessions to go back). The message is ready to edit and resend.` });
|
|
9171
9358
|
return;
|
|
9172
9359
|
}
|
|
9173
9360
|
// The redrawn transcript opens with the summary entry, which is what the
|
|
@@ -9258,7 +9445,7 @@ async function saveMaxParallel(deps, limit) {
|
|
|
9258
9445
|
function copySelection(deps, text) {
|
|
9259
9446
|
if (!text) return;
|
|
9260
9447
|
const lines = text.split("\n").length;
|
|
9261
|
-
const command = (0,
|
|
9448
|
+
const command = (0, import_core50.clipboardCopyCommand)(deps.hasBin);
|
|
9262
9449
|
if (command) {
|
|
9263
9450
|
try {
|
|
9264
9451
|
(deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
|
|
@@ -9270,7 +9457,7 @@ function copySelection(deps, text) {
|
|
|
9270
9457
|
deps.writeTerminal(osc52(text));
|
|
9271
9458
|
deps.dispatch({
|
|
9272
9459
|
type: "notice",
|
|
9273
|
-
message: `Copied ${lines} ${lines === 1 ? "line" : "lines"} through the terminal
|
|
9460
|
+
message: `Copied ${lines} ${lines === 1 ? "line" : "lines"} through the terminal: no clipboard tool here (pbcopy, xclip, xsel or wl-copy), so it only lands if your terminal supports OSC 52.`
|
|
9274
9461
|
});
|
|
9275
9462
|
}
|
|
9276
9463
|
async function withExecutionStream(deps, sessionId, request) {
|
|
@@ -9363,7 +9550,7 @@ function lastAssistantMessage(plan) {
|
|
|
9363
9550
|
const history = plan?.conversationHistory ?? [];
|
|
9364
9551
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
9365
9552
|
const entry = history[i];
|
|
9366
|
-
if ((0,
|
|
9553
|
+
if ((0, import_core50.isUserMessage)(entry)) return null;
|
|
9367
9554
|
if (entry.role === "assistant" && entry.kind === void 0) {
|
|
9368
9555
|
const { content, timestamp } = entry;
|
|
9369
9556
|
return { content, ...timestamp ? { timestamp } : {} };
|
|
@@ -9718,7 +9905,7 @@ async function handleTui(subArgs) {
|
|
|
9718
9905
|
}
|
|
9719
9906
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
9720
9907
|
try {
|
|
9721
|
-
(0,
|
|
9908
|
+
(0, import_core51.assertWorkspaceExists)(workspace);
|
|
9722
9909
|
} catch {
|
|
9723
9910
|
console.error(
|
|
9724
9911
|
`Workspace "${workspace}" does not exist.
|
|
@@ -9727,7 +9914,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
9727
9914
|
);
|
|
9728
9915
|
process.exit(1);
|
|
9729
9916
|
}
|
|
9730
|
-
const discoveredSkills = (0,
|
|
9917
|
+
const discoveredSkills = (0, import_core51.createSkillsService)(workspace, new import_core51.EnvConfig().workspaceRepos).listSkills();
|
|
9731
9918
|
registerSkillCommands(discoveredSkills);
|
|
9732
9919
|
const taskSkills = discoveredSkills.filter((s) => s.appliesTo === "task").map((s) => ({ name: s.name, description: s.description }));
|
|
9733
9920
|
const explicitPort = flag(subArgs, "--port") || process.env.ORDEWELL_PORT;
|
|
@@ -9737,24 +9924,37 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
9737
9924
|
const api = new ApiClient(port, workspace);
|
|
9738
9925
|
let terminal;
|
|
9739
9926
|
const mouseCapture = process.env.ORDEWELL_TUI_MOUSE !== "false" && process.env.ORDEWELL_TUI_MOUSE !== "0";
|
|
9927
|
+
const settings = new import_core51.SettingsService();
|
|
9740
9928
|
const app = createApp({
|
|
9741
9929
|
// The level the daemon plans with: the saved one, unless the environment overrides it (ADR-0030).
|
|
9742
|
-
|
|
9930
|
+
// The run settings are seeded from the same file because the first frame is
|
|
9931
|
+
// drawn before the daemon answers, and the welcome's autorun line is only
|
|
9932
|
+
// truthful once they are known. That holds only for a daemon this process
|
|
9933
|
+
// spawned, which inherits its settings path; an attached one may read a
|
|
9934
|
+
// different file (ORDEWELL_SETTINGS_PATH), and nothing it reports names that
|
|
9935
|
+
// file, so the welcome stays silent until its own settings arrive.
|
|
9936
|
+
initial: {
|
|
9937
|
+
workspace,
|
|
9938
|
+
autonomous: settings.autonomousMode(),
|
|
9939
|
+
...ownedAtLaunch ? { runSettings: settings.getRunSettings(), runSettingsKnown: true } : {},
|
|
9940
|
+
mouseCapture,
|
|
9941
|
+
taskSkills
|
|
9942
|
+
},
|
|
9743
9943
|
draw: (frame2) => terminal?.draw(frame2),
|
|
9744
9944
|
perform: (effect) => runEffect(effect, {
|
|
9745
9945
|
api,
|
|
9746
9946
|
workspace,
|
|
9747
9947
|
port,
|
|
9748
9948
|
dispatch: (action) => app.dispatch(action),
|
|
9749
|
-
newSessionId: () => (0,
|
|
9949
|
+
newSessionId: () => (0, import_core51.mintSessionId)(),
|
|
9750
9950
|
setEnvVar: (key, value) => {
|
|
9751
9951
|
writeEnvVar(findEnvFile(), key, value);
|
|
9752
9952
|
process.env[key] = value;
|
|
9753
9953
|
},
|
|
9754
9954
|
saveAutonomy: (enabled) => {
|
|
9755
|
-
const
|
|
9756
|
-
|
|
9757
|
-
return { overridden:
|
|
9955
|
+
const settings2 = new import_core51.SettingsService();
|
|
9956
|
+
settings2.setAutonomousMode(enabled);
|
|
9957
|
+
return { overridden: settings2.autonomousModeOverridden() };
|
|
9758
9958
|
},
|
|
9759
9959
|
setMouseCapture: (enabled) => terminal?.setMouse(enabled),
|
|
9760
9960
|
// The alt screen is up with the cursor hidden, so an OSC 52 write shows
|
|
@@ -9866,7 +10066,7 @@ var COMMANDS = {
|
|
|
9866
10066
|
// src/main.ts
|
|
9867
10067
|
var TUI_FLAGS = /* @__PURE__ */ new Set(["--workspace", "--port"]);
|
|
9868
10068
|
async function main() {
|
|
9869
|
-
(0,
|
|
10069
|
+
(0, import_core52.migrateOldConfigDir)();
|
|
9870
10070
|
moveAutonomyToSettings();
|
|
9871
10071
|
loadEnvFile();
|
|
9872
10072
|
const argv = process.argv.slice(2);
|