@ordewell/cli 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +618 -184
- package/package.json +4 -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_core33 = require("@ordewell/core");
|
|
28
28
|
|
|
29
29
|
// src/utils/env.ts
|
|
30
30
|
var import_fs = require("fs");
|
|
@@ -33,42 +33,56 @@ var import_core = require("@ordewell/core");
|
|
|
33
33
|
function findEnvFile() {
|
|
34
34
|
return (0, import_path.join)((0, import_core.globalDataDir)(), ".env");
|
|
35
35
|
}
|
|
36
|
+
function unquote(value) {
|
|
37
|
+
const quote = value[0];
|
|
38
|
+
return value.length >= 2 && (quote === '"' || quote === "'") && value.endsWith(quote) ? value.slice(1, -1) : value;
|
|
39
|
+
}
|
|
36
40
|
function loadEnvFile() {
|
|
37
41
|
const filePath = findEnvFile();
|
|
38
42
|
if (!(0, import_fs.existsSync)(filePath)) return;
|
|
39
43
|
const content = (0, import_fs.readFileSync)(filePath, "utf8");
|
|
40
44
|
for (const line of content.split("\n")) {
|
|
41
|
-
const trimmed = line.trim();
|
|
45
|
+
const trimmed = line.trim().replace(/^export\s+/, "");
|
|
42
46
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
43
47
|
const eq = trimmed.indexOf("=");
|
|
44
48
|
if (eq === -1) continue;
|
|
45
49
|
const key = trimmed.slice(0, eq).trim();
|
|
46
|
-
const value = trimmed.slice(eq + 1).trim();
|
|
50
|
+
const value = unquote(trimmed.slice(eq + 1).trim());
|
|
47
51
|
if (key && process.env[key] === void 0) {
|
|
48
52
|
process.env[key] = value;
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
}
|
|
56
|
+
var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
57
|
+
function assertValidEnvKey(key) {
|
|
58
|
+
if (!ENV_KEY_PATTERN.test(key)) {
|
|
59
|
+
throw new Error(`"${key}" is not a valid environment variable name.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
52
62
|
function writeEnvVar(filePath, key, value) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
assertValidEnvKey(key);
|
|
64
|
+
if (/[\r\n]/.test(value)) {
|
|
65
|
+
throw new Error(`Refusing to write ${key}: the value contains a newline.`);
|
|
66
|
+
}
|
|
67
|
+
let content = "";
|
|
68
|
+
if ((0, import_fs.existsSync)(filePath)) {
|
|
69
|
+
content = (0, import_fs.readFileSync)(filePath, "utf8");
|
|
70
|
+
const lines = content.split("\n");
|
|
71
|
+
const index = lines.findIndex((line) => line.startsWith(`${key}=`));
|
|
72
|
+
if (index === -1) {
|
|
73
|
+
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
74
|
+
content += `${key}=${value}
|
|
65
75
|
`;
|
|
66
|
-
}
|
|
67
76
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
lines[index] = `${key}=${value}`;
|
|
78
|
+
content = lines.join("\n");
|
|
70
79
|
}
|
|
71
|
-
|
|
80
|
+
} else {
|
|
81
|
+
content = `${key}=${value}
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
(0, import_core.writePrivateFile)(filePath, content);
|
|
72
86
|
} catch (err) {
|
|
73
87
|
console.error(`Could not write to ${filePath}: ${err.message}`);
|
|
74
88
|
}
|
|
@@ -106,6 +120,7 @@ Tasks (<id> is an order number or a task ID):
|
|
|
106
120
|
ordewell force-start <id> Start a task now, ignoring dependencies
|
|
107
121
|
ordewell run-task <id> Run only one task
|
|
108
122
|
ordewell retry <id> Re-run a failed task
|
|
123
|
+
ordewell continue <id> <msg> Continue a finished task in its saved runner session
|
|
109
124
|
ordewell cancel <id> Kill a running task
|
|
110
125
|
ordewell terminal <id> Open a real terminal attached to a task's runner
|
|
111
126
|
|
|
@@ -124,6 +139,7 @@ Planner, models and runners:
|
|
|
124
139
|
ordewell runners [<id> on|off] Enable or disable runners (claude-code, opencode, codex)
|
|
125
140
|
ordewell allowlist set|clear|show Limit which models a runner may use
|
|
126
141
|
ordewell auto [on|off] Autonomous permission mode for new sessions
|
|
142
|
+
ordewell transport [terminal|structured] Experimental: run Claude Code tasks over its protocol, from the next run
|
|
127
143
|
ordewell parallel [<n>] How many AI tasks run at once (default 3; applies to a live run)
|
|
128
144
|
ordewell refresh Re-discover runners and model catalogs
|
|
129
145
|
ordewell models List every provider's catalog (works without a server)
|
|
@@ -583,6 +599,40 @@ var ApiClient = class _ApiClient {
|
|
|
583
599
|
}
|
|
584
600
|
return res.data;
|
|
585
601
|
}
|
|
602
|
+
/** A user message to a structured task: delivered now if it waits for input, else queued behind its turn. */
|
|
603
|
+
async sendTaskMessage(sessionId, taskId, text) {
|
|
604
|
+
const res = await this.httpRequest("POST", `/api/plans/${sessionId}/tasks/${taskId}/messages`, { text });
|
|
605
|
+
if (res.status !== 200) {
|
|
606
|
+
throw new Error(res.data?.error || "Send message failed");
|
|
607
|
+
}
|
|
608
|
+
return res.data;
|
|
609
|
+
}
|
|
610
|
+
/** Continue a finished structured task in its saved session, with `text` as its next turn. */
|
|
611
|
+
async continueTask(sessionId, taskId, text) {
|
|
612
|
+
const res = await this.httpRequest("POST", `/api/plans/${sessionId}/tasks/${taskId}/continue`, { text });
|
|
613
|
+
if (res.status !== 200) {
|
|
614
|
+
throw new Error(res.data?.error || "Continue failed");
|
|
615
|
+
}
|
|
616
|
+
return res.data;
|
|
617
|
+
}
|
|
618
|
+
/** `removed` is false once the message was already delivered. */
|
|
619
|
+
async removeQueuedTaskMessage(sessionId, taskId, messageId) {
|
|
620
|
+
const res = await this.httpRequest(
|
|
621
|
+
"DELETE",
|
|
622
|
+
`/api/plans/${sessionId}/tasks/${taskId}/messages/${encodeURIComponent(messageId)}`
|
|
623
|
+
);
|
|
624
|
+
if (res.status !== 200) {
|
|
625
|
+
throw new Error(res.data?.error || "Remove message failed");
|
|
626
|
+
}
|
|
627
|
+
return res.data;
|
|
628
|
+
}
|
|
629
|
+
async interruptTask(sessionId, taskId) {
|
|
630
|
+
const res = await this.httpRequest("POST", `/api/plans/${sessionId}/tasks/${taskId}/interrupt`);
|
|
631
|
+
if (res.status !== 200) {
|
|
632
|
+
throw new Error(res.data?.error || "Interrupt failed");
|
|
633
|
+
}
|
|
634
|
+
return res.data;
|
|
635
|
+
}
|
|
586
636
|
async addTask(sessionId, task) {
|
|
587
637
|
const res = await this.httpRequest("POST", `/api/plans/${sessionId}/tasks`, task);
|
|
588
638
|
if (res.status !== 200) {
|
|
@@ -742,6 +792,20 @@ var ApiClient = class _ApiClient {
|
|
|
742
792
|
}
|
|
743
793
|
return res.data;
|
|
744
794
|
}
|
|
795
|
+
/** The attempts of a structured task that have a saved log, oldest first. */
|
|
796
|
+
async getTaskLogAttempts(sessionId, taskId, workspace) {
|
|
797
|
+
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
798
|
+
const res = await this.httpRequest("GET", `/api/sessions/${sessionId}/tasks/${encodeURIComponent(taskId)}/log${qs}`);
|
|
799
|
+
if (res.status !== 200) throw new Error(res.data?.error || "Could not read the task log");
|
|
800
|
+
return res.data.attempts;
|
|
801
|
+
}
|
|
802
|
+
/** One attempt's saved log, for `replayTaskLog`. */
|
|
803
|
+
async getTaskLog(sessionId, taskId, attempt, workspace) {
|
|
804
|
+
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
805
|
+
const res = await this.httpRequest("GET", `/api/sessions/${sessionId}/tasks/${encodeURIComponent(taskId)}/log/${attempt}${qs}`);
|
|
806
|
+
if (res.status !== 200) throw new Error(res.data?.error || "Could not read the task log");
|
|
807
|
+
return res.data.events;
|
|
808
|
+
}
|
|
745
809
|
/**
|
|
746
810
|
* The one place a session socket is built, so the token travels on both
|
|
747
811
|
* stream paths. It rides as a subprotocol rather than a query parameter, so
|
|
@@ -808,12 +872,16 @@ var ApiClient = class _ApiClient {
|
|
|
808
872
|
});
|
|
809
873
|
});
|
|
810
874
|
}
|
|
811
|
-
/**
|
|
812
|
-
|
|
875
|
+
/**
|
|
876
|
+
* Answer an approval prompt: the planner's with a yes/no, a task runner's
|
|
877
|
+
* with the whole decision (ADR-0018, A1). The planner's requests arrive over
|
|
878
|
+
* the session socket; a runner's, in its task log.
|
|
879
|
+
*/
|
|
880
|
+
async respondToApproval(sessionId, approvalId, answer) {
|
|
813
881
|
const { status, data } = await this.httpRequest(
|
|
814
882
|
"POST",
|
|
815
883
|
`/api/approvals/${encodeURIComponent(sessionId)}/${encodeURIComponent(approvalId)}`,
|
|
816
|
-
{ granted }
|
|
884
|
+
typeof answer === "boolean" ? { granted: answer } : answer
|
|
817
885
|
);
|
|
818
886
|
if (status !== 200) throw new Error(`Failed to answer approval ${approvalId} (HTTP ${status})`);
|
|
819
887
|
return data;
|
|
@@ -1931,6 +1999,7 @@ ${providerLabel(provider)}`);
|
|
|
1931
1999
|
|
|
1932
2000
|
// src/commands/setup.ts
|
|
1933
2001
|
var import_fs6 = require("fs");
|
|
2002
|
+
var import_core13 = require("@ordewell/core");
|
|
1934
2003
|
function handleSetup(subArgs) {
|
|
1935
2004
|
const modelIdx = subArgs.indexOf("--model");
|
|
1936
2005
|
const chosenModel = modelIdx !== -1 ? subArgs[modelIdx + 1] : null;
|
|
@@ -2002,15 +2071,15 @@ function handleSetup(subArgs) {
|
|
|
2002
2071
|
# STEPFUN_API_KEY=...
|
|
2003
2072
|
# SILICONFLOW_API_KEY=...
|
|
2004
2073
|
`;
|
|
2005
|
-
(0,
|
|
2074
|
+
(0, import_core13.writePrivateFile)(envFile, content);
|
|
2006
2075
|
console.log(`Wrote: ${envFile}`);
|
|
2007
2076
|
console.log("Review and edit the file, then run: ordewell tui");
|
|
2008
2077
|
}
|
|
2009
2078
|
|
|
2010
2079
|
// src/commands/plugins.ts
|
|
2011
|
-
var
|
|
2080
|
+
var import_core14 = require("@ordewell/core");
|
|
2012
2081
|
function handlePlugins(subArgs) {
|
|
2013
|
-
const registry = new
|
|
2082
|
+
const registry = new import_core14.RunnerRegistry();
|
|
2014
2083
|
registry.loadUserPlugins();
|
|
2015
2084
|
const sub = subArgs[0];
|
|
2016
2085
|
if (sub === "list" || !sub) {
|
|
@@ -2046,7 +2115,7 @@ ${plugins.length} plugin(s):
|
|
|
2046
2115
|
process.exit(1);
|
|
2047
2116
|
}
|
|
2048
2117
|
try {
|
|
2049
|
-
const resolved = (0,
|
|
2118
|
+
const resolved = (0, import_core14.classifyPluginSource)(source);
|
|
2050
2119
|
const manifest = resolved.kind === "git" ? registry.installFromGit(resolved.url) : registry.installFromPath(resolved.path);
|
|
2051
2120
|
console.log(`Installed plugin: ${manifest.name} v${manifest.version}`);
|
|
2052
2121
|
} catch (err) {
|
|
@@ -2207,6 +2276,22 @@ async function handleVerify(subArgs, api) {
|
|
|
2207
2276
|
}
|
|
2208
2277
|
}
|
|
2209
2278
|
|
|
2279
|
+
// src/commands/transport.ts
|
|
2280
|
+
var describe = (transport) => `Runner transport (experimental): ${transport === "structured" ? "structured" : "terminal"} \u2014 applies from the next run`;
|
|
2281
|
+
async function handleTransport(subArgs, api) {
|
|
2282
|
+
const action = subArgs[0];
|
|
2283
|
+
const port = api ? void 0 : await ensureDaemon(resolvePort(subArgs));
|
|
2284
|
+
const client = api || new ApiClient(port);
|
|
2285
|
+
if (action === "terminal" || action === "structured") {
|
|
2286
|
+
const result = await client.sendCommand("transport", { action });
|
|
2287
|
+
console.log(describe(result.settings?.runnerTransport));
|
|
2288
|
+
} else {
|
|
2289
|
+
const settings = await client.getSettings();
|
|
2290
|
+
console.log(describe(settings.runnerTransport));
|
|
2291
|
+
console.log("Usage: ordewell transport [terminal|structured]");
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2210
2295
|
// src/commands/task-control.ts
|
|
2211
2296
|
var PAST_TENSE = {
|
|
2212
2297
|
run: "Task started.",
|
|
@@ -2263,6 +2348,23 @@ function makeHandler(action, command = action) {
|
|
|
2263
2348
|
);
|
|
2264
2349
|
};
|
|
2265
2350
|
}
|
|
2351
|
+
async function handleContinue(subArgs, injectedApi) {
|
|
2352
|
+
const usage = "Usage: ordewell continue <task-id-or-order> <message> [--session-id <id>] [--workspace /path]";
|
|
2353
|
+
const message = positionals(subArgs).slice(1).join(" ").trim();
|
|
2354
|
+
if (!message) {
|
|
2355
|
+
console.error(usage);
|
|
2356
|
+
process.exit(1);
|
|
2357
|
+
}
|
|
2358
|
+
await withResolvedTask(subArgs, usage, injectedApi, async (api, sessionId, taskId) => {
|
|
2359
|
+
try {
|
|
2360
|
+
await api.continueTask(sessionId, taskId, message);
|
|
2361
|
+
console.log("Task continued.");
|
|
2362
|
+
} catch (err) {
|
|
2363
|
+
console.error(`Failed to continue task: ${err.message}`);
|
|
2364
|
+
process.exit(1);
|
|
2365
|
+
}
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2266
2368
|
var handleRunTask = makeHandler("run", "run-task");
|
|
2267
2369
|
var handleForceStart = makeHandler("force-start");
|
|
2268
2370
|
var handleRetry = makeHandler("retry");
|
|
@@ -2515,7 +2617,7 @@ async function handleRewind(subArgs, injectedApi) {
|
|
|
2515
2617
|
var import_readline2 = require("readline");
|
|
2516
2618
|
|
|
2517
2619
|
// src/tui/transcript.ts
|
|
2518
|
-
var
|
|
2620
|
+
var import_core15 = require("@ordewell/core");
|
|
2519
2621
|
function show(state, { view: conversation, gate: turnGate }) {
|
|
2520
2622
|
const gated = turnGate === state.turnGate ? state : { ...state, turnGate };
|
|
2521
2623
|
if (conversation === state.conversation) return gated;
|
|
@@ -2523,16 +2625,16 @@ function show(state, { view: conversation, gate: turnGate }) {
|
|
|
2523
2625
|
return { ...gated, conversation, scroll };
|
|
2524
2626
|
}
|
|
2525
2627
|
function fold(state, input) {
|
|
2526
|
-
return show(state, { view: (0,
|
|
2628
|
+
return show(state, { view: (0, import_core15.reduceConversation)(state.conversation, input), gate: state.turnGate });
|
|
2527
2629
|
}
|
|
2528
2630
|
function say(state, role, text) {
|
|
2529
2631
|
return fold(state, { type: "local_entry", role, text });
|
|
2530
2632
|
}
|
|
2531
2633
|
function hear(state, message) {
|
|
2532
|
-
return show(state, (0,
|
|
2634
|
+
return show(state, (0, import_core15.followTurn)(state.conversation, state.turnGate, message));
|
|
2533
2635
|
}
|
|
2534
2636
|
function cutTurn(state) {
|
|
2535
|
-
return show(state, (0,
|
|
2637
|
+
return show(state, (0, import_core15.stopTurn)(state.conversation, state.turnGate));
|
|
2536
2638
|
}
|
|
2537
2639
|
function wiped(view) {
|
|
2538
2640
|
return { ...view, blocks: view.blocks.filter((b) => b.type === "usage") };
|
|
@@ -2677,7 +2779,13 @@ var style = {
|
|
|
2677
2779
|
blue: colour("34"),
|
|
2678
2780
|
magenta: colour("35"),
|
|
2679
2781
|
cyan: colour("36"),
|
|
2680
|
-
grey: colour("90")
|
|
2782
|
+
grey: colour("90"),
|
|
2783
|
+
/**
|
|
2784
|
+
* The task view's accent: a bright blue, separate from the planner chat's
|
|
2785
|
+
* cyan and magenta. Bright rather than the plain 34, which reads as black on
|
|
2786
|
+
* a dark background.
|
|
2787
|
+
*/
|
|
2788
|
+
accent: colour("94")
|
|
2681
2789
|
};
|
|
2682
2790
|
|
|
2683
2791
|
// src/tui/handoff.ts
|
|
@@ -2927,7 +3035,7 @@ async function handleHandoff(subArgs, injectedApi, confirm = askYesNo) {
|
|
|
2927
3035
|
}
|
|
2928
3036
|
|
|
2929
3037
|
// src/commands/planner.ts
|
|
2930
|
-
var
|
|
3038
|
+
var import_core16 = require("@ordewell/core");
|
|
2931
3039
|
|
|
2932
3040
|
// src/plannerModelSwitch.ts
|
|
2933
3041
|
function describePlannerSwitch(settings, provider) {
|
|
@@ -2946,7 +3054,7 @@ function rememberedModelFor(settings, provider) {
|
|
|
2946
3054
|
}
|
|
2947
3055
|
|
|
2948
3056
|
// src/commands/planner.ts
|
|
2949
|
-
var KNOWN_PROVIDERS = Object.keys(
|
|
3057
|
+
var KNOWN_PROVIDERS = Object.keys(import_core16.ALL_PROVIDERS);
|
|
2950
3058
|
async function handlePlanner(subArgs, injectedApi) {
|
|
2951
3059
|
const api = await connect(subArgs, injectedApi);
|
|
2952
3060
|
const [wanted] = positionals(subArgs);
|
|
@@ -2961,13 +3069,13 @@ async function handlePlanner(subArgs, injectedApi) {
|
|
|
2961
3069
|
const settings = await persistEnv(api, { AI_PROVIDER: provider });
|
|
2962
3070
|
const recall2 = describePlannerSwitch(settings, provider);
|
|
2963
3071
|
writeEnv({ ORCHESTRATOR_MODEL: recall2.model, ORDEWELL_PLANNER_EFFORT: recall2.effort });
|
|
2964
|
-
const meta =
|
|
3072
|
+
const meta = import_core16.ALL_PROVIDERS[provider];
|
|
2965
3073
|
const catalog = await fetchCatalog(api);
|
|
2966
3074
|
console.log(
|
|
2967
|
-
(0,
|
|
3075
|
+
(0, import_core16.isCliProvider)(provider) ? `Planning with ${meta.label} \u2014 no API key needed, it uses that agent's own subscription.` : `Planner set to ${meta.label}.`
|
|
2968
3076
|
);
|
|
2969
3077
|
console.log(plannerModelLine(recall2));
|
|
2970
|
-
if (!(0,
|
|
3078
|
+
if (!(0, import_core16.isCliProvider)(provider) && !catalog.providers.includes(provider)) {
|
|
2971
3079
|
console.log(` No ${meta.apiKeyEnvVar} configured yet \u2014 set one with \`ordewell key set ${provider} <key>\`.`);
|
|
2972
3080
|
}
|
|
2973
3081
|
}
|
|
@@ -2985,15 +3093,15 @@ async function showPlanners(api) {
|
|
|
2985
3093
|
const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
|
|
2986
3094
|
const current = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
2987
3095
|
console.log("\nCoding agents (no API key \u2014 they use their own subscription)");
|
|
2988
|
-
for (const id of
|
|
2989
|
-
const runner = (0,
|
|
3096
|
+
for (const id of import_core16.CLI_PROVIDERS) {
|
|
3097
|
+
const runner = (0, import_core16.runnerForProvider)(id);
|
|
2990
3098
|
const discovered = runner ? catalog.models.some((m) => m.runners?.includes(runner)) : false;
|
|
2991
|
-
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${
|
|
3099
|
+
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${import_core16.ALL_PROVIDERS[id].label}${discovered ? "" : " (not detected)"}`);
|
|
2992
3100
|
}
|
|
2993
3101
|
console.log("\nAPI providers");
|
|
2994
|
-
for (const id of
|
|
3102
|
+
for (const id of import_core16.PROVIDER_PRIORITY) {
|
|
2995
3103
|
const configured = catalog.providers.includes(id);
|
|
2996
|
-
const meta =
|
|
3104
|
+
const meta = import_core16.ALL_PROVIDERS[id];
|
|
2997
3105
|
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${meta.label}${configured ? "" : ` (no ${meta.apiKeyEnvVar})`}`);
|
|
2998
3106
|
}
|
|
2999
3107
|
console.log(`
|
|
@@ -3010,7 +3118,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
3010
3118
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
3011
3119
|
const modelId = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
|
|
3012
3120
|
const currentEffort = typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : "";
|
|
3013
|
-
const runner = (0,
|
|
3121
|
+
const runner = (0, import_core16.runnerForProvider)(provider);
|
|
3014
3122
|
if (!runner) {
|
|
3015
3123
|
fail(
|
|
3016
3124
|
"Thinking effort applies to a coding-agent planner.",
|
|
@@ -3050,7 +3158,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
3050
3158
|
}
|
|
3051
3159
|
|
|
3052
3160
|
// src/commands/parallel.ts
|
|
3053
|
-
var
|
|
3161
|
+
var import_core17 = require("@ordewell/core");
|
|
3054
3162
|
var USAGE5 = "Usage: ordewell parallel [<number of tasks, 1 or more>]";
|
|
3055
3163
|
async function handleParallel(subArgs, injectedApi) {
|
|
3056
3164
|
const [wanted] = positionals(subArgs);
|
|
@@ -3060,14 +3168,14 @@ async function handleParallel(subArgs, injectedApi) {
|
|
|
3060
3168
|
console.log(`Up to ${String(settings.maxParallel ?? "?")} AI tasks run at once. Change with: ordewell parallel <n>`);
|
|
3061
3169
|
return;
|
|
3062
3170
|
}
|
|
3063
|
-
const limit = (0,
|
|
3171
|
+
const limit = (0, import_core17.parseMaxParallel)(wanted);
|
|
3064
3172
|
if (limit === null) fail(`"${wanted}" is not a number of tasks (1 or more).`, USAGE5);
|
|
3065
3173
|
await persistEnv(api, { ORDEWELL_MAX_PARALLEL: String(limit) });
|
|
3066
3174
|
console.log(`Up to ${limit} AI task${limit === 1 ? "" : "s"} now run at once.`);
|
|
3067
3175
|
}
|
|
3068
3176
|
|
|
3069
3177
|
// src/commands/key.ts
|
|
3070
|
-
var
|
|
3178
|
+
var import_core18 = require("@ordewell/core");
|
|
3071
3179
|
var USAGE6 = "Usage: ordewell key [list | set <provider> <key>]";
|
|
3072
3180
|
async function handleKey(subArgs, injectedApi) {
|
|
3073
3181
|
const [action, provider, ...keyParts] = positionals(subArgs);
|
|
@@ -3078,8 +3186,8 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
3078
3186
|
if (action !== "set") {
|
|
3079
3187
|
const catalog = await fetchCatalog(api);
|
|
3080
3188
|
console.log("\nAPI provider keys\n");
|
|
3081
|
-
for (const id2 of
|
|
3082
|
-
const meta2 =
|
|
3189
|
+
for (const id2 of import_core18.PROVIDER_PRIORITY) {
|
|
3190
|
+
const meta2 = import_core18.ALL_PROVIDERS[id2];
|
|
3083
3191
|
const configured = catalog.providers.includes(id2);
|
|
3084
3192
|
console.log(` ${configured ? "\u2713" : " "} ${id2.padEnd(14)} ${meta2.apiKeyEnvVar.padEnd(28)} ${meta2.label}`);
|
|
3085
3193
|
}
|
|
@@ -3089,7 +3197,7 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
3089
3197
|
}
|
|
3090
3198
|
if (!provider) fail(USAGE6);
|
|
3091
3199
|
const id = provider.toLowerCase();
|
|
3092
|
-
const meta =
|
|
3200
|
+
const meta = import_core18.ALL_PROVIDERS[id];
|
|
3093
3201
|
if (!meta) {
|
|
3094
3202
|
fail(`Unknown provider: ${provider}`, "Run `ordewell key` to see the list.");
|
|
3095
3203
|
}
|
|
@@ -3170,7 +3278,7 @@ async function handleApprove(subArgs, injectedApi) {
|
|
|
3170
3278
|
// src/tui/terminalLauncher.ts
|
|
3171
3279
|
var import_child_process3 = require("child_process");
|
|
3172
3280
|
var import_util = require("util");
|
|
3173
|
-
var
|
|
3281
|
+
var import_core19 = require("@ordewell/core");
|
|
3174
3282
|
var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
|
|
3175
3283
|
var defaultExecFile = async (command, args) => {
|
|
3176
3284
|
const { stdout, stderr } = await execFileAsync(command, args);
|
|
@@ -3190,16 +3298,16 @@ var defaultWhich = (bin) => {
|
|
|
3190
3298
|
async function openTaskTerminal(port, planSessionId, taskId, deps = {}) {
|
|
3191
3299
|
const platform = deps.platform ?? process.platform;
|
|
3192
3300
|
const env = deps.env ?? process.env;
|
|
3193
|
-
const hasTmuxImpl = deps.hasTmuxImpl ??
|
|
3301
|
+
const hasTmuxImpl = deps.hasTmuxImpl ?? import_core19.hasTmux;
|
|
3194
3302
|
const execFileImpl = deps.execFileImpl ?? defaultExecFile;
|
|
3195
3303
|
const spawnImpl = deps.spawnImpl ?? defaultSpawn;
|
|
3196
3304
|
const which = deps.which ?? defaultWhich;
|
|
3197
3305
|
if (!hasTmuxImpl()) {
|
|
3198
3306
|
return { ok: false, message: "tmux is not installed \u2014 install it to get a real terminal per task." };
|
|
3199
3307
|
}
|
|
3200
|
-
const session = (0,
|
|
3201
|
-
const socket = (0,
|
|
3202
|
-
const base = (0,
|
|
3308
|
+
const session = (0, import_core19.tmuxSessionName)(port);
|
|
3309
|
+
const socket = (0, import_core19.tmuxSocketName)(port);
|
|
3310
|
+
const base = (0, import_core19.tmuxWindowName)(taskId, planSessionId);
|
|
3203
3311
|
let windows;
|
|
3204
3312
|
try {
|
|
3205
3313
|
const { stdout } = await execFileImpl("tmux", ["-L", socket, "list-windows", "-t", session, "-F", "#{window_name}"]);
|
|
@@ -3493,7 +3601,7 @@ Dependencies \xB7 #${task.order} ${task.title}
|
|
|
3493
3601
|
}
|
|
3494
3602
|
|
|
3495
3603
|
// src/tui/index.ts
|
|
3496
|
-
var
|
|
3604
|
+
var import_core32 = require("@ordewell/core");
|
|
3497
3605
|
|
|
3498
3606
|
// src/tui/editor.ts
|
|
3499
3607
|
function emptyEditor() {
|
|
@@ -3641,7 +3749,8 @@ var SLASH_COMMANDS = [
|
|
|
3641
3749
|
{ name: "retry", usage: "/retry <id>", description: "Re-run a failed task", category: "tasks" },
|
|
3642
3750
|
{ name: "cancel", usage: "/cancel <id>", description: "Kill a running task", category: "tasks" },
|
|
3643
3751
|
{ name: "force-start", usage: "/force-start <id>", description: "Start a task now, ignoring dependencies", category: "tasks" },
|
|
3644
|
-
{ name: "
|
|
3752
|
+
{ name: "continue", usage: "/continue <id> <message>", description: "Continue a finished task in its saved runner session, with your message", category: "tasks" },
|
|
3753
|
+
{ name: "terminal", usage: "/terminal <id>", description: "Open a task: its OS terminal, or a structured task's live log", category: "tasks" },
|
|
3645
3754
|
{ name: "task-runner", usage: "/task-runner <id> [runner]", description: "Choose or set a task's executor (re-picks its model and mode)", category: "tasks" },
|
|
3646
3755
|
{ name: "task-model", usage: "/task-model <id> [model]", description: "Choose or set a task's executor model", category: "tasks" },
|
|
3647
3756
|
{ name: "task-effort", usage: "/task-effort <id> [level]", description: "Choose or set a task's thinking effort", category: "tasks" },
|
|
@@ -3655,6 +3764,7 @@ var SLASH_COMMANDS = [
|
|
|
3655
3764
|
{ name: "allowlist", usage: "/allowlist [set <runner> <ids> | clear <runner>]", description: "Limit which models a runner may use", category: "models" },
|
|
3656
3765
|
{ name: "runners", usage: "/runners [<id> on|off]", description: "Enable or disable runners (claude-code, opencode, codex)", category: "models" },
|
|
3657
3766
|
{ name: "auto", usage: "/auto [on|off]", description: "Autonomous permission mode for new plans", category: "models" },
|
|
3767
|
+
{ name: "transport", usage: "/transport [terminal|structured]", description: "Experimental: run Claude Code tasks over its protocol instead of a terminal, from the next run", category: "models" },
|
|
3658
3768
|
{ name: "parallel", usage: "/parallel [<n>]", description: "How many AI tasks run at once (applies to a live run)", category: "models" },
|
|
3659
3769
|
{ name: "refresh", usage: "/refresh", description: "Re-discover runners and model catalogs", category: "models" },
|
|
3660
3770
|
// Skills
|
|
@@ -3739,7 +3849,7 @@ function tokenCompletions(token) {
|
|
|
3739
3849
|
}
|
|
3740
3850
|
|
|
3741
3851
|
// src/tui/blocks.ts
|
|
3742
|
-
var
|
|
3852
|
+
var import_core20 = require("@ordewell/core");
|
|
3743
3853
|
|
|
3744
3854
|
// src/tui/markdown.ts
|
|
3745
3855
|
function renderMarkdown(source, cols) {
|
|
@@ -3872,13 +3982,14 @@ function tailOf(text, max) {
|
|
|
3872
3982
|
var ROLE_PREFIX = {
|
|
3873
3983
|
user: (t) => `${style.cyan("\u276F")} ${t}`,
|
|
3874
3984
|
planner: (t) => `${style.magenta("\u25C6")} ${t}`,
|
|
3985
|
+
agent: (t) => `${style.magenta("\u25C6")} ${t}`,
|
|
3875
3986
|
system: (t) => style.grey(`\xB7 ${t}`),
|
|
3876
3987
|
error: (t) => `${style.red("\u2717")} ${style.red(t)}`
|
|
3877
3988
|
};
|
|
3878
3989
|
function messageLines(block, cols) {
|
|
3879
3990
|
const text = sanitize(block.text);
|
|
3880
3991
|
const room = Math.max(1, cols - 2);
|
|
3881
|
-
const wrapped = block.role === "planner" ? renderMarkdown(text, room) : wrap(text, room);
|
|
3992
|
+
const wrapped = block.role === "planner" || block.role === "agent" ? renderMarkdown(text, room) : wrap(text, room);
|
|
3882
3993
|
return wrapped.map((line, i) => i === 0 ? ROLE_PREFIX[block.role](line) : ` ${line}`);
|
|
3883
3994
|
}
|
|
3884
3995
|
function thinkingLines(block, cols, detailAll) {
|
|
@@ -3946,16 +4057,16 @@ function resultLines(block, room, detailAll) {
|
|
|
3946
4057
|
const fit2 = (line) => detailAll ? wrap(line, room) : [truncate(line, room)];
|
|
3947
4058
|
const outcome = unsettledOutcome(block);
|
|
3948
4059
|
if (outcome) {
|
|
3949
|
-
const [reason, ...rest] = (0,
|
|
4060
|
+
const [reason, ...rest] = (0, import_core20.outputLines)(block.output).map(sanitize);
|
|
3950
4061
|
const status = fit2(reason ? `${outcome.label}: ${reason}` : outcome.label).map(outcome.paint);
|
|
3951
4062
|
return detailAll ? [...status, ...rest.flatMap(fit2).map(style.grey)] : status;
|
|
3952
4063
|
}
|
|
3953
4064
|
if (detailAll) {
|
|
3954
|
-
const lines2 = (0,
|
|
4065
|
+
const lines2 = (0, import_core20.outputLines)(block.output).map(sanitize);
|
|
3955
4066
|
const note = lines2.length > PREVIEW_LINES ? ["\u2026 (ctrl+o to collapse)"] : [];
|
|
3956
4067
|
return lines2.length > 0 ? [...lines2.flatMap(fit2), ...note].map(style.grey) : [style.grey("(no output)")];
|
|
3957
4068
|
}
|
|
3958
|
-
const { lines, hiddenLineCount } = (0,
|
|
4069
|
+
const { lines, hiddenLineCount } = (0, import_core20.outputPreview)(block.output, PREVIEW_LINES);
|
|
3959
4070
|
if (lines.length === 0) return [style.grey("(no output)")];
|
|
3960
4071
|
const more = hiddenLineCount > 0 ? [`\u2026 +${countOf(hiddenLineCount, "line")} (ctrl+o to expand)`] : [];
|
|
3961
4072
|
return [...lines.map(sanitize), ...more].map((line) => style.grey(truncate(line, room)));
|
|
@@ -3996,7 +4107,8 @@ function subagentLines(block, cols, detailAll) {
|
|
|
3996
4107
|
var APPROVAL_KIND = {
|
|
3997
4108
|
shell_command: "Run a command",
|
|
3998
4109
|
url_fetch: "Fetch a URL",
|
|
3999
|
-
external_path: "Read outside the workspace"
|
|
4110
|
+
external_path: "Read outside the workspace",
|
|
4111
|
+
runner_tool: "Use a tool"
|
|
4000
4112
|
};
|
|
4001
4113
|
var DECIDED_BY = {
|
|
4002
4114
|
"pre-approved": "pre-approved",
|
|
@@ -4006,8 +4118,9 @@ var DECIDED_BY = {
|
|
|
4006
4118
|
};
|
|
4007
4119
|
function approvalLine(block, cols) {
|
|
4008
4120
|
const policy = block.decidedBy && block.decidedBy !== "asked" ? DECIDED_BY[block.decidedBy] : void 0;
|
|
4009
|
-
const [mark, status, paint] = block.status === "pending" ? ["?", "Waiting for you", style.yellow] : block.status === "granted" ? ["\u2713", policy ? `Auto-approved (${policy})` : "Approved", style.green] : ["\u2298", policy ? `Auto-denied (${policy})` : "Denied", style.yellow];
|
|
4010
|
-
const
|
|
4121
|
+
const [mark, status, paint] = block.status === "pending" ? ["?", "Waiting for you", style.yellow] : block.status === "granted" ? ["\u2713", policy ? `Auto-approved (${policy})` : block.forTask ? "Approved for this task" : "Approved", style.green] : block.status === "withdrawn" ? ["\xB7", "Withdrawn", style.grey] : ["\u2298", policy ? `Auto-denied (${policy})` : "Denied", style.yellow];
|
|
4122
|
+
const note = block.note ? ` \u2014 \u201C${firstLine(block.note)}\u201D` : "";
|
|
4123
|
+
const line = `${mark} ${status} \xB7 ${APPROVAL_KIND[block.kind]}: ${firstLine(block.subject)}${note}`;
|
|
4011
4124
|
return `${paint(mark)}${truncate(line, cols).slice(mark.length)}`;
|
|
4012
4125
|
}
|
|
4013
4126
|
function planLine(block, cols) {
|
|
@@ -4073,38 +4186,8 @@ function tokenLine(blocks, cols) {
|
|
|
4073
4186
|
return parts.length > 0 ? style.grey(truncate(parts.join(" \xB7 "), cols)) : null;
|
|
4074
4187
|
}
|
|
4075
4188
|
|
|
4076
|
-
// src/tui/geometry.ts
|
|
4077
|
-
var PLAN_MIN_COLS = 32;
|
|
4078
|
-
var PLAN_MAX_COLS = 64;
|
|
4079
|
-
var PANE_INDENT = 4;
|
|
4080
|
-
var CHAT_PROMPT_COLS = 2;
|
|
4081
|
-
function planPaneWidth(state) {
|
|
4082
|
-
if (state.tasks.length === 0) return 0;
|
|
4083
|
-
const wanted = Math.min(PLAN_MAX_COLS, Math.floor(state.cols * 0.46));
|
|
4084
|
-
return wanted >= PLAN_MIN_COLS && state.cols - wanted > PLAN_MIN_COLS ? wanted : 0;
|
|
4085
|
-
}
|
|
4086
|
-
function chatPaneWidth(state) {
|
|
4087
|
-
const plan = planPaneWidth(state);
|
|
4088
|
-
return plan === 0 ? state.cols : state.cols - plan - 1;
|
|
4089
|
-
}
|
|
4090
|
-
function paneColumns(state, pane) {
|
|
4091
|
-
const plan = planPaneWidth(state);
|
|
4092
|
-
if (plan === 0) return { first: 1, last: state.cols };
|
|
4093
|
-
const chat = chatPaneWidth(state);
|
|
4094
|
-
return pane === "chat" ? { first: 1, last: chat } : { first: chat + 2, last: state.cols };
|
|
4095
|
-
}
|
|
4096
|
-
function paneTextRoom(paneCols) {
|
|
4097
|
-
return Math.max(1, paneCols - PANE_INDENT);
|
|
4098
|
-
}
|
|
4099
|
-
function taskEditorRoom(state) {
|
|
4100
|
-
return paneTextRoom(planPaneWidth(state));
|
|
4101
|
-
}
|
|
4102
|
-
function chatEditorRoom(cols, active) {
|
|
4103
|
-
return Math.max(1, cols - CHAT_PROMPT_COLS - (active ? 1 : 0));
|
|
4104
|
-
}
|
|
4105
|
-
|
|
4106
4189
|
// src/tui/state.ts
|
|
4107
|
-
var
|
|
4190
|
+
var import_core21 = require("@ordewell/core");
|
|
4108
4191
|
var SKILL_IDS = ["tdd", "verify"];
|
|
4109
4192
|
function noSkills() {
|
|
4110
4193
|
return { tdd: false, verify: false };
|
|
@@ -4134,6 +4217,12 @@ function planRows(state) {
|
|
|
4134
4217
|
function selectedPlanRow(state) {
|
|
4135
4218
|
return planRows(state)[state.selectedTask] ?? null;
|
|
4136
4219
|
}
|
|
4220
|
+
function waitingApproval(tv) {
|
|
4221
|
+
return tv.view.blocks.find((b) => b.type === "approval" && b.status === "pending" && b.approvalId !== void 0);
|
|
4222
|
+
}
|
|
4223
|
+
function continuesTask(task) {
|
|
4224
|
+
return task?.continuable === true && (task.status === "completed" || task.status === "failed");
|
|
4225
|
+
}
|
|
4137
4226
|
function findTask(tasks, id) {
|
|
4138
4227
|
for (const task of tasks) {
|
|
4139
4228
|
if (task.id === id) return task;
|
|
@@ -4145,8 +4234,9 @@ function findTask(tasks, id) {
|
|
|
4145
4234
|
function initialState(overrides = {}) {
|
|
4146
4235
|
return {
|
|
4147
4236
|
editor: emptyEditor(),
|
|
4148
|
-
conversation:
|
|
4149
|
-
|
|
4237
|
+
conversation: import_core21.EMPTY_CONVERSATION,
|
|
4238
|
+
taskView: null,
|
|
4239
|
+
turnGate: import_core21.NO_TURN,
|
|
4150
4240
|
detailAll: false,
|
|
4151
4241
|
status: "idle",
|
|
4152
4242
|
busyLabel: "",
|
|
@@ -4171,17 +4261,18 @@ function initialState(overrides = {}) {
|
|
|
4171
4261
|
orchestratorModel: "",
|
|
4172
4262
|
plannerProvider: "",
|
|
4173
4263
|
plannerEffort: "",
|
|
4174
|
-
maxParallel:
|
|
4264
|
+
maxParallel: import_core21.DEFAULT_MAX_PARALLEL,
|
|
4175
4265
|
configuredProviders: [],
|
|
4176
4266
|
allowlist: {},
|
|
4177
4267
|
autonomous: true,
|
|
4268
|
+
runnerTransport: "terminal",
|
|
4178
4269
|
mouseCapture: true,
|
|
4179
4270
|
selection: null,
|
|
4180
4271
|
workspace: process.cwd(),
|
|
4181
4272
|
handoff: null,
|
|
4182
4273
|
overlay: null,
|
|
4183
4274
|
pendingApprovals: [],
|
|
4184
|
-
queuedPrompts:
|
|
4275
|
+
queuedPrompts: import_core21.EMPTY_HOLD,
|
|
4185
4276
|
stopArmed: false,
|
|
4186
4277
|
stopArmToken: 0,
|
|
4187
4278
|
stopRequested: false,
|
|
@@ -4194,19 +4285,53 @@ function initialState(overrides = {}) {
|
|
|
4194
4285
|
};
|
|
4195
4286
|
}
|
|
4196
4287
|
|
|
4288
|
+
// src/tui/geometry.ts
|
|
4289
|
+
var PLAN_MIN_COLS = 32;
|
|
4290
|
+
var PLAN_MAX_COLS = 64;
|
|
4291
|
+
var PANE_INDENT = 4;
|
|
4292
|
+
function planPaneWidth(state) {
|
|
4293
|
+
if (state.tasks.length === 0) return 0;
|
|
4294
|
+
const wanted = Math.min(PLAN_MAX_COLS, Math.floor(state.cols * 0.46));
|
|
4295
|
+
return wanted >= PLAN_MIN_COLS && state.cols - wanted > PLAN_MIN_COLS ? wanted : 0;
|
|
4296
|
+
}
|
|
4297
|
+
function chatPaneWidth(state) {
|
|
4298
|
+
const plan = planPaneWidth(state);
|
|
4299
|
+
return plan === 0 ? state.cols : state.cols - plan - 1;
|
|
4300
|
+
}
|
|
4301
|
+
function paneColumns(state, pane) {
|
|
4302
|
+
const plan = planPaneWidth(state);
|
|
4303
|
+
if (plan === 0) return { first: 1, last: state.cols };
|
|
4304
|
+
const chat = chatPaneWidth(state);
|
|
4305
|
+
return pane === "chat" ? { first: 1, last: chat } : { first: chat + 2, last: state.cols };
|
|
4306
|
+
}
|
|
4307
|
+
function paneTextRoom(paneCols) {
|
|
4308
|
+
return Math.max(1, paneCols - PANE_INDENT);
|
|
4309
|
+
}
|
|
4310
|
+
function taskEditorRoom(state) {
|
|
4311
|
+
return paneTextRoom(planPaneWidth(state));
|
|
4312
|
+
}
|
|
4313
|
+
function chatPromptLabel(state) {
|
|
4314
|
+
if (!state.taskView) return "\u276F";
|
|
4315
|
+
const task = findTask(state.tasks, state.taskView.taskId);
|
|
4316
|
+
return `\u2192 ${continuesTask(task) ? "Continue task" : "Task"} ${task?.order ?? "?"}`;
|
|
4317
|
+
}
|
|
4318
|
+
function chatEditorRoomFor(state, active) {
|
|
4319
|
+
return Math.max(1, state.cols - width(chatPromptLabel(state)) - 1 - (active ? 1 : 0));
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4197
4322
|
// src/tui/layout.ts
|
|
4198
|
-
var
|
|
4323
|
+
var import_core22 = require("@ordewell/core");
|
|
4199
4324
|
var CHROME_ROWS = 4;
|
|
4200
4325
|
function chatInputWrap(state) {
|
|
4201
4326
|
const active = state.focus === "chat" && !state.overlay;
|
|
4202
4327
|
if (!active) return null;
|
|
4203
|
-
return wrapLines(state.editor.text,
|
|
4328
|
+
return wrapLines(state.editor.text, chatEditorRoomFor(state, true));
|
|
4204
4329
|
}
|
|
4205
4330
|
function footerHints(state) {
|
|
4206
4331
|
const selected = selectedPlanRow(state)?.task;
|
|
4207
4332
|
const markHint = selected?.status === "completed" ? "m undone" : "m done";
|
|
4208
4333
|
const resolveHint = selected?.isolation?.state === "conflict" ? ["x resolve conflict"] : [];
|
|
4209
|
-
const planning = plannerInFlight(state);
|
|
4334
|
+
const planning = plannerInFlight(state) && !state.taskView;
|
|
4210
4335
|
const escHint = !planning ? null : state.queuedPrompts.length > 0 ? "esc unsend" : "esc \xD72 stop planning";
|
|
4211
4336
|
if (state.focus === "plan") {
|
|
4212
4337
|
if (state.taskEditor) {
|
|
@@ -4234,7 +4359,7 @@ function footerHints(state) {
|
|
|
4234
4359
|
"tab chat"
|
|
4235
4360
|
];
|
|
4236
4361
|
}
|
|
4237
|
-
const detailHint = (0,
|
|
4362
|
+
const detailHint = (0, import_core22.hasHiddenDetail)(state.taskView ? state.taskView.view.blocks : state.conversation.blocks) ? [state.detailAll ? "ctrl-o collapse all" : "ctrl-o expand all"] : [];
|
|
4238
4363
|
return [
|
|
4239
4364
|
"/help",
|
|
4240
4365
|
"tab plan",
|
|
@@ -4252,6 +4377,43 @@ function queuedBubble(text, cols) {
|
|
|
4252
4377
|
const room = Math.max(1, cols - width("\u25C7 \xB7 queued \xB7 esc to unsend"));
|
|
4253
4378
|
return wrap(text, room).map((line, i) => i === 0 ? truncate(`${style.grey(`\u25C7 ${line}`)} \xB7 queued \xB7 esc to unsend`, cols) : truncate(style.grey(` ${line}`), cols));
|
|
4254
4379
|
}
|
|
4380
|
+
function taskQueuedRows(state, cols) {
|
|
4381
|
+
const tv = state.taskView;
|
|
4382
|
+
if (!tv) return [];
|
|
4383
|
+
return tv.view.queued.flatMap((message, i) => queuedTaskBubble(message.text, i === tv.queuedIndex, cols));
|
|
4384
|
+
}
|
|
4385
|
+
function queuedTaskBubble(text, selected, cols) {
|
|
4386
|
+
const marker2 = selected ? style.accent("\u276F") : style.grey("\u25C7");
|
|
4387
|
+
const tag = selected ? " \xB7 queued \xB7 ctrl-r removes" : " \xB7 queued";
|
|
4388
|
+
const room = Math.max(1, cols - width(`\u276F \u25C7 ${tag}`));
|
|
4389
|
+
return wrap(text, room).map((line, i) => i === 0 ? truncate(`${marker2} ${selected ? style.bold(line) : line}${style.grey(tag)}`, cols) : truncate(` ${line}`, cols));
|
|
4390
|
+
}
|
|
4391
|
+
function taskActivity(task, working) {
|
|
4392
|
+
if (task?.awaitingApproval) return approvalLabel(task.awaitingApproval);
|
|
4393
|
+
if (working) return "working";
|
|
4394
|
+
if (task?.status === "awaiting_user") {
|
|
4395
|
+
return task.awaitingReason ? AWAITING_LABEL[task.awaitingReason] : "waiting for your input";
|
|
4396
|
+
}
|
|
4397
|
+
return task?.status ?? "";
|
|
4398
|
+
}
|
|
4399
|
+
function taskHeaderLines(state, tv, cols) {
|
|
4400
|
+
const task = findTask(state.tasks, tv.taskId);
|
|
4401
|
+
const parts = [
|
|
4402
|
+
`\u2192 Task ${task?.order ?? "?"}`,
|
|
4403
|
+
task?.title ?? "(task removed)",
|
|
4404
|
+
task?.assignedRunner ?? "",
|
|
4405
|
+
taskActivity(task, tv.view.working)
|
|
4406
|
+
].filter(Boolean);
|
|
4407
|
+
const approval = waitingApproval(tv);
|
|
4408
|
+
const hint = approval ? ["ctrl-y allow", ...approval.allowForTask ? ["ctrl-t allow for task"] : [], "ctrl-g deny (composer text is the note)"] : ["ctrl-r remove queued", "ctrl-x interrupt"];
|
|
4409
|
+
const index = tv.attempts.indexOf(tv.attempt);
|
|
4410
|
+
if (tv.attempts.length > 1) hint.push(`alt\u2190/\u2192 attempt ${index >= 0 ? index + 1 : 1}/${tv.attempts.length}`);
|
|
4411
|
+
hint.push("esc back");
|
|
4412
|
+
return [
|
|
4413
|
+
style.accent(truncate(parts.join(" \xB7 "), cols)),
|
|
4414
|
+
style.grey(truncate(hint.join(" \xB7 "), cols))
|
|
4415
|
+
];
|
|
4416
|
+
}
|
|
4255
4417
|
function stopHint(state) {
|
|
4256
4418
|
return plannerInFlight(state) && state.stopArmed ? "Press Esc again to stop" : "";
|
|
4257
4419
|
}
|
|
@@ -4284,24 +4446,27 @@ function chatBodyLines(blocks, cols, detailAll) {
|
|
|
4284
4446
|
return lines;
|
|
4285
4447
|
}
|
|
4286
4448
|
function chatLayout(state, rows, cols) {
|
|
4287
|
-
const
|
|
4288
|
-
const
|
|
4449
|
+
const tv = state.taskView;
|
|
4450
|
+
const blocks = tv ? tv.view.blocks : state.conversation.blocks;
|
|
4451
|
+
const welcome = !tv && state.tasks.length === 0;
|
|
4452
|
+
const body = chatBodyLines(blocks, cols, state.detailAll);
|
|
4289
4453
|
const transcript = !welcome ? body : body.length === 0 ? welcomeLines(state, cols) : [...welcomeLines(state, cols), "", ...body];
|
|
4290
|
-
const bubbles = queuedPromptRows(state, cols);
|
|
4454
|
+
const bubbles = tv ? taskQueuedRows(state, cols) : queuedPromptRows(state, cols);
|
|
4291
4455
|
const lines = [...transcript, ...bubbles.length > 0 ? ["", ...bubbles] : []];
|
|
4292
|
-
const
|
|
4456
|
+
const header = tv ? taskHeaderLines(state, tv, cols) : [];
|
|
4457
|
+
const usage = rows > 1 ? tokenLine(blocks, cols) : null;
|
|
4293
4458
|
const footer = usage ? [usage] : [];
|
|
4294
|
-
const room = rows - footer.length;
|
|
4295
|
-
return { lines, footer, anchor: lines.length > room ? "bottom" : "top", maxScroll: Math.max(0, lines.length - room) };
|
|
4459
|
+
const room = Math.max(0, rows - header.length - footer.length);
|
|
4460
|
+
return { lines, header, footer, anchor: lines.length > room ? "bottom" : "top", maxScroll: Math.max(0, lines.length - room) };
|
|
4296
4461
|
}
|
|
4297
4462
|
function chatScrollMax(state) {
|
|
4298
4463
|
return chatLayout(state, bodyRows(state), chatPaneWidth(state)).maxScroll;
|
|
4299
4464
|
}
|
|
4300
4465
|
function plannerSetup(state) {
|
|
4301
4466
|
const provider = state.plannerProvider;
|
|
4302
|
-
const registration =
|
|
4467
|
+
const registration = import_core22.ALL_PROVIDERS[provider];
|
|
4303
4468
|
if (!registration) return { label: "not set", ready: false };
|
|
4304
|
-
const runner = (0,
|
|
4469
|
+
const runner = (0, import_core22.runnerForProvider)(provider);
|
|
4305
4470
|
const ready = runner ? state.runners.some((r) => r.id === runner) : state.configuredProviders.includes(provider);
|
|
4306
4471
|
return { label: registration.label, ready };
|
|
4307
4472
|
}
|
|
@@ -4408,6 +4573,14 @@ var STATUS_ICON = {
|
|
|
4408
4573
|
approved: "\xB7",
|
|
4409
4574
|
pending: "\xB7"
|
|
4410
4575
|
};
|
|
4576
|
+
var AWAITING_LABEL = {
|
|
4577
|
+
input: "waiting for your input",
|
|
4578
|
+
checkpoint: "checkpoint",
|
|
4579
|
+
conflict: "merge conflict"
|
|
4580
|
+
};
|
|
4581
|
+
function approvalLabel(count) {
|
|
4582
|
+
return count > 1 ? `waiting for approval (${count})` : "waiting for approval";
|
|
4583
|
+
}
|
|
4411
4584
|
var IDLE_ICON = "~";
|
|
4412
4585
|
var FIRST_ROW_LINE = 2;
|
|
4413
4586
|
function planLayout(state, rows, cols) {
|
|
@@ -4484,7 +4657,7 @@ function taskLines(state, row, index, cols) {
|
|
|
4484
4657
|
const icon = paint(rawIcon);
|
|
4485
4658
|
const kind = running ? style.yellow("RUN") : task.type === "user" ? style.yellow("MAN") : style.grey(" AI");
|
|
4486
4659
|
const caret = selected ? style.cyan("\u276F") : " ";
|
|
4487
|
-
const orderLabel = parent ? (0,
|
|
4660
|
+
const orderLabel = parent ? (0, import_core22.taskOrderLabel)(task, parent) : String(task.order).padStart(2);
|
|
4488
4661
|
const indent = parent ? " " : "";
|
|
4489
4662
|
const head = `${indent}${caret} ${icon} ${orderLabel} ${kind} `;
|
|
4490
4663
|
const titleRoom = Math.max(1, cols - width(head));
|
|
@@ -4505,22 +4678,25 @@ function taskLines(state, row, index, cols) {
|
|
|
4505
4678
|
const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}${modeInfo?.autonomous ? style.yellow(" \u26A1") : ""}` : "";
|
|
4506
4679
|
const runner = task.assignedRunner ?? "";
|
|
4507
4680
|
const bodyPad = parent ? " " : " ";
|
|
4508
|
-
const
|
|
4509
|
-
const
|
|
4681
|
+
const structured = task.transport?.kind === "structured";
|
|
4682
|
+
const waiting = task.status === "awaiting_user" && task.awaitingReason ? AWAITING_LABEL[task.awaitingReason] : "";
|
|
4683
|
+
const activity = task.awaitingApproval ? `${approvalLabel(task.awaitingApproval)} \u2014 t opens it` : idle ? structured ? "quiet" : "quiet \u2014 t opens its terminal" : running ? "working" : waiting;
|
|
4684
|
+
const meta = [activity, runner, structured ? "structured" : "", model].filter(Boolean).join(" \xB7 ");
|
|
4510
4685
|
if (meta) lines.push(style.grey(truncate(`${bodyPad}${meta}`, cols)));
|
|
4686
|
+
if (task.transport?.fallback) lines.push(style.yellow(truncate(`${bodyPad}terminal: ${task.transport.fallback}`, cols)));
|
|
4511
4687
|
if (effort || mode) lines.push(style.grey(truncate(`${bodyPad}${[effort, mode].filter(Boolean).join(" \xB7 ")}`, cols)));
|
|
4512
4688
|
if (task.isolation?.state === "conflict") {
|
|
4513
4689
|
const where = task.isolation.conflictRepo && task.isolation.conflictRepo !== "." ? ` in ${task.isolation.conflictRepo}` : "";
|
|
4514
|
-
const files = task.isolation.conflictFiles?.length ? ` (${(0,
|
|
4690
|
+
const files = task.isolation.conflictFiles?.length ? ` (${(0, import_core22.capConflictFiles)(task.isolation.conflictFiles)})` : "";
|
|
4515
4691
|
lines.push(style.red(truncate(`${bodyPad}\u26A0 merge conflict${where}${files} \u2014 its work is kept on its own branch`, cols)));
|
|
4516
4692
|
}
|
|
4517
4693
|
if (task.isolation?.state === "repairing") {
|
|
4518
|
-
const files = task.isolation.conflictFiles?.length ? ` in ${(0,
|
|
4694
|
+
const files = task.isolation.conflictFiles?.length ? ` in ${(0, import_core22.capConflictFiles)(task.isolation.conflictFiles)}` : "";
|
|
4519
4695
|
const attempt = task.isolation.repair ? ` (attempt ${task.isolation.repair.attempt}/${task.isolation.repair.limit})` : "";
|
|
4520
4696
|
lines.push(style.yellow(truncate(`${bodyPad}\u21BB repairing conflict${files}${attempt}`, cols)));
|
|
4521
4697
|
}
|
|
4522
4698
|
if (task.isolation?.state === "integrated" && task.isolation.repairedFiles?.length) {
|
|
4523
|
-
lines.push(style.grey(truncate(`${bodyPad}\u21BB landed after repairing conflict in ${(0,
|
|
4699
|
+
lines.push(style.grey(truncate(`${bodyPad}\u21BB landed after repairing conflict in ${(0, import_core22.capConflictFiles)(task.isolation.repairedFiles)}`, cols)));
|
|
4524
4700
|
}
|
|
4525
4701
|
let editorLine;
|
|
4526
4702
|
if (expanded) {
|
|
@@ -4596,6 +4772,9 @@ function helpLayout(rows, cols) {
|
|
|
4596
4772
|
body.push(
|
|
4597
4773
|
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")
|
|
4598
4774
|
);
|
|
4775
|
+
body.push(
|
|
4776
|
+
style.grey("in a task view (t or /terminal on a structured task): ctrl-r removes the selected queued message \xB7 ctrl-x interrupts \xB7 ctrl-y allows a tool request \xB7 ctrl-t allows it for the task \xB7 ctrl-g denies it, with the composer text as the note \xB7 alt\u2190/\u2192 changes attempt \xB7 esc returns")
|
|
4777
|
+
);
|
|
4599
4778
|
const lines = body.map((line) => truncate(line, Math.max(1, cols - 2)));
|
|
4600
4779
|
const room = Math.max(1, rows - 2);
|
|
4601
4780
|
return { lines, room, maxScroll: Math.max(0, lines.length - room) };
|
|
@@ -4605,7 +4784,7 @@ function helpScrollMax(state) {
|
|
|
4605
4784
|
}
|
|
4606
4785
|
|
|
4607
4786
|
// src/tui/render.ts
|
|
4608
|
-
var
|
|
4787
|
+
var import_core23 = require("@ordewell/core");
|
|
4609
4788
|
function render(state) {
|
|
4610
4789
|
const { rows, cols } = state;
|
|
4611
4790
|
const rowsForBody = bodyRows(state);
|
|
@@ -4614,7 +4793,7 @@ function render(state) {
|
|
|
4614
4793
|
renderSkills(state, cols),
|
|
4615
4794
|
...body,
|
|
4616
4795
|
renderStatus(state, cols),
|
|
4617
|
-
...renderInput(state
|
|
4796
|
+
...renderInput(state),
|
|
4618
4797
|
...renderFooter(state, cols)
|
|
4619
4798
|
];
|
|
4620
4799
|
const frame2 = lines.slice(Math.max(0, lines.length - rows)).map((l) => pad(l, cols));
|
|
@@ -4701,7 +4880,8 @@ function renderSkills(state, cols) {
|
|
|
4701
4880
|
(id) => state.skills[id] ? style.green(`\u25CF ${id}`) : style.grey(`\u25CB ${id}`)
|
|
4702
4881
|
);
|
|
4703
4882
|
const auto = state.autonomous ? style.yellow("\u25CF auto") : style.grey("\u25CB auto");
|
|
4704
|
-
|
|
4883
|
+
const structured = state.runnerTransport === "structured" ? [style.yellow("\u25CF structured")] : [];
|
|
4884
|
+
return truncate([...badges, auto, ...structured].join(" "), cols);
|
|
4705
4885
|
}
|
|
4706
4886
|
function scrolledBackMark(state) {
|
|
4707
4887
|
return state.focus !== "plan" && state.scroll > 0 ? "\u2191 scrolled back \xB7 pgdn for live" : "";
|
|
@@ -4746,10 +4926,10 @@ function paintSpans(text, base, spans) {
|
|
|
4746
4926
|
}
|
|
4747
4927
|
return out + text.slice(cursor);
|
|
4748
4928
|
}
|
|
4749
|
-
function renderInput(state
|
|
4750
|
-
const prompt = state.focus === "plan" ? style.grey("\u276F ") : style.cyan("\u276F ");
|
|
4929
|
+
function renderInput(state) {
|
|
4930
|
+
const prompt = state.taskView ? style.accent(`${chatPromptLabel(state)} `) : state.focus === "plan" ? style.grey("\u276F ") : style.cyan("\u276F ");
|
|
4751
4931
|
const lines = chatInputWrap(state);
|
|
4752
|
-
const room =
|
|
4932
|
+
const room = chatEditorRoomFor(state, lines !== null);
|
|
4753
4933
|
const { text, cursor } = state.editor;
|
|
4754
4934
|
const continuation = " ".repeat(width(prompt));
|
|
4755
4935
|
const spans = highlightSpans(text);
|
|
@@ -4792,16 +4972,18 @@ function renderBody(state, rows, cols) {
|
|
|
4792
4972
|
const chatCols = chatPaneWidth(state);
|
|
4793
4973
|
const chat = renderChat(state, rows, chatCols);
|
|
4794
4974
|
const plan = renderPlan(state, rows, planCols);
|
|
4795
|
-
const
|
|
4975
|
+
const border = state.taskView ? style.accent("\u2502") : style.grey("\u2502");
|
|
4976
|
+
const divider = `${ERASE_TO_END}${atColumn(chatCols + 1)}${border}`;
|
|
4796
4977
|
return Array.from(
|
|
4797
4978
|
{ length: rows },
|
|
4798
4979
|
(_, i) => `${pad(chat[i] ?? "", chatCols)}${divider}${pad(plan[i] ?? "", planCols)}`
|
|
4799
4980
|
);
|
|
4800
4981
|
}
|
|
4801
4982
|
function renderChat(state, rows, cols) {
|
|
4802
|
-
const { lines, footer, anchor, maxScroll } = chatLayout(state, rows, cols);
|
|
4983
|
+
const { lines, header, footer, anchor, maxScroll } = chatLayout(state, rows, cols);
|
|
4803
4984
|
const back = Math.min(state.scroll, maxScroll);
|
|
4804
|
-
|
|
4985
|
+
const body = Math.max(0, rows - header.length - footer.length);
|
|
4986
|
+
return [...header, ...fit(lines.slice(0, lines.length - back), body, anchor), ...footer];
|
|
4805
4987
|
}
|
|
4806
4988
|
function renderPlan(state, rows, cols) {
|
|
4807
4989
|
const layout = planLayout(state, rows, cols);
|
|
@@ -4867,7 +5049,7 @@ function renderHandoff(state, overlay, rows, cols) {
|
|
|
4867
5049
|
return frame(`Diff \u2014 ${handoffBranch(handoff)}`, [...shown, "", style.grey(`${more}enter or esc goes back`)], rows, cols);
|
|
4868
5050
|
}
|
|
4869
5051
|
const landed = handoff.landed.length === 0 ? [style.grey("Nothing landed on it.")] : handoff.landed.map((task) => {
|
|
4870
|
-
const repaired = task.repairedFiles?.length ? style.yellow(` \u2014 repaired (${(0,
|
|
5052
|
+
const repaired = task.repairedFiles?.length ? style.yellow(` \u2014 repaired (${(0, import_core23.capConflictFiles)(task.repairedFiles)})`) : "";
|
|
4871
5053
|
return ` ${style.green("\u2713")} #${task.order} ${task.title}${repaired}`;
|
|
4872
5054
|
});
|
|
4873
5055
|
const actions = handoffActions(handoff).map((action, index) => {
|
|
@@ -4957,7 +5139,7 @@ function frame(title, body, rows, cols) {
|
|
|
4957
5139
|
}
|
|
4958
5140
|
|
|
4959
5141
|
// src/tui/reducer.ts
|
|
4960
|
-
var
|
|
5142
|
+
var import_core29 = require("@ordewell/core");
|
|
4961
5143
|
|
|
4962
5144
|
// src/tui/reducers/shared.ts
|
|
4963
5145
|
function step(state, effects = []) {
|
|
@@ -5006,10 +5188,10 @@ function handleApprovalKey(state, overlay, key) {
|
|
|
5006
5188
|
}
|
|
5007
5189
|
|
|
5008
5190
|
// src/tui/reducers/commands.ts
|
|
5009
|
-
var
|
|
5191
|
+
var import_core27 = require("@ordewell/core");
|
|
5010
5192
|
|
|
5011
5193
|
// src/tui/reducers/pickers.ts
|
|
5012
|
-
var
|
|
5194
|
+
var import_core24 = require("@ordewell/core");
|
|
5013
5195
|
var import_plan_utils2 = require("@ordewell/core/plan-utils");
|
|
5014
5196
|
var DEFAULT_EFFORT = "__runner_default__";
|
|
5015
5197
|
function picker(title, items, action, extra = {}) {
|
|
@@ -5028,11 +5210,11 @@ function modelDetail(m) {
|
|
|
5028
5210
|
function providerErrorHint(state) {
|
|
5029
5211
|
const failed = Object.keys(state.providerErrors ?? {});
|
|
5030
5212
|
if (failed.length === 0) return void 0;
|
|
5031
|
-
const names = failed.map((p) =>
|
|
5213
|
+
const names = failed.map((p) => import_core24.ALL_PROVIDERS[p]?.label ?? p);
|
|
5032
5214
|
return `\u26A0 Unavailable (key or fetch failed): ${names.join(", ")} \u2014 showing working providers only.`;
|
|
5033
5215
|
}
|
|
5034
5216
|
function plannerModelItems(state) {
|
|
5035
|
-
const runner = (0,
|
|
5217
|
+
const runner = (0, import_core24.runnerForProvider)(state.plannerProvider);
|
|
5036
5218
|
if (!runner) return state.orchestratorModels.map((m) => ({ id: m.id, label: m.label, detail: modelDetail(m) }));
|
|
5037
5219
|
const items = state.models.filter((m) => m.runners?.includes(runner)).map((m) => ({
|
|
5038
5220
|
id: m.id,
|
|
@@ -5046,7 +5228,7 @@ function plannerModelItems(state) {
|
|
|
5046
5228
|
return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
|
|
5047
5229
|
}
|
|
5048
5230
|
function plannerEffortItems(state) {
|
|
5049
|
-
const runner = (0,
|
|
5231
|
+
const runner = (0, import_core24.runnerForProvider)(state.plannerProvider);
|
|
5050
5232
|
if (!runner) {
|
|
5051
5233
|
return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
|
|
5052
5234
|
}
|
|
@@ -5155,21 +5337,21 @@ function pickerItemsFor(state, action) {
|
|
|
5155
5337
|
}
|
|
5156
5338
|
function plannerItems(state) {
|
|
5157
5339
|
const installed = new Set(state.runners.map((r) => r.id));
|
|
5158
|
-
const agents =
|
|
5159
|
-
const runner = (0,
|
|
5340
|
+
const agents = import_core24.CLI_PROVIDERS.map((id) => {
|
|
5341
|
+
const runner = (0, import_core24.runnerForProvider)(id);
|
|
5160
5342
|
const ready = installed.has(runner);
|
|
5161
5343
|
return {
|
|
5162
5344
|
id,
|
|
5163
|
-
label:
|
|
5345
|
+
label: import_core24.ALL_PROVIDERS[id].label,
|
|
5164
5346
|
detail: ready ? "coding agent \xB7 no API key needed" : "CLI not installed or not on PATH",
|
|
5165
5347
|
selected: state.plannerProvider === id,
|
|
5166
5348
|
disabled: !ready
|
|
5167
5349
|
};
|
|
5168
5350
|
});
|
|
5169
|
-
const vendors =
|
|
5351
|
+
const vendors = import_core24.PROVIDER_PRIORITY.filter((id) => state.configuredProviders.includes(id)).map((id) => ({
|
|
5170
5352
|
id,
|
|
5171
|
-
label:
|
|
5172
|
-
detail:
|
|
5353
|
+
label: import_core24.ALL_PROVIDERS[id].label,
|
|
5354
|
+
detail: import_core24.ALL_PROVIDERS[id].apiKeyEnvVar,
|
|
5173
5355
|
selected: state.plannerProvider === id
|
|
5174
5356
|
}));
|
|
5175
5357
|
return vendors.length > 0 ? [...agents, ...vendors] : [...agents, { id: "", label: "No API providers configured", detail: "add a key with /key", disabled: true }];
|
|
@@ -5250,6 +5432,139 @@ function handleMouseSelect(state, key) {
|
|
|
5250
5432
|
return step({ ...state, selection: null }, [{ type: "copyText", text: selectedText({ ...state, selection: moved }) }]);
|
|
5251
5433
|
}
|
|
5252
5434
|
|
|
5435
|
+
// src/tui/reducers/taskView.ts
|
|
5436
|
+
var import_core25 = require("@ordewell/core");
|
|
5437
|
+
function openTaskView(state, sessionId, taskId) {
|
|
5438
|
+
const task = findTask(state.tasks, taskId);
|
|
5439
|
+
if (!task) return step(say(state, "system", `No task ${taskId} in this plan.`));
|
|
5440
|
+
const taskView = {
|
|
5441
|
+
taskId,
|
|
5442
|
+
view: import_core25.EMPTY_TASK_LOG,
|
|
5443
|
+
attempts: [],
|
|
5444
|
+
attempt: 0,
|
|
5445
|
+
pending: [],
|
|
5446
|
+
loaded: false,
|
|
5447
|
+
followLatest: true,
|
|
5448
|
+
queuedIndex: 0
|
|
5449
|
+
};
|
|
5450
|
+
return step({ ...state, taskView, focus: "chat", scroll: 0 }, [{ type: "openTaskLog", sessionId, taskId }]);
|
|
5451
|
+
}
|
|
5452
|
+
function openTaskTerminalOrView(state, sessionId, taskId) {
|
|
5453
|
+
const task = findTask(state.tasks, taskId);
|
|
5454
|
+
if (task?.transport?.kind === "structured") return openTaskView(state, sessionId, taskId);
|
|
5455
|
+
return step(state, [{ type: "openTaskTerminal", sessionId, taskId }]);
|
|
5456
|
+
}
|
|
5457
|
+
function continueTaskStep(state, sessionId, taskId, text) {
|
|
5458
|
+
const opened = state.taskView?.taskId === taskId ? step({ ...state, taskView: { ...state.taskView, followLatest: true } }) : openTaskView(state, sessionId, taskId);
|
|
5459
|
+
const watch = state.status !== "executing";
|
|
5460
|
+
return step(opened.state, [...opened.effects, { type: "continueTask", sessionId, taskId, text, ...watch ? { watch: true } : {} }]);
|
|
5461
|
+
}
|
|
5462
|
+
function clampQueueIndex(tv) {
|
|
5463
|
+
const max = Math.max(0, tv.view.queued.length - 1);
|
|
5464
|
+
return tv.queuedIndex <= max ? tv : { ...tv, queuedIndex: max };
|
|
5465
|
+
}
|
|
5466
|
+
function applyLive(tv, attempt, events) {
|
|
5467
|
+
if (attempt <= 0) return tv;
|
|
5468
|
+
let next = tv;
|
|
5469
|
+
if (attempt > next.attempt) {
|
|
5470
|
+
if (!next.followLatest) {
|
|
5471
|
+
return next.attempts.includes(attempt) ? next : { ...next, attempts: [...next.attempts, attempt] };
|
|
5472
|
+
}
|
|
5473
|
+
next = {
|
|
5474
|
+
...next,
|
|
5475
|
+
view: import_core25.EMPTY_TASK_LOG,
|
|
5476
|
+
attempt,
|
|
5477
|
+
attempts: next.attempts.includes(attempt) ? next.attempts : [...next.attempts, attempt]
|
|
5478
|
+
};
|
|
5479
|
+
}
|
|
5480
|
+
if (attempt < next.attempt) return next;
|
|
5481
|
+
return clampQueueIndex({ ...next, view: events.reduce(import_core25.reduceTaskLog, next.view) });
|
|
5482
|
+
}
|
|
5483
|
+
function taskLogAbandoned(state) {
|
|
5484
|
+
const tv = state.taskView;
|
|
5485
|
+
if (!tv || tv.loaded) return state;
|
|
5486
|
+
let next = { ...tv, pending: [], loaded: true };
|
|
5487
|
+
for (const batch of tv.pending) next = applyLive(next, batch.attempt, batch.events);
|
|
5488
|
+
return { ...state, taskView: clampQueueIndex(next) };
|
|
5489
|
+
}
|
|
5490
|
+
function taskLogArrived(state, action) {
|
|
5491
|
+
const tv = state.taskView;
|
|
5492
|
+
if (!tv || tv.taskId !== action.taskId) return state;
|
|
5493
|
+
if (!tv.loaded) {
|
|
5494
|
+
return { ...state, taskView: { ...tv, pending: [...tv.pending, { attempt: action.attempt, events: action.events }] } };
|
|
5495
|
+
}
|
|
5496
|
+
return { ...state, taskView: applyLive(tv, action.attempt, action.events) };
|
|
5497
|
+
}
|
|
5498
|
+
function announceApprovals(state, action) {
|
|
5499
|
+
if (state.taskView?.taskId === action.taskId) return state;
|
|
5500
|
+
const task = findTask(state.tasks, action.taskId);
|
|
5501
|
+
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" ? say(s, "system", `\xB7 Task ${task?.order ?? "?"} waits for approval: ${(0, import_core25.runnerToolSubject)(event.tool, event.args)} \u2014 t on it to answer`) : s, state);
|
|
5502
|
+
return state.taskView && spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
|
|
5503
|
+
}
|
|
5504
|
+
function taskLogLoaded(state, action) {
|
|
5505
|
+
const tv = state.taskView;
|
|
5506
|
+
if (!tv || tv.taskId !== action.taskId) return state;
|
|
5507
|
+
const loaded = action.attempt >= 1 ? {
|
|
5508
|
+
...tv,
|
|
5509
|
+
view: (0, import_core25.replayTaskLog)(action.events),
|
|
5510
|
+
attempts: action.attempts ?? tv.attempts,
|
|
5511
|
+
attempt: action.attempt
|
|
5512
|
+
} : { ...tv, attempts: action.attempts ?? tv.attempts };
|
|
5513
|
+
let next = { ...loaded, pending: [], loaded: true };
|
|
5514
|
+
for (const batch of tv.pending) {
|
|
5515
|
+
if (batch.attempt === action.attempt) continue;
|
|
5516
|
+
next = applyLive(next, batch.attempt, batch.events);
|
|
5517
|
+
}
|
|
5518
|
+
return { ...state, taskView: clampQueueIndex(next) };
|
|
5519
|
+
}
|
|
5520
|
+
function answerApproval(state, tv, sessionId, decision) {
|
|
5521
|
+
const approval = waitingApproval(tv);
|
|
5522
|
+
if (!approval?.approvalId) return step(say(state, "system", "Nothing is waiting for approval."));
|
|
5523
|
+
if (decision === "allowForTask" && !approval.allowForTask) {
|
|
5524
|
+
return step(say(state, "system", "The runner offered no grant for the rest of this task \u2014 ctrl-y allows this call."));
|
|
5525
|
+
}
|
|
5526
|
+
const note = decision === "deny" ? state.editor.text.trim() : "";
|
|
5527
|
+
const answer = decision === "deny" ? { decision, ...note ? { note } : {} } : { decision };
|
|
5528
|
+
const next = note ? { ...state, editor: { ...state.editor, text: "", cursor: 0 } } : state;
|
|
5529
|
+
return step(next, [{ type: "answerTaskApproval", sessionId, approvalId: approval.approvalId, answer }]);
|
|
5530
|
+
}
|
|
5531
|
+
function handleTaskViewKey(state, key) {
|
|
5532
|
+
const tv = state.taskView;
|
|
5533
|
+
if (!tv || !state.sessionId) return null;
|
|
5534
|
+
if (key.name === "alt-left" || key.name === "alt-right") {
|
|
5535
|
+
const index = tv.attempts.indexOf(tv.attempt);
|
|
5536
|
+
if (index < 0) return null;
|
|
5537
|
+
const target = key.name === "alt-left" ? index - 1 : index + 1;
|
|
5538
|
+
const attempt = tv.attempts[target];
|
|
5539
|
+
if (attempt === void 0) {
|
|
5540
|
+
return step(say(state, "system", target < 0 ? "This is the first attempt." : "This is the latest attempt."));
|
|
5541
|
+
}
|
|
5542
|
+
return step(
|
|
5543
|
+
{ ...state, taskView: { ...tv, followLatest: false } },
|
|
5544
|
+
[{ type: "loadTaskAttempt", sessionId: state.sessionId, taskId: tv.taskId, attempt }]
|
|
5545
|
+
);
|
|
5546
|
+
}
|
|
5547
|
+
if (key.name === "ctrl-r") {
|
|
5548
|
+
const queued = tv.view.queued[tv.queuedIndex];
|
|
5549
|
+
if (!queued) return step(say(state, "system", "No queued message to remove."));
|
|
5550
|
+
return step(state, [{ type: "removeTaskMessage", sessionId: state.sessionId, taskId: tv.taskId, messageId: queued.id }]);
|
|
5551
|
+
}
|
|
5552
|
+
if (key.name === "ctrl-x") {
|
|
5553
|
+
return step(state, [{ type: "interruptTask", sessionId: state.sessionId, taskId: tv.taskId }]);
|
|
5554
|
+
}
|
|
5555
|
+
if (key.name === "ctrl-y") return answerApproval(state, tv, state.sessionId, "allow");
|
|
5556
|
+
if (key.name === "ctrl-t") return answerApproval(state, tv, state.sessionId, "allowForTask");
|
|
5557
|
+
if (key.name === "ctrl-g") return answerApproval(state, tv, state.sessionId, "deny");
|
|
5558
|
+
if (key.name === "ctrl-n" || key.name === "ctrl-p") {
|
|
5559
|
+
const count = tv.view.queued.length;
|
|
5560
|
+
if (count === 0) return null;
|
|
5561
|
+
const delta = key.name === "ctrl-n" ? 1 : -1;
|
|
5562
|
+
const queuedIndex = Math.max(0, Math.min(count - 1, tv.queuedIndex + delta));
|
|
5563
|
+
return step({ ...state, taskView: { ...tv, queuedIndex } });
|
|
5564
|
+
}
|
|
5565
|
+
return null;
|
|
5566
|
+
}
|
|
5567
|
+
|
|
5253
5568
|
// src/tui/reducers/taskEdits.ts
|
|
5254
5569
|
var import_plan_utils3 = require("@ordewell/core/plan-utils");
|
|
5255
5570
|
function openTaskRunnerPicker(state, task) {
|
|
@@ -5520,9 +5835,7 @@ function handlePlanKey(state, key) {
|
|
|
5520
5835
|
return step(state, [taskActionEffect(state, state.sessionId, task.id, action)]);
|
|
5521
5836
|
}
|
|
5522
5837
|
if (key.char === "d") return confirmRemoveTask(state, task);
|
|
5523
|
-
if (key.char === "t")
|
|
5524
|
-
return step(state, [{ type: "openTaskTerminal", sessionId: state.sessionId, taskId: task.id }]);
|
|
5525
|
-
}
|
|
5838
|
+
if (key.char === "t") return openTaskTerminalOrView(state, state.sessionId, task.id);
|
|
5526
5839
|
if (key.char === "x" && task.isolation?.state === "conflict") {
|
|
5527
5840
|
return step(state, [{ type: "resolveConflict", sessionId: state.sessionId, taskId: task.id }]);
|
|
5528
5841
|
}
|
|
@@ -5555,9 +5868,9 @@ function commitTaskEdit(state, task, editor) {
|
|
|
5555
5868
|
}
|
|
5556
5869
|
|
|
5557
5870
|
// src/tui/reducers/turnQueue.ts
|
|
5558
|
-
var
|
|
5871
|
+
var import_core26 = require("@ordewell/core");
|
|
5559
5872
|
function drainQueue(state, settled) {
|
|
5560
|
-
const next = (0,
|
|
5873
|
+
const next = (0, import_core26.drainNext)(state.queuedPrompts);
|
|
5561
5874
|
const ended = disarmStop({ ...settled, stopRequested: false });
|
|
5562
5875
|
if (!next) return step(ended);
|
|
5563
5876
|
const spoken = say(ended, "user", next.text);
|
|
@@ -5576,14 +5889,14 @@ function stopPlanning(state, sessionId) {
|
|
|
5576
5889
|
return step(queueToEditor(cutTurn({ ...dismissed, stopArmed: false, stopRequested: true })), [{ type: "cancelPlanning", sessionId }]);
|
|
5577
5890
|
}
|
|
5578
5891
|
function queueToEditor(state) {
|
|
5579
|
-
const all = (0,
|
|
5892
|
+
const all = (0, import_core26.unsendAll)(state.queuedPrompts);
|
|
5580
5893
|
if (!all) return state;
|
|
5581
5894
|
return { ...state, queuedPrompts: all.rest, editor: { ...state.editor, text: all.text, cursor: all.text.length } };
|
|
5582
5895
|
}
|
|
5583
5896
|
function unsendLatest(state) {
|
|
5584
|
-
const latest = (0,
|
|
5897
|
+
const latest = (0, import_core26.unsendLatest)(state.queuedPrompts);
|
|
5585
5898
|
if (!latest) return step(state);
|
|
5586
|
-
const text = (0,
|
|
5899
|
+
const text = (0, import_core26.aheadOfDraft)(latest.text, state.editor.text);
|
|
5587
5900
|
return step({
|
|
5588
5901
|
...state,
|
|
5589
5902
|
queuedPrompts: latest.rest,
|
|
@@ -5600,7 +5913,7 @@ function plannerEscape(state) {
|
|
|
5600
5913
|
}
|
|
5601
5914
|
|
|
5602
5915
|
// src/tui/reducers/commands.ts
|
|
5603
|
-
var KNOWN_PROVIDERS2 = Object.keys(
|
|
5916
|
+
var KNOWN_PROVIDERS2 = Object.keys(import_core27.ALL_PROVIDERS);
|
|
5604
5917
|
function runCommand(state, { name, args }) {
|
|
5605
5918
|
if (!findCommand(name)) {
|
|
5606
5919
|
return fail3(state, `Unknown command: /${name} \u2014 type /help to see what's available.`);
|
|
@@ -5641,6 +5954,8 @@ function runCommand(state, { name, args }) {
|
|
|
5641
5954
|
return runners(state, args);
|
|
5642
5955
|
case "auto":
|
|
5643
5956
|
return setAutonomous(state, args[0]);
|
|
5957
|
+
case "transport":
|
|
5958
|
+
return setTransport(state, args[0]);
|
|
5644
5959
|
case "parallel":
|
|
5645
5960
|
return setMaxParallel(state, args[0]);
|
|
5646
5961
|
case "mouse":
|
|
@@ -5678,11 +5993,13 @@ function runCommand(state, { name, args }) {
|
|
|
5678
5993
|
(sessionId, taskId) => step(state, [{ type: "removeTask", sessionId, taskId }])
|
|
5679
5994
|
);
|
|
5680
5995
|
case "terminal":
|
|
5681
|
-
return taskCommand(
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5996
|
+
return taskCommand(state, args[0], (sessionId, taskId) => openTaskTerminalOrView(state, sessionId, taskId));
|
|
5997
|
+
case "continue":
|
|
5998
|
+
return taskCommand(state, args[0], (sessionId, taskId) => {
|
|
5999
|
+
const text = args.slice(1).join(" ").trim();
|
|
6000
|
+
if (!text) return fail3(state, "Usage: /continue <id> <message>");
|
|
6001
|
+
return continueTaskStep(state, sessionId, taskId, text);
|
|
6002
|
+
});
|
|
5686
6003
|
case "task-runner":
|
|
5687
6004
|
return taskRunnerCommand(state, args);
|
|
5688
6005
|
case "task-model":
|
|
@@ -5749,10 +6066,11 @@ function newSession(state) {
|
|
|
5749
6066
|
handoff: null,
|
|
5750
6067
|
sessionId: null,
|
|
5751
6068
|
goal: "",
|
|
6069
|
+
taskView: null,
|
|
5752
6070
|
tasks: [],
|
|
5753
6071
|
planApproved: false,
|
|
5754
|
-
conversation:
|
|
5755
|
-
turnGate:
|
|
6072
|
+
conversation: import_core27.EMPTY_CONVERSATION,
|
|
6073
|
+
turnGate: import_core27.NO_TURN,
|
|
5756
6074
|
selectedTask: 0,
|
|
5757
6075
|
expandedTaskId: null,
|
|
5758
6076
|
taskEditor: null,
|
|
@@ -5763,14 +6081,14 @@ function newSession(state) {
|
|
|
5763
6081
|
// Prompts belong to the session that raised them; the old planner is gone
|
|
5764
6082
|
// and its pending requests deny on their own timeout.
|
|
5765
6083
|
pendingApprovals: [],
|
|
5766
|
-
queuedPrompts:
|
|
6084
|
+
queuedPrompts: import_core27.EMPTY_HOLD,
|
|
5767
6085
|
stopArmed: false,
|
|
5768
6086
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
5769
6087
|
}, closeEffects);
|
|
5770
6088
|
}
|
|
5771
6089
|
function setModel(state, args) {
|
|
5772
6090
|
if (args[0] === "set" && args[1]) {
|
|
5773
|
-
const runner = (0,
|
|
6091
|
+
const runner = (0, import_core27.runnerForProvider)(state.plannerProvider);
|
|
5774
6092
|
const known = runner ? modelsForRunner(state.models, runner) : [];
|
|
5775
6093
|
if (runner && known.length > 0 && !known.some((m) => m.id === args[1])) {
|
|
5776
6094
|
return fail3(state, `${args[1]} was not discovered for ${runner}.`);
|
|
@@ -5843,16 +6161,16 @@ function setKey(state, args) {
|
|
|
5843
6161
|
}
|
|
5844
6162
|
return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
|
|
5845
6163
|
}
|
|
5846
|
-
const items =
|
|
6164
|
+
const items = import_core27.PROVIDER_PRIORITY.map((id) => ({
|
|
5847
6165
|
id,
|
|
5848
|
-
label:
|
|
5849
|
-
detail:
|
|
6166
|
+
label: import_core27.ALL_PROVIDERS[id].label,
|
|
6167
|
+
detail: import_core27.ALL_PROVIDERS[id].apiKeyEnvVar,
|
|
5850
6168
|
selected: state.configuredProviders.includes(id)
|
|
5851
6169
|
}));
|
|
5852
6170
|
return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
|
|
5853
6171
|
}
|
|
5854
6172
|
function keyPrompt(provider) {
|
|
5855
|
-
const meta =
|
|
6173
|
+
const meta = import_core27.ALL_PROVIDERS[provider];
|
|
5856
6174
|
return {
|
|
5857
6175
|
kind: "prompt",
|
|
5858
6176
|
title: `${meta.label} API key`,
|
|
@@ -5929,7 +6247,7 @@ function runners(state, args) {
|
|
|
5929
6247
|
}
|
|
5930
6248
|
function setMaxParallel(state, arg) {
|
|
5931
6249
|
if (!arg) return step(say(state, "system", `Up to ${state.maxParallel} AI task${state.maxParallel === 1 ? "" : "s"} run at once \u2014 /parallel <n> changes it.`));
|
|
5932
|
-
const limit = (0,
|
|
6250
|
+
const limit = (0, import_core27.parseMaxParallel)(arg);
|
|
5933
6251
|
if (limit === null) return fail3(state, "Usage: /parallel [<number of tasks, 1 or more>]");
|
|
5934
6252
|
return step({ ...state, maxParallel: limit }, [{ type: "setMaxParallel", limit }]);
|
|
5935
6253
|
}
|
|
@@ -5938,6 +6256,11 @@ function setAutonomous(state, arg) {
|
|
|
5938
6256
|
if (enabled === null) return fail3(state, "Usage: /auto [on|off]");
|
|
5939
6257
|
return step({ ...state, autonomous: enabled }, [{ type: "setAutonomous", enabled }]);
|
|
5940
6258
|
}
|
|
6259
|
+
function setTransport(state, arg) {
|
|
6260
|
+
const value = arg?.toLowerCase() ?? (state.runnerTransport === "structured" ? "terminal" : "structured");
|
|
6261
|
+
if (!(0, import_core27.isRunnerTransport)(value)) return fail3(state, "Usage: /transport [terminal|structured]");
|
|
6262
|
+
return step(state, [{ type: "setTransport", transport: value }]);
|
|
6263
|
+
}
|
|
5941
6264
|
function setMouseCapture(state, arg) {
|
|
5942
6265
|
const enabled = resolveToggle(arg, state.mouseCapture);
|
|
5943
6266
|
if (enabled === null) return fail3(state, "Usage: /mouse [on|off]");
|
|
@@ -6185,6 +6508,7 @@ function choose(state, picker2, item) {
|
|
|
6185
6508
|
}
|
|
6186
6509
|
|
|
6187
6510
|
// src/tui/reducers/incoming.ts
|
|
6511
|
+
var import_core28 = require("@ordewell/core");
|
|
6188
6512
|
var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
|
|
6189
6513
|
var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
|
|
6190
6514
|
var asRecord = (value) => typeof value === "object" && value !== null ? value : {};
|
|
@@ -6215,6 +6539,7 @@ function toTaskView2(t, index) {
|
|
|
6215
6539
|
prompt: typeof t.prompt === "string" ? sanitize(t.prompt) : void 0,
|
|
6216
6540
|
type: t.type === "user" ? "user" : "ai",
|
|
6217
6541
|
status: planLabel(t.status, "pending"),
|
|
6542
|
+
awaitingReason: t.status === "awaiting_user" && (0, import_core28.isAwaitingReason)(t.awaitingReason) ? t.awaitingReason : void 0,
|
|
6218
6543
|
dependencies: Array.isArray(t.dependencies) ? t.dependencies.map((id) => planLabel(id)) : [],
|
|
6219
6544
|
assignedRunner: typeof t.assignedRunner === "string" ? planLabel(t.assignedRunner) : void 0,
|
|
6220
6545
|
taskMode: typeof t.taskMode === "string" ? planLabel(t.taskMode) : void 0,
|
|
@@ -6245,6 +6570,7 @@ function applySettings(state, settings) {
|
|
|
6245
6570
|
plannerProvider: typeof settings.aiProvider === "string" ? settings.aiProvider : state.plannerProvider,
|
|
6246
6571
|
plannerEffort: typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : state.plannerEffort,
|
|
6247
6572
|
maxParallel: typeof settings.maxParallel === "number" ? settings.maxParallel : state.maxParallel,
|
|
6573
|
+
runnerTransport: (0, import_core28.isRunnerTransport)(settings.runnerTransport) ? settings.runnerTransport : state.runnerTransport,
|
|
6248
6574
|
allowlist: settings.modelAllowlist && typeof settings.modelAllowlist === "object" ? settings.modelAllowlist : state.allowlist
|
|
6249
6575
|
};
|
|
6250
6576
|
}
|
|
@@ -6279,6 +6605,7 @@ function followSession(state, message) {
|
|
|
6279
6605
|
const heard = turnActivity(hear(state, message));
|
|
6280
6606
|
if (message.type === "approval_request") {
|
|
6281
6607
|
const { id, kind, subject, scope, detail } = message;
|
|
6608
|
+
if (kind === "runner_tool") return heard;
|
|
6282
6609
|
return enqueueApproval(heard, { id, kind, subject, scope, ...detail ? { detail } : {} });
|
|
6283
6610
|
}
|
|
6284
6611
|
return message.type === "approval_settled" ? dropApproval(heard, message.id) : heard;
|
|
@@ -6292,7 +6619,7 @@ function reduce(state, action) {
|
|
|
6292
6619
|
// Isolation belongs to one session's plan, and task ids repeat across
|
|
6293
6620
|
// sessions, so nothing of the last one's may carry over into this.
|
|
6294
6621
|
case "sessionStarted":
|
|
6295
|
-
return step(clearIsolation({ ...state, sessionId: action.sessionId, goal: action.goal }));
|
|
6622
|
+
return step(clearIsolation({ ...state, sessionId: action.sessionId, goal: action.goal, taskView: null }));
|
|
6296
6623
|
// The daemon registers a session only once planning succeeds; when it does
|
|
6297
6624
|
// not, holding on to the id would send every next message to a session the
|
|
6298
6625
|
// server never had.
|
|
@@ -6301,14 +6628,15 @@ function reduce(state, action) {
|
|
|
6301
6628
|
...clearIsolation(state),
|
|
6302
6629
|
sessionId: null,
|
|
6303
6630
|
goal: "",
|
|
6631
|
+
taskView: null,
|
|
6304
6632
|
pendingApprovals: [],
|
|
6305
|
-
queuedPrompts:
|
|
6633
|
+
queuedPrompts: import_core29.EMPTY_HOLD,
|
|
6306
6634
|
stopArmed: false,
|
|
6307
6635
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
6308
6636
|
});
|
|
6309
6637
|
case "chatRestored":
|
|
6310
6638
|
if (stale(state, action.sessionId)) return step(state);
|
|
6311
|
-
return step({ ...state, conversation: (0,
|
|
6639
|
+
return step({ ...state, conversation: (0, import_core29.fromTranscript)(action.history, action.researchLog, action.plannerUsage), scroll: 0 });
|
|
6312
6640
|
case "planUpdated": {
|
|
6313
6641
|
if (stale(state, action.sessionId)) return step(state);
|
|
6314
6642
|
const isolation = isolationOfPlan(action.plan);
|
|
@@ -6340,6 +6668,12 @@ function reduce(state, action) {
|
|
|
6340
6668
|
case "sessionMessage":
|
|
6341
6669
|
if (stale(state, action.sessionId)) return step(state);
|
|
6342
6670
|
return step(followSession(state, action.message));
|
|
6671
|
+
case "taskLog":
|
|
6672
|
+
if (stale(state, action.sessionId)) return step(state);
|
|
6673
|
+
return step(announceApprovals(taskLogArrived(state, action), action));
|
|
6674
|
+
case "taskLogLoaded":
|
|
6675
|
+
if (stale(state, action.sessionId)) return step(state);
|
|
6676
|
+
return step(taskLogLoaded(state, action));
|
|
6343
6677
|
// A settled transcript line, not a research step: nothing ever settles a
|
|
6344
6678
|
// task's start, so as a step it stayed "⋯" forever and was counted into
|
|
6345
6679
|
// the planner's next "(+N more)".
|
|
@@ -6348,12 +6682,12 @@ function reduce(state, action) {
|
|
|
6348
6682
|
const existing = findTask(state.tasks, action.taskId);
|
|
6349
6683
|
if (existing && isTaskRunning(existing)) return step(state);
|
|
6350
6684
|
const tasks = state.tasks.map((t) => t.id === action.taskId && !isTaskRunning(t) ? { ...t, status: "in_progress" } : t);
|
|
6351
|
-
const spoken = say(state, "system", (0,
|
|
6685
|
+
const spoken = say(state, "system", (0, import_core29.taskStartedNotice)(action.title, action.runner));
|
|
6352
6686
|
return step({ ...spoken, tasks, status: "executing", busyLabel: runLabel(tasks) });
|
|
6353
6687
|
}
|
|
6354
6688
|
case "taskStatus": {
|
|
6355
6689
|
if (stale(state, action.sessionId) || !state.tasks.some((t) => t.id === action.taskId)) return step(state);
|
|
6356
|
-
const tasks = state.tasks.map((t) => t.id === action.taskId ? { ...t, status: action.status } : t);
|
|
6690
|
+
const tasks = state.tasks.map((t) => t.id === action.taskId ? { ...t, status: action.status, awaitingReason: void 0 } : t);
|
|
6357
6691
|
const status = runStatus(state, tasks);
|
|
6358
6692
|
return step({ ...state, status, tasks, busyLabel: status === "executing" ? runLabel(tasks) : state.busyLabel });
|
|
6359
6693
|
}
|
|
@@ -6366,9 +6700,13 @@ function reduce(state, action) {
|
|
|
6366
6700
|
if (update === void 0) return t;
|
|
6367
6701
|
const idleSince = update.idleSince ?? null;
|
|
6368
6702
|
const isolation = update.isolation ?? t.isolation;
|
|
6369
|
-
|
|
6703
|
+
const transport = update.transport;
|
|
6704
|
+
const awaitingReason = update.awaitingReason;
|
|
6705
|
+
const continuable = update.continuable === true;
|
|
6706
|
+
const awaitingApproval = update.awaitingApproval;
|
|
6707
|
+
if (update.status !== t.status || idleSince !== (t.idleSince ?? null) || !sameIsolation(isolation, t.isolation) || !sameTransport(transport, t.transport) || awaitingReason !== t.awaitingReason || continuable !== (t.continuable ?? false) || awaitingApproval !== t.awaitingApproval) {
|
|
6370
6708
|
changed = true;
|
|
6371
|
-
return { ...t, status: update.status, idleSince, isolation };
|
|
6709
|
+
return { ...t, status: update.status, idleSince, isolation, transport, awaitingReason, continuable, awaitingApproval };
|
|
6372
6710
|
}
|
|
6373
6711
|
return t;
|
|
6374
6712
|
});
|
|
@@ -6443,11 +6781,12 @@ function reduce(state, action) {
|
|
|
6443
6781
|
...clearIsolation(state),
|
|
6444
6782
|
sessionId: action.sessionId,
|
|
6445
6783
|
goal: action.goal,
|
|
6784
|
+
taskView: null,
|
|
6446
6785
|
status: "idle",
|
|
6447
6786
|
busyLabel: "",
|
|
6448
6787
|
planApproved: false,
|
|
6449
6788
|
pendingApprovals: [],
|
|
6450
|
-
queuedPrompts:
|
|
6789
|
+
queuedPrompts: import_core29.EMPTY_HOLD,
|
|
6451
6790
|
stopArmed: false,
|
|
6452
6791
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
6453
6792
|
});
|
|
@@ -6467,6 +6806,7 @@ function reduce(state, action) {
|
|
|
6467
6806
|
orchestratorModel: action.orchestratorModel ?? state.orchestratorModel
|
|
6468
6807
|
}, ["set-planner", "set-task-runner"]));
|
|
6469
6808
|
case "failed": {
|
|
6809
|
+
state = taskLogAbandoned(state);
|
|
6470
6810
|
const quiet = plannerInFlight(state) && state.stopRequested;
|
|
6471
6811
|
const reported = { ...quiet ? state : say(state, "error", action.message), status: "idle", busyLabel: "" };
|
|
6472
6812
|
if (!plannerInFlight(state)) return step(reported);
|
|
@@ -6502,6 +6842,9 @@ function reduce(state, action) {
|
|
|
6502
6842
|
return step(disarmStop(state));
|
|
6503
6843
|
}
|
|
6504
6844
|
}
|
|
6845
|
+
function sameTransport(a, b) {
|
|
6846
|
+
return a?.kind === b?.kind && a?.fallback === b?.fallback;
|
|
6847
|
+
}
|
|
6505
6848
|
function runStatus(state, tasks) {
|
|
6506
6849
|
if (tasks.some(isTaskRunning)) return "executing";
|
|
6507
6850
|
if (tasks.some((t) => t.status === "awaiting_user")) return "executing";
|
|
@@ -6520,6 +6863,10 @@ function handleKey2(state, key) {
|
|
|
6520
6863
|
}
|
|
6521
6864
|
if (key.name === "ctrl-l") return step({ ...state, conversation: wiped(state.conversation), scroll: 0 });
|
|
6522
6865
|
if (key.name === "ctrl-o" && state.focus === "chat" && !state.overlay) return toggleDetail(state);
|
|
6866
|
+
if (key.name === "escape" && state.taskView && !state.overlay) {
|
|
6867
|
+
if (state.editor.text) return step({ ...state, editor: { ...state.editor, text: "", cursor: 0 } });
|
|
6868
|
+
return step({ ...state, taskView: null, scroll: 0 });
|
|
6869
|
+
}
|
|
6523
6870
|
if (key.name === "escape") {
|
|
6524
6871
|
const stopped = plannerEscape(state);
|
|
6525
6872
|
if (stopped) return stopped;
|
|
@@ -6543,12 +6890,16 @@ function handleKey2(state, key) {
|
|
|
6543
6890
|
return step({ ...state, focus: state.focus === "chat" ? "plan" : "chat" });
|
|
6544
6891
|
}
|
|
6545
6892
|
if (state.focus === "plan") return handlePlanKey(state, key);
|
|
6893
|
+
if (state.taskView) {
|
|
6894
|
+
const handled = handleTaskViewKey(state, key);
|
|
6895
|
+
if (handled) return handled;
|
|
6896
|
+
}
|
|
6546
6897
|
if (key.name === "enter") return submit(state);
|
|
6547
6898
|
if (key.name === "escape") return step({ ...state, editor: { ...state.editor, text: "", cursor: 0 } });
|
|
6548
6899
|
const scroll = scrollDelta(key, state);
|
|
6549
6900
|
if (scroll !== null) return scrollChat(state, scroll);
|
|
6550
6901
|
const multilineEditor = state.editor.text.includes("\n");
|
|
6551
|
-
const room =
|
|
6902
|
+
const room = chatEditorRoomFor(state, true);
|
|
6552
6903
|
return step({ ...state, editor: applyKey(state.editor, key, multilineEditor ? room : void 0) });
|
|
6553
6904
|
}
|
|
6554
6905
|
function submit(state) {
|
|
@@ -6557,8 +6908,14 @@ function submit(state) {
|
|
|
6557
6908
|
const cleared = { ...state, editor: commit(state.editor) };
|
|
6558
6909
|
const command = parseSlash(text);
|
|
6559
6910
|
if (command && findCommand(command.name)?.source !== "skill") return runCommand(cleared, command);
|
|
6911
|
+
if (state.taskView) {
|
|
6912
|
+
if (!state.sessionId) return step(state);
|
|
6913
|
+
const taskId = state.taskView.taskId;
|
|
6914
|
+
if (continuesTask(findTask(state.tasks, taskId))) return continueTaskStep(cleared, state.sessionId, taskId, text);
|
|
6915
|
+
return step(cleared, [{ type: "sendTaskMessage", sessionId: state.sessionId, taskId, text }]);
|
|
6916
|
+
}
|
|
6560
6917
|
if (plannerInFlight(state)) {
|
|
6561
|
-
return step({ ...cleared, queuedPrompts: (0,
|
|
6918
|
+
return step({ ...cleared, queuedPrompts: (0, import_core29.holdPrompt)(state.queuedPrompts, text) });
|
|
6562
6919
|
}
|
|
6563
6920
|
const spoken = say(cleared, "user", text);
|
|
6564
6921
|
const effect = state.sessionId ? { type: "sendMessage", sessionId: state.sessionId, message: text } : { type: "startConversation", goal: text };
|
|
@@ -6618,10 +6975,10 @@ function createApp(deps) {
|
|
|
6618
6975
|
|
|
6619
6976
|
// src/tui/effects.ts
|
|
6620
6977
|
var import_child_process4 = require("child_process");
|
|
6621
|
-
var
|
|
6978
|
+
var import_core31 = require("@ordewell/core");
|
|
6622
6979
|
|
|
6623
6980
|
// src/tui/inbound.ts
|
|
6624
|
-
var
|
|
6981
|
+
var import_core30 = require("@ordewell/core");
|
|
6625
6982
|
var CONVERSATION_TYPES = /* @__PURE__ */ new Set([
|
|
6626
6983
|
"planner_message",
|
|
6627
6984
|
"planner_turn_started",
|
|
@@ -6831,7 +7188,15 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
6831
7188
|
case "status_update": {
|
|
6832
7189
|
const updates = {};
|
|
6833
7190
|
for (const task of event.tasks ?? []) {
|
|
6834
|
-
updates[String(task.id)] = {
|
|
7191
|
+
updates[String(task.id)] = {
|
|
7192
|
+
status: String(task.status),
|
|
7193
|
+
idleSince: task.idleSince ?? null,
|
|
7194
|
+
isolation: task.isolation,
|
|
7195
|
+
transport: task.transport,
|
|
7196
|
+
awaitingReason: (0, import_core30.isAwaitingReason)(task.awaitingReason) ? task.awaitingReason : void 0,
|
|
7197
|
+
...task.continuable ? { continuable: true } : {},
|
|
7198
|
+
awaitingApproval: typeof task.awaitingApproval === "number" && task.awaitingApproval > 0 ? task.awaitingApproval : void 0
|
|
7199
|
+
};
|
|
6835
7200
|
}
|
|
6836
7201
|
dispatch({ type: "tasksStatus", updates, sessionId });
|
|
6837
7202
|
return;
|
|
@@ -6842,7 +7207,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
6842
7207
|
// The extension shows these in its checkpoint panel; here they are
|
|
6843
7208
|
// transcript lines, which is the TUI's equivalent surface.
|
|
6844
7209
|
case "checkpoint":
|
|
6845
|
-
dispatch({ type: "notice", message: `\xB7 Checkpoint \u2014 ${event.taskTitle}: ${(0,
|
|
7210
|
+
dispatch({ type: "notice", message: `\xB7 Checkpoint \u2014 ${event.taskTitle}: ${(0, import_core30.truncateCheckpointSummary)(event.summary)}` });
|
|
6846
7211
|
return;
|
|
6847
7212
|
case "review_needed":
|
|
6848
7213
|
dispatch({ type: "notice", message: "Plan needs your sign-off \u2014 /approve to continue." });
|
|
@@ -6875,6 +7240,12 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
6875
7240
|
case "isolation_handoff":
|
|
6876
7241
|
dispatch({ type: "isolationHandoff", handoff: { repos: event.repos, landed: event.landed }, sessionId });
|
|
6877
7242
|
return;
|
|
7243
|
+
// A structured task's log as it happens (ADR-0018, P1): the task view is
|
|
7244
|
+
// its only reader, and the reducer drops it unless that view is open on
|
|
7245
|
+
// this task. The run's watcher itself does nothing with it.
|
|
7246
|
+
case "task_log":
|
|
7247
|
+
dispatch({ type: "taskLog", taskId: String(event.taskId), attempt: event.attempt, events: event.events, sessionId });
|
|
7248
|
+
return;
|
|
6878
7249
|
// Raw runner chatter. Listed rather than defaulted, so a new lifecycle
|
|
6879
7250
|
// variant fails to compile here until someone decides what a run's watcher
|
|
6880
7251
|
// does with it.
|
|
@@ -7025,17 +7396,73 @@ async function perform(effect, deps) {
|
|
|
7025
7396
|
}
|
|
7026
7397
|
return;
|
|
7027
7398
|
}
|
|
7399
|
+
// The card settles when the task log reports the answer, not here.
|
|
7400
|
+
case "answerTaskApproval": {
|
|
7401
|
+
try {
|
|
7402
|
+
await api.respondToApproval(effect.sessionId, effect.approvalId, effect.answer);
|
|
7403
|
+
} catch {
|
|
7404
|
+
dispatch({ type: "failed", message: "Could not deliver the answer \u2014 the task is still waiting for approval." });
|
|
7405
|
+
}
|
|
7406
|
+
return;
|
|
7407
|
+
}
|
|
7028
7408
|
case "openTaskTerminal": {
|
|
7029
7409
|
const result = await deps.openTerminal(effect.sessionId, effect.taskId);
|
|
7030
7410
|
dispatch(result.ok ? { type: "notice", message: result.message } : { type: "failed", message: result.message });
|
|
7031
7411
|
return;
|
|
7032
7412
|
}
|
|
7413
|
+
// The saved log is read off disk (ADR-0018, P1) so the view opens with the
|
|
7414
|
+
// task's history; the live `task_log` stream then catches it up in place.
|
|
7415
|
+
case "openTaskLog": {
|
|
7416
|
+
const attempts = await api.getTaskLogAttempts(effect.sessionId, effect.taskId, workspace);
|
|
7417
|
+
if (attempts.length === 0) {
|
|
7418
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts: [], attempt: 0, events: [], sessionId: effect.sessionId });
|
|
7419
|
+
return;
|
|
7420
|
+
}
|
|
7421
|
+
const attempt = attempts[attempts.length - 1];
|
|
7422
|
+
const events = await api.getTaskLog(effect.sessionId, effect.taskId, attempt, workspace);
|
|
7423
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts, attempt, events, sessionId: effect.sessionId });
|
|
7424
|
+
return;
|
|
7425
|
+
}
|
|
7426
|
+
case "loadTaskAttempt": {
|
|
7427
|
+
const events = await api.getTaskLog(effect.sessionId, effect.taskId, effect.attempt, workspace);
|
|
7428
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempt: effect.attempt, events, sessionId: effect.sessionId });
|
|
7429
|
+
return;
|
|
7430
|
+
}
|
|
7431
|
+
// The daemon queues the message (or delivers it to a waiting task) and
|
|
7432
|
+
// broadcasts the `task_log` event that shows it; nothing to dispatch here.
|
|
7433
|
+
case "sendTaskMessage":
|
|
7434
|
+
await api.sendTaskMessage(effect.sessionId, effect.taskId, effect.text);
|
|
7435
|
+
return;
|
|
7436
|
+
// The daemon starts the new attempt; its `task_log` and status arrive over
|
|
7437
|
+
// the execution stream, which a TUI not already watching a run holds open.
|
|
7438
|
+
case "continueTask": {
|
|
7439
|
+
const { sessionId, taskId, text } = effect;
|
|
7440
|
+
const request = () => api.continueTask(sessionId, taskId, text);
|
|
7441
|
+
await (effect.watch ? withExecutionStream(deps, sessionId, request) : request());
|
|
7442
|
+
await refreshPlan(deps, sessionId);
|
|
7443
|
+
return;
|
|
7444
|
+
}
|
|
7445
|
+
case "removeTaskMessage": {
|
|
7446
|
+
const { removed } = await api.removeQueuedTaskMessage(effect.sessionId, effect.taskId, effect.messageId);
|
|
7447
|
+
if (!removed) dispatch({ type: "notice", message: "That message was already delivered to the task." });
|
|
7448
|
+
return;
|
|
7449
|
+
}
|
|
7450
|
+
case "interruptTask":
|
|
7451
|
+
await api.interruptTask(effect.sessionId, effect.taskId);
|
|
7452
|
+
dispatch({ type: "notice", message: "Interrupting the task\u2026" });
|
|
7453
|
+
return;
|
|
7033
7454
|
case "command": {
|
|
7034
7455
|
const result = await api.sendCommand(effect.name, { action: effect.action });
|
|
7035
7456
|
if (result.settings) dispatch({ type: "settingsLoaded", settings: result.settings });
|
|
7036
7457
|
dispatch({ type: "notice", message: `${effect.name} is ${effect.action}.` });
|
|
7037
7458
|
return;
|
|
7038
7459
|
}
|
|
7460
|
+
case "setTransport": {
|
|
7461
|
+
const result = await api.sendCommand("transport", { action: effect.transport });
|
|
7462
|
+
if (result.settings) dispatch({ type: "settingsLoaded", settings: result.settings });
|
|
7463
|
+
dispatch({ type: "notice", message: `Runner transport is ${effect.transport} (experimental) \u2014 it applies from the next run.` });
|
|
7464
|
+
return;
|
|
7465
|
+
}
|
|
7039
7466
|
case "setModel":
|
|
7040
7467
|
await api.updateSettings({ orchestratorModel: effect.modelId });
|
|
7041
7468
|
deps.setEnvVar("ORCHESTRATOR_MODEL", effect.modelId);
|
|
@@ -7043,7 +7470,7 @@ async function perform(effect, deps) {
|
|
|
7043
7470
|
dispatch({ type: "notice", message: `Orchestrator model set to ${effect.modelId}.` });
|
|
7044
7471
|
return;
|
|
7045
7472
|
case "setPlanner": {
|
|
7046
|
-
const meta =
|
|
7473
|
+
const meta = import_core31.ALL_PROVIDERS[effect.provider];
|
|
7047
7474
|
if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
|
|
7048
7475
|
const env = { AI_PROVIDER: effect.provider };
|
|
7049
7476
|
const settings = await api.updateSettings({ env });
|
|
@@ -7055,7 +7482,7 @@ async function perform(effect, deps) {
|
|
|
7055
7482
|
});
|
|
7056
7483
|
dispatch({
|
|
7057
7484
|
type: "notice",
|
|
7058
|
-
message: `${(0,
|
|
7485
|
+
message: `${(0, import_core31.isCliProvider)(effect.provider) ? `Planning with ${meta.label} \u2014 no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
|
|
7059
7486
|
});
|
|
7060
7487
|
await loadModels(deps);
|
|
7061
7488
|
return;
|
|
@@ -7067,7 +7494,7 @@ async function perform(effect, deps) {
|
|
|
7067
7494
|
dispatch({ type: "notice", message: `Planner effort set to ${effect.effort || "the runner default"}.` });
|
|
7068
7495
|
return;
|
|
7069
7496
|
case "setApiKey": {
|
|
7070
|
-
const meta =
|
|
7497
|
+
const meta = import_core31.ALL_PROVIDERS[effect.provider];
|
|
7071
7498
|
if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
|
|
7072
7499
|
await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
|
|
7073
7500
|
deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
|
|
@@ -7238,7 +7665,7 @@ function osc52(text) {
|
|
|
7238
7665
|
function copySelection(deps, text) {
|
|
7239
7666
|
if (!text) return;
|
|
7240
7667
|
const lines = text.split("\n").length;
|
|
7241
|
-
const command = (0,
|
|
7668
|
+
const command = (0, import_core31.clipboardCopyCommand)(deps.hasBin);
|
|
7242
7669
|
if (command) {
|
|
7243
7670
|
try {
|
|
7244
7671
|
(deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
|
|
@@ -7348,13 +7775,18 @@ var CONTROL2 = {
|
|
|
7348
7775
|
"": "ctrl-c",
|
|
7349
7776
|
"": "ctrl-d",
|
|
7350
7777
|
"": "ctrl-e",
|
|
7778
|
+
"\x07": "ctrl-g",
|
|
7351
7779
|
"\v": "ctrl-k",
|
|
7352
7780
|
"\f": "ctrl-l",
|
|
7353
7781
|
"": "ctrl-n",
|
|
7354
7782
|
"": "ctrl-o",
|
|
7355
7783
|
"": "ctrl-p",
|
|
7784
|
+
"": "ctrl-r",
|
|
7785
|
+
"": "ctrl-t",
|
|
7356
7786
|
"": "ctrl-u",
|
|
7357
|
-
"": "ctrl-w"
|
|
7787
|
+
"": "ctrl-w",
|
|
7788
|
+
"": "ctrl-x",
|
|
7789
|
+
"": "ctrl-y"
|
|
7358
7790
|
};
|
|
7359
7791
|
var ESCAPES = {
|
|
7360
7792
|
"\x1B[A": "up",
|
|
@@ -7623,7 +8055,7 @@ async function handleTui(subArgs) {
|
|
|
7623
8055
|
}
|
|
7624
8056
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
7625
8057
|
try {
|
|
7626
|
-
(0,
|
|
8058
|
+
(0, import_core32.assertWorkspaceExists)(workspace);
|
|
7627
8059
|
} catch {
|
|
7628
8060
|
console.error(
|
|
7629
8061
|
`Workspace "${workspace}" does not exist.
|
|
@@ -7632,7 +8064,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
7632
8064
|
);
|
|
7633
8065
|
process.exit(1);
|
|
7634
8066
|
}
|
|
7635
|
-
registerSkillCommands((0,
|
|
8067
|
+
registerSkillCommands((0, import_core32.createSkillsService)(workspace).listSkills());
|
|
7636
8068
|
const explicitPort = flag(subArgs, "--port") || process.env.ORDEWELL_PORT;
|
|
7637
8069
|
const targetPort = explicitPort ? resolvePort(subArgs) : await findFreePort();
|
|
7638
8070
|
const { port, owned: ownedAtLaunch } = await ensureDaemonOwned(targetPort, { detached: false });
|
|
@@ -7649,7 +8081,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
7649
8081
|
workspace,
|
|
7650
8082
|
port,
|
|
7651
8083
|
dispatch: (action) => app.dispatch(action),
|
|
7652
|
-
newSessionId: () => (0,
|
|
8084
|
+
newSessionId: () => (0, import_core32.mintSessionId)(),
|
|
7653
8085
|
setEnvVar: (key, value) => {
|
|
7654
8086
|
writeEnvVar(findEnvFile(), key, value);
|
|
7655
8087
|
process.env[key] = value;
|
|
@@ -7725,6 +8157,7 @@ var COMMANDS = {
|
|
|
7725
8157
|
key: handleKey,
|
|
7726
8158
|
runners: handleRunners,
|
|
7727
8159
|
auto: handleAuto,
|
|
8160
|
+
transport: handleTransport,
|
|
7728
8161
|
refresh: handleRefresh,
|
|
7729
8162
|
setup: handleSetup,
|
|
7730
8163
|
plugins: handlePlugins,
|
|
@@ -7738,6 +8171,7 @@ var COMMANDS = {
|
|
|
7738
8171
|
"force-start": handleForceStart,
|
|
7739
8172
|
"retry": handleRetry,
|
|
7740
8173
|
"cancel": handleCancel,
|
|
8174
|
+
continue: handleContinue,
|
|
7741
8175
|
"add-task": handleAddTask,
|
|
7742
8176
|
"remove-task": handleRemoveTask,
|
|
7743
8177
|
"task-runner": handleTaskRunner,
|
|
@@ -7770,7 +8204,7 @@ function cliVersion() {
|
|
|
7770
8204
|
// src/main.ts
|
|
7771
8205
|
var TUI_FLAGS = /* @__PURE__ */ new Set(["--workspace", "--port"]);
|
|
7772
8206
|
async function main() {
|
|
7773
|
-
(0,
|
|
8207
|
+
(0, import_core33.migrateOldConfigDir)();
|
|
7774
8208
|
loadEnvFile();
|
|
7775
8209
|
const argv = process.argv.slice(2);
|
|
7776
8210
|
if (TUI_FLAGS.has(argv[0] ?? "")) argv.unshift("tui");
|