executant 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/index.js +304 -157
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -121,6 +121,20 @@ steps:
|
|
|
121
121
|
- **`self_healing: true`** — on script failure, Claude diagnoses and repairs the command, then re-runs it, up to 5×
|
|
122
122
|
- **`self_improve: true`** — after the workflow finishes, Claude analyzes execution highlights and saves an improved YAML to `tasks/backlog/`
|
|
123
123
|
|
|
124
|
+
## Interjection
|
|
125
|
+
|
|
126
|
+
While a workflow is running, press **`i`** to open a text input at the bottom of the TUI. Type a correction and press **Enter** to send it; **Esc** cancels.
|
|
127
|
+
|
|
128
|
+
The message is queued and prepended to the **next Claude step's prompt** as `[User correction from a previous step]`. Claude sees your note before it starts and incorporates it into its work. If you interject while a script step is running, the correction waits for the next Claude step in the workflow.
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
press i → ▷ don't delete that file, use git revert▌ esc to cancel
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**What it's good for:** steering the next Claude step while watching the current one run — leaving a note for the step that's about to start.
|
|
135
|
+
|
|
136
|
+
**What it can't do:** interrupt a Claude step mid-execution. The Claude CLI processes each invocation as a complete unit; there's no mechanism to inject a message partway through. To abort a runaway step immediately, press `q`.
|
|
137
|
+
|
|
124
138
|
## Examples
|
|
125
139
|
|
|
126
140
|
| File | Demonstrates |
|
package/dist/index.js
CHANGED
|
@@ -406,12 +406,11 @@ import { execSync, spawn as spawn2 } from "node:child_process";
|
|
|
406
406
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
407
407
|
var METHODOLOGY = loadPrompt("development-methodology");
|
|
408
408
|
var DEFAULT_TOOLS = ["Read", "Edit", "Write", "Bash", "Glob", "Grep"];
|
|
409
|
-
function buildClaudeArgs(task) {
|
|
409
|
+
function buildClaudeArgs(task, interactive = false) {
|
|
410
410
|
const allowedTools = task.allowedTools ?? DEFAULT_TOOLS;
|
|
411
411
|
const permissionMode = task.permissionMode ?? "bypassPermissions";
|
|
412
412
|
return [
|
|
413
|
-
"--print",
|
|
414
|
-
task.prompt,
|
|
413
|
+
...interactive ? [] : ["--print", task.prompt],
|
|
415
414
|
"--output-format",
|
|
416
415
|
"stream-json",
|
|
417
416
|
"--verbose",
|
|
@@ -433,7 +432,7 @@ function resolveClaudePath() {
|
|
|
433
432
|
);
|
|
434
433
|
}
|
|
435
434
|
}
|
|
436
|
-
async function* runClaude(task) {
|
|
435
|
+
async function* runClaude(task, _channel) {
|
|
437
436
|
yield {
|
|
438
437
|
type: "log",
|
|
439
438
|
level: "info",
|
|
@@ -562,7 +561,7 @@ function shouldSkipStep(stepNumber, name, options2) {
|
|
|
562
561
|
}
|
|
563
562
|
return options2.fromStep !== void 0 && stepNumber < options2.fromStep[0];
|
|
564
563
|
}
|
|
565
|
-
async function* runWorkflow(workflow2, options2 = {}) {
|
|
564
|
+
async function* runWorkflow(workflow2, options2 = {}, channel2) {
|
|
566
565
|
const workflowStart = Date.now();
|
|
567
566
|
yield { type: "workflow:start", workflow: workflow2 };
|
|
568
567
|
for (const [i, task] of workflow2.tasks.entries()) {
|
|
@@ -575,7 +574,7 @@ async function* runWorkflow(workflow2, options2 = {}) {
|
|
|
575
574
|
yield { type: "step:start", index: i, name: task.name };
|
|
576
575
|
const from = options2.fromStep && options2.fromStep[0] === stepNumber ? options2.fromStep.slice(1) : void 0;
|
|
577
576
|
try {
|
|
578
|
-
for await (const event of runStep(task, from)) {
|
|
577
|
+
for await (const event of runStep(task, from, channel2)) {
|
|
579
578
|
if (event.type === "step:iteration" || event.type === "step:inner" || event.type === "output:text" || event.type === "output:tool") {
|
|
580
579
|
yield { ...event, index: i };
|
|
581
580
|
} else {
|
|
@@ -600,7 +599,7 @@ async function* runWorkflow(workflow2, options2 = {}) {
|
|
|
600
599
|
durationMs: Date.now() - workflowStart
|
|
601
600
|
};
|
|
602
601
|
}
|
|
603
|
-
async function* runStep(task, from) {
|
|
602
|
+
async function* runStep(task, from, channel2) {
|
|
604
603
|
switch (task.type) {
|
|
605
604
|
case "log":
|
|
606
605
|
yield* runLog(task);
|
|
@@ -619,11 +618,20 @@ async function* runStep(task, from) {
|
|
|
619
618
|
}
|
|
620
619
|
case "claude": {
|
|
621
620
|
const expanded = expandContext(task);
|
|
622
|
-
|
|
621
|
+
const queued = channel2?.consumeQueue() ?? [];
|
|
622
|
+
const enriched = queued.length > 0 ? {
|
|
623
|
+
...expanded,
|
|
624
|
+
prompt: `[User correction from a previous step]
|
|
625
|
+
${queued.join("\n")}
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
${expanded.prompt}`
|
|
629
|
+
} : expanded;
|
|
630
|
+
yield* enriched.llmAsJudge ? runClaudeWithJudge(enriched, channel2) : runClaude(enriched, channel2);
|
|
623
631
|
break;
|
|
624
632
|
}
|
|
625
633
|
case "forEach":
|
|
626
|
-
yield* runForEach(task, from);
|
|
634
|
+
yield* runForEach(task, from, channel2);
|
|
627
635
|
break;
|
|
628
636
|
default: {
|
|
629
637
|
const _ = task;
|
|
@@ -634,7 +642,7 @@ async function* runStep(task, from) {
|
|
|
634
642
|
async function* runLog(task) {
|
|
635
643
|
yield { type: "output:text", index: -1, text: task.message };
|
|
636
644
|
}
|
|
637
|
-
async function* runForEach(task, from) {
|
|
645
|
+
async function* runForEach(task, from, channel2) {
|
|
638
646
|
const items = await resolveItems(task.forEach);
|
|
639
647
|
const total = items.length;
|
|
640
648
|
const innerTotal = task.inner.length;
|
|
@@ -669,7 +677,7 @@ async function* runForEach(task, from) {
|
|
|
669
677
|
}
|
|
670
678
|
const childFrom = childIdx === startChild ? iterFrom?.slice(1) : void 0;
|
|
671
679
|
try {
|
|
672
|
-
for await (const event of runStep(substituted, childFrom)) {
|
|
680
|
+
for await (const event of runStep(substituted, childFrom, channel2)) {
|
|
673
681
|
if (event.type !== "step:iteration" && event.type !== "step:inner") {
|
|
674
682
|
yield event;
|
|
675
683
|
}
|
|
@@ -803,14 +811,14 @@ async function* runCommandWithHealing(task) {
|
|
|
803
811
|
}
|
|
804
812
|
}
|
|
805
813
|
}
|
|
806
|
-
async function* runClaudeWithJudge(task) {
|
|
814
|
+
async function* runClaudeWithJudge(task, channel2) {
|
|
807
815
|
let judgeContext = "";
|
|
808
816
|
for (let attempt = 0; attempt < MAX_JUDGE_RETRIES; attempt++) {
|
|
809
817
|
const prompt = attempt === 0 ? task.prompt : `${task.prompt}
|
|
810
818
|
|
|
811
819
|
${fillTemplate(JUDGE_RETRY_CONTEXT, { FEEDBACK: judgeContext })}`;
|
|
812
820
|
const lines = [];
|
|
813
|
-
yield* collectLines(runClaude({ ...task, prompt }), lines);
|
|
821
|
+
yield* collectLines(runClaude({ ...task, prompt }, channel2), lines);
|
|
814
822
|
yield {
|
|
815
823
|
type: "log",
|
|
816
824
|
level: "info",
|
|
@@ -918,18 +926,86 @@ ${blocks.join("\n\n")}`;
|
|
|
918
926
|
init_update();
|
|
919
927
|
|
|
920
928
|
// src/ui/App.tsx
|
|
921
|
-
import { useEffect as useEffect2, useReducer, useState } from "react";
|
|
922
|
-
import { Box as
|
|
929
|
+
import { useEffect as useEffect2, useReducer, useState as useState2 } from "react";
|
|
930
|
+
import { Box as Box6, Text as Text6, useApp, useStdin, useStdout } from "ink";
|
|
923
931
|
|
|
924
932
|
// src/ui/KeyboardHandler.tsx
|
|
925
933
|
import { useInput } from "ink";
|
|
926
|
-
function KeyboardHandler({
|
|
934
|
+
function KeyboardHandler({
|
|
935
|
+
onExit,
|
|
936
|
+
onInterject,
|
|
937
|
+
isInterjecting
|
|
938
|
+
}) {
|
|
927
939
|
useInput((input, key) => {
|
|
940
|
+
if (isInterjecting) return;
|
|
928
941
|
if (input === "q" || key.ctrl && input === "c") onExit();
|
|
942
|
+
if (input === "i" && onInterject) onInterject();
|
|
929
943
|
});
|
|
930
944
|
return null;
|
|
931
945
|
}
|
|
932
946
|
|
|
947
|
+
// src/ui/InterjectInput.tsx
|
|
948
|
+
import { useState } from "react";
|
|
949
|
+
import { Box, Text, useInput as useInput2 } from "ink";
|
|
950
|
+
|
|
951
|
+
// src/ui/theme.ts
|
|
952
|
+
import { createRequire } from "node:module";
|
|
953
|
+
import { oklchToHex } from "@coston/design-tokens";
|
|
954
|
+
var THEME_NAME = "purple-dark";
|
|
955
|
+
var _require = createRequire(import.meta.url);
|
|
956
|
+
var { themes } = _require("@coston/design-tokens/tokens.json");
|
|
957
|
+
function hex(key) {
|
|
958
|
+
return oklchToHex(themes[THEME_NAME][key]);
|
|
959
|
+
}
|
|
960
|
+
var theme = {
|
|
961
|
+
foreground: hex("foreground"),
|
|
962
|
+
// primary text
|
|
963
|
+
muted: hex("muted-foreground"),
|
|
964
|
+
// dimmed / inactive text and borders
|
|
965
|
+
primary: hex("primary"),
|
|
966
|
+
// tool calls, cursor, active
|
|
967
|
+
primaryLight: hex("secondary-foreground"),
|
|
968
|
+
// lighter tint of primary (same hue, higher lightness)
|
|
969
|
+
success: hex("success"),
|
|
970
|
+
// completed steps
|
|
971
|
+
error: hex("destructive"),
|
|
972
|
+
// errors
|
|
973
|
+
warning: hex("warning"),
|
|
974
|
+
// warnings, retries, updates
|
|
975
|
+
border: hex("border")
|
|
976
|
+
// log pane border
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
// src/ui/InterjectInput.tsx
|
|
980
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
981
|
+
function InterjectInput({ onSubmit, onCancel }) {
|
|
982
|
+
const [value, setValue] = useState("");
|
|
983
|
+
useInput2((input, key) => {
|
|
984
|
+
if (key.escape) {
|
|
985
|
+
onCancel();
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (key.return) {
|
|
989
|
+
if (value.trim()) onSubmit(value.trim());
|
|
990
|
+
else onCancel();
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (key.backspace || key.delete) {
|
|
994
|
+
setValue((v) => v.slice(0, -1));
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
if (input && !key.ctrl && !key.meta) {
|
|
998
|
+
setValue((v) => v + input);
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
return /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "row", children: [
|
|
1002
|
+
/* @__PURE__ */ jsx(Text, { color: theme.primary, bold: true, children: "\u25B7 " }),
|
|
1003
|
+
/* @__PURE__ */ jsx(Text, { children: value }),
|
|
1004
|
+
/* @__PURE__ */ jsx(Text, { color: theme.primary, children: "\u258C" }),
|
|
1005
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " esc to cancel" })
|
|
1006
|
+
] });
|
|
1007
|
+
}
|
|
1008
|
+
|
|
933
1009
|
// src/ui/formatTool.ts
|
|
934
1010
|
function formatToolCall2(tool, input) {
|
|
935
1011
|
switch (tool) {
|
|
@@ -1081,6 +1157,11 @@ function reducer(state, event) {
|
|
|
1081
1157
|
if (idx >= state.tasks.length) return state;
|
|
1082
1158
|
return appendLines(state, idx, `[${event.level}] ${event.text}`);
|
|
1083
1159
|
}
|
|
1160
|
+
case "step:interjection": {
|
|
1161
|
+
const idx = event.index;
|
|
1162
|
+
if (idx >= state.tasks.length) return state;
|
|
1163
|
+
return appendLines(state, idx, `[interjection] ${event.message}`);
|
|
1164
|
+
}
|
|
1084
1165
|
default: {
|
|
1085
1166
|
const _ = event;
|
|
1086
1167
|
void _;
|
|
@@ -1116,35 +1197,7 @@ function appendLines(state, index, text) {
|
|
|
1116
1197
|
}
|
|
1117
1198
|
|
|
1118
1199
|
// src/ui/TaskRow.tsx
|
|
1119
|
-
import { Box, Text } from "ink";
|
|
1120
|
-
|
|
1121
|
-
// src/ui/theme.ts
|
|
1122
|
-
import { createRequire } from "node:module";
|
|
1123
|
-
import { oklchToHex } from "@coston/design-tokens";
|
|
1124
|
-
var THEME_NAME = "purple-dark";
|
|
1125
|
-
var _require = createRequire(import.meta.url);
|
|
1126
|
-
var { themes } = _require("@coston/design-tokens/tokens.json");
|
|
1127
|
-
function hex(key) {
|
|
1128
|
-
return oklchToHex(themes[THEME_NAME][key]);
|
|
1129
|
-
}
|
|
1130
|
-
var theme = {
|
|
1131
|
-
foreground: hex("foreground"),
|
|
1132
|
-
// primary text
|
|
1133
|
-
muted: hex("muted-foreground"),
|
|
1134
|
-
// dimmed / inactive text and borders
|
|
1135
|
-
primary: hex("primary"),
|
|
1136
|
-
// tool calls, cursor, active
|
|
1137
|
-
primaryLight: hex("secondary-foreground"),
|
|
1138
|
-
// lighter tint of primary (same hue, higher lightness)
|
|
1139
|
-
success: hex("success"),
|
|
1140
|
-
// completed steps
|
|
1141
|
-
error: hex("destructive"),
|
|
1142
|
-
// errors
|
|
1143
|
-
warning: hex("warning"),
|
|
1144
|
-
// warnings, retries, updates
|
|
1145
|
-
border: hex("border")
|
|
1146
|
-
// log pane border
|
|
1147
|
-
};
|
|
1200
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
1148
1201
|
|
|
1149
1202
|
// src/ui/utils.ts
|
|
1150
1203
|
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -1162,6 +1215,12 @@ var STATUS_COLOR = {
|
|
|
1162
1215
|
function statusIcon(status, tick) {
|
|
1163
1216
|
return status === "running" ? SPINNER[tick % SPINNER.length] : STATUS_ICON[status] ?? "\xB7";
|
|
1164
1217
|
}
|
|
1218
|
+
function countIterationRows(iterationHistory, maxVisible) {
|
|
1219
|
+
if (!iterationHistory?.length) return 0;
|
|
1220
|
+
if (iterationHistory.every((r) => r.item === String(r.iteration))) return 0;
|
|
1221
|
+
const visible = Math.min(iterationHistory.length, maxVisible);
|
|
1222
|
+
return visible + (iterationHistory.length > maxVisible ? 1 : 0);
|
|
1223
|
+
}
|
|
1165
1224
|
var EXIT_DELAY_MS = 300;
|
|
1166
1225
|
function formatHeaderElapsed(start, end) {
|
|
1167
1226
|
const ms = (end ?? Date.now()) - start;
|
|
@@ -1176,7 +1235,7 @@ function formatTaskElapsed(start, end, status) {
|
|
|
1176
1235
|
}
|
|
1177
1236
|
|
|
1178
1237
|
// src/ui/TaskRow.tsx
|
|
1179
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1238
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1180
1239
|
function TaskRow({ taskState, isActive, index, tick }) {
|
|
1181
1240
|
const { task, status, startTime, endTime } = taskState;
|
|
1182
1241
|
const icon = statusIcon(status, tick);
|
|
@@ -1184,13 +1243,13 @@ function TaskRow({ taskState, isActive, index, tick }) {
|
|
|
1184
1243
|
const elapsed = formatTaskElapsed(startTime, endTime, status);
|
|
1185
1244
|
const iterInfo = formatIterCount(taskState.iterationHistory);
|
|
1186
1245
|
const label = `${index + 1}. ${task.name}${iterInfo}`;
|
|
1187
|
-
return /* @__PURE__ */
|
|
1188
|
-
/* @__PURE__ */
|
|
1246
|
+
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
1247
|
+
/* @__PURE__ */ jsxs2(Text2, { color, children: [
|
|
1189
1248
|
icon,
|
|
1190
1249
|
" "
|
|
1191
1250
|
] }),
|
|
1192
|
-
/* @__PURE__ */
|
|
1193
|
-
/* @__PURE__ */
|
|
1251
|
+
/* @__PURE__ */ jsx2(Text2, { color: isActive ? theme.foreground : theme.muted, bold: isActive, children: label }),
|
|
1252
|
+
/* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
1194
1253
|
" ",
|
|
1195
1254
|
elapsed
|
|
1196
1255
|
] })
|
|
@@ -1209,22 +1268,22 @@ function formatIterCount(history) {
|
|
|
1209
1268
|
}
|
|
1210
1269
|
|
|
1211
1270
|
// src/ui/IterationRow.tsx
|
|
1212
|
-
import { Box as
|
|
1213
|
-
import { Fragment, jsx as
|
|
1271
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
1272
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1214
1273
|
function IterationRow({ record, tick }) {
|
|
1215
1274
|
const icon = statusIcon(record.status, tick);
|
|
1216
1275
|
const color = STATUS_COLOR[record.status] ?? theme.primary;
|
|
1217
1276
|
const innerText = record.inner ? ` \u2014 ${stripItem(record.inner.name, record.item)} [${record.inner.index + 1}/${record.inner.total}]` : "";
|
|
1218
1277
|
const ms = (record.endTime ?? Date.now()) - record.startTime;
|
|
1219
1278
|
const elapsed = `${(ms / 1e3).toFixed(1)}s`;
|
|
1220
|
-
return /* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1222
|
-
/* @__PURE__ */
|
|
1223
|
-
/* @__PURE__ */
|
|
1224
|
-
/* @__PURE__ */
|
|
1225
|
-
/* @__PURE__ */
|
|
1226
|
-
/* @__PURE__ */
|
|
1227
|
-
|
|
1279
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
1280
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " " }),
|
|
1281
|
+
/* @__PURE__ */ jsx3(Text3, { color, children: icon }),
|
|
1282
|
+
/* @__PURE__ */ jsx3(Text3, { children: " " }),
|
|
1283
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: `[${record.iteration}/${record.total}]` }),
|
|
1284
|
+
/* @__PURE__ */ jsx3(Text3, { children: " " }),
|
|
1285
|
+
/* @__PURE__ */ jsxs3(
|
|
1286
|
+
Text3,
|
|
1228
1287
|
{
|
|
1229
1288
|
color: record.status === "running" ? theme.foreground : theme.muted,
|
|
1230
1289
|
children: [
|
|
@@ -1233,7 +1292,7 @@ function IterationRow({ record, tick }) {
|
|
|
1233
1292
|
]
|
|
1234
1293
|
}
|
|
1235
1294
|
),
|
|
1236
|
-
/* @__PURE__ */
|
|
1295
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1237
1296
|
" ",
|
|
1238
1297
|
elapsed
|
|
1239
1298
|
] })
|
|
@@ -1255,29 +1314,29 @@ function IterationList({
|
|
|
1255
1314
|
if (isRepeatStyle(iterationHistory)) return null;
|
|
1256
1315
|
const hidden = iterationHistory.length - maxVisible;
|
|
1257
1316
|
const visible = iterationHistory.slice(-maxVisible);
|
|
1258
|
-
return /* @__PURE__ */
|
|
1259
|
-
hidden > 0 && /* @__PURE__ */
|
|
1260
|
-
visible.map((record) => /* @__PURE__ */
|
|
1317
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1318
|
+
hidden > 0 && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` \u2026 ${hidden} earlier` }),
|
|
1319
|
+
visible.map((record) => /* @__PURE__ */ jsx3(IterationRow, { record, tick }, record.iteration))
|
|
1261
1320
|
] });
|
|
1262
1321
|
}
|
|
1263
1322
|
|
|
1264
1323
|
// src/ui/LogPane.tsx
|
|
1265
|
-
import { Box as
|
|
1266
|
-
import { jsx as
|
|
1324
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
1325
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1267
1326
|
function LogPane({ lines, isActive = false, maxLines = 15 }) {
|
|
1268
1327
|
const visible = lines.slice(-maxLines);
|
|
1269
1328
|
if (visible.length === 0) {
|
|
1270
|
-
return /* @__PURE__ */
|
|
1329
|
+
return /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: isActive ? "\u2838 waiting for output\u2026" : "\u2014 no output yet \u2014" }) });
|
|
1271
1330
|
}
|
|
1272
|
-
return /* @__PURE__ */
|
|
1273
|
-
|
|
1331
|
+
return /* @__PURE__ */ jsx4(
|
|
1332
|
+
Box4,
|
|
1274
1333
|
{
|
|
1275
1334
|
flexDirection: "column",
|
|
1276
1335
|
marginTop: 1,
|
|
1277
1336
|
borderStyle: "single",
|
|
1278
1337
|
borderColor: theme.border,
|
|
1279
1338
|
paddingX: 1,
|
|
1280
|
-
children: visible.map((line, i) => /* @__PURE__ */
|
|
1339
|
+
children: visible.map((line, i) => /* @__PURE__ */ jsx4(
|
|
1281
1340
|
LogLine,
|
|
1282
1341
|
{
|
|
1283
1342
|
text: line,
|
|
@@ -1289,52 +1348,52 @@ function LogPane({ lines, isActive = false, maxLines = 15 }) {
|
|
|
1289
1348
|
);
|
|
1290
1349
|
}
|
|
1291
1350
|
function LogLine({ text, cursor }) {
|
|
1292
|
-
const suffix = cursor ? /* @__PURE__ */
|
|
1351
|
+
const suffix = cursor ? /* @__PURE__ */ jsx4(Text4, { color: theme.primary, children: " \u258C" }) : null;
|
|
1293
1352
|
if (/^\[[\w:]+\]/.test(text)) {
|
|
1294
1353
|
const bracket = text.match(/^\[[\w:]+\]/)?.[0] ?? "";
|
|
1295
1354
|
const rest = text.slice(bracket.length);
|
|
1296
|
-
return /* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1298
|
-
/* @__PURE__ */
|
|
1355
|
+
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1356
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.primary, children: bracket }),
|
|
1357
|
+
/* @__PURE__ */ jsx4(Text4, { children: rest }),
|
|
1299
1358
|
suffix
|
|
1300
1359
|
] });
|
|
1301
1360
|
}
|
|
1302
1361
|
if (/^\s*\$\s/.test(text))
|
|
1303
|
-
return /* @__PURE__ */
|
|
1362
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.warning, children: [
|
|
1304
1363
|
text,
|
|
1305
1364
|
suffix
|
|
1306
1365
|
] });
|
|
1307
1366
|
if (text.startsWith("[warn]"))
|
|
1308
|
-
return /* @__PURE__ */
|
|
1367
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.warning, children: [
|
|
1309
1368
|
text,
|
|
1310
1369
|
suffix
|
|
1311
1370
|
] });
|
|
1312
1371
|
if (text.startsWith("[error]"))
|
|
1313
|
-
return /* @__PURE__ */
|
|
1372
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.error, children: [
|
|
1314
1373
|
text,
|
|
1315
1374
|
suffix
|
|
1316
1375
|
] });
|
|
1317
1376
|
if (/^[\s]*(✓|✔|✅|done|success|compiled|built|passed)/i.test(text) && !/\b(error|fail|failed|warn|warning)\b/i.test(text))
|
|
1318
|
-
return /* @__PURE__ */
|
|
1377
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.success, children: [
|
|
1319
1378
|
text,
|
|
1320
1379
|
suffix
|
|
1321
1380
|
] });
|
|
1322
1381
|
if (/\b(error|failed|fail)\b/i.test(text))
|
|
1323
|
-
return /* @__PURE__ */
|
|
1382
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.error, children: [
|
|
1324
1383
|
text,
|
|
1325
1384
|
suffix
|
|
1326
1385
|
] });
|
|
1327
1386
|
if (/\b(warn|warning)\b/i.test(text))
|
|
1328
|
-
return /* @__PURE__ */
|
|
1387
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.warning, children: [
|
|
1329
1388
|
text,
|
|
1330
1389
|
suffix
|
|
1331
1390
|
] });
|
|
1332
1391
|
if (/^[·…⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/.test(text))
|
|
1333
|
-
return /* @__PURE__ */
|
|
1392
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.muted, children: [
|
|
1334
1393
|
text,
|
|
1335
1394
|
suffix
|
|
1336
1395
|
] });
|
|
1337
|
-
return /* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1338
1397
|
text,
|
|
1339
1398
|
suffix
|
|
1340
1399
|
] });
|
|
@@ -1354,8 +1413,8 @@ function useInterval(callback, delayMs) {
|
|
|
1354
1413
|
}
|
|
1355
1414
|
|
|
1356
1415
|
// src/ui/BrandMark.tsx
|
|
1357
|
-
import { Box as
|
|
1358
|
-
import { jsx as
|
|
1416
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
1417
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1359
1418
|
var BRAND = "Executant";
|
|
1360
1419
|
var SWEEP_TICKS = BRAND.length * 2;
|
|
1361
1420
|
var GAP_TICKS = 30;
|
|
@@ -1368,15 +1427,22 @@ function charColor(charIndex, tick, isActive) {
|
|
|
1368
1427
|
return charIndex === charPos ? theme.primaryLight : theme.primary;
|
|
1369
1428
|
}
|
|
1370
1429
|
function BrandMark({ tick, isActive }) {
|
|
1371
|
-
return /* @__PURE__ */
|
|
1430
|
+
return /* @__PURE__ */ jsx5(Box5, { children: [...BRAND].map((char, i) => /* @__PURE__ */ jsx5(Text5, { color: charColor(i, tick, isActive), bold: true, children: char }, i)) });
|
|
1372
1431
|
}
|
|
1373
1432
|
|
|
1374
1433
|
// src/ui/App.tsx
|
|
1375
|
-
import { jsx as
|
|
1434
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1376
1435
|
var MAX_VISIBLE_ITERATIONS = 8;
|
|
1377
|
-
function App({
|
|
1436
|
+
function App({
|
|
1437
|
+
workflow: workflow2,
|
|
1438
|
+
events: events2,
|
|
1439
|
+
options: options2,
|
|
1440
|
+
updateCheck: updateCheck2,
|
|
1441
|
+
interjectChannel
|
|
1442
|
+
}) {
|
|
1378
1443
|
const { exit } = useApp();
|
|
1379
1444
|
const [state, dispatch] = useReducer(reducer, buildInitialState(workflow2));
|
|
1445
|
+
const [isInterjecting, setIsInterjecting] = useState2(false);
|
|
1380
1446
|
useEffect2(() => {
|
|
1381
1447
|
let active = true;
|
|
1382
1448
|
(async () => {
|
|
@@ -1405,20 +1471,34 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1405
1471
|
}, [events2, exit]);
|
|
1406
1472
|
const { isRawModeSupported } = useStdin();
|
|
1407
1473
|
const { stdout } = useStdout();
|
|
1408
|
-
const
|
|
1409
|
-
const FIXED_OVERHEAD = 12;
|
|
1410
|
-
const logPaneMaxLines = Math.max(
|
|
1411
|
-
5,
|
|
1412
|
-
terminalRows - FIXED_OVERHEAD - state.tasks.length
|
|
1413
|
-
);
|
|
1414
|
-
const [tick, setTick] = useState(0);
|
|
1415
|
-
useInterval(() => {
|
|
1416
|
-
if (!state.endTime) setTick((t) => t + 1);
|
|
1417
|
-
}, 100);
|
|
1418
|
-
const [updateVersion, setUpdateVersion] = useState(null);
|
|
1474
|
+
const [updateVersion, setUpdateVersion] = useState2(null);
|
|
1419
1475
|
useEffect2(() => {
|
|
1420
1476
|
updateCheck2.then(setUpdateVersion);
|
|
1421
1477
|
}, [updateCheck2]);
|
|
1478
|
+
const [tick, setTick] = useState2(0);
|
|
1479
|
+
useInterval(() => {
|
|
1480
|
+
if (!state.endTime) setTick((t) => t + 1);
|
|
1481
|
+
}, 100);
|
|
1482
|
+
const terminalRows = stdout?.rows ?? 24;
|
|
1483
|
+
const LOG_PANE_MIN = 5;
|
|
1484
|
+
const runningTask = state.tasks.find((t) => t.status === "running");
|
|
1485
|
+
const iterationRowCount = countIterationRows(
|
|
1486
|
+
runningTask?.iterationHistory,
|
|
1487
|
+
MAX_VISIBLE_ITERATIONS
|
|
1488
|
+
);
|
|
1489
|
+
const FIXED_OVERHEAD = 12 + (updateVersion ? 1 : 0) + (isInterjecting ? 1 : 0);
|
|
1490
|
+
const availableForTaskSection = Math.max(
|
|
1491
|
+
1,
|
|
1492
|
+
terminalRows - FIXED_OVERHEAD - LOG_PANE_MIN - iterationRowCount
|
|
1493
|
+
);
|
|
1494
|
+
const visibleTaskCount = state.tasks.length > availableForTaskSection ? availableForTaskSection - 1 : state.tasks.length;
|
|
1495
|
+
const taskSlice = state.tasks.slice(-visibleTaskCount);
|
|
1496
|
+
const hiddenTaskCount = state.tasks.length - taskSlice.length;
|
|
1497
|
+
const taskRowsUsed = visibleTaskCount + (hiddenTaskCount > 0 ? 1 : 0);
|
|
1498
|
+
const logPaneMaxLines = Math.max(
|
|
1499
|
+
LOG_PANE_MIN,
|
|
1500
|
+
terminalRows - FIXED_OVERHEAD - taskRowsUsed - iterationRowCount
|
|
1501
|
+
);
|
|
1422
1502
|
const elapsed = formatHeaderElapsed(state.startTime, state.endTime);
|
|
1423
1503
|
const activeTask = state.tasks[state.currentIndex];
|
|
1424
1504
|
const completedCount = state.tasks.filter(
|
|
@@ -1426,11 +1506,11 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1426
1506
|
).length;
|
|
1427
1507
|
const totalCount = state.tasks.length;
|
|
1428
1508
|
const filterInfo = options2?.stepFilter ? ` [step: ${options2.stepFilter}]` : options2?.fromStep ? ` [from step: ${options2.fromStep}]` : "";
|
|
1429
|
-
return /* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1431
|
-
/* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
1433
|
-
/* @__PURE__ */
|
|
1509
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", padding: 1, children: [
|
|
1510
|
+
/* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(BrandMark, { tick, isActive: !state.endTime }) }),
|
|
1511
|
+
/* @__PURE__ */ jsxs5(Box6, { marginBottom: 1, children: [
|
|
1512
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, color: theme.primary, children: workflow2.goal }),
|
|
1513
|
+
/* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
1434
1514
|
" ",
|
|
1435
1515
|
completedCount,
|
|
1436
1516
|
"/",
|
|
@@ -1440,26 +1520,37 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1440
1520
|
filterInfo
|
|
1441
1521
|
] })
|
|
1442
1522
|
] }),
|
|
1443
|
-
/* @__PURE__ */
|
|
1444
|
-
/* @__PURE__ */
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1523
|
+
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginBottom: 1, children: [
|
|
1524
|
+
hiddenTaskCount > 0 && /* @__PURE__ */ jsxs5(Text6, { dimColor: true, children: [
|
|
1525
|
+
" ",
|
|
1526
|
+
"\xB7\xB7\xB7 ",
|
|
1527
|
+
hiddenTaskCount,
|
|
1528
|
+
" earlier"
|
|
1529
|
+
] }),
|
|
1530
|
+
taskSlice.map((taskState, i) => {
|
|
1531
|
+
const globalIndex = hiddenTaskCount + i;
|
|
1532
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
1533
|
+
/* @__PURE__ */ jsx6(
|
|
1534
|
+
TaskRow,
|
|
1535
|
+
{
|
|
1536
|
+
index: globalIndex,
|
|
1537
|
+
tick,
|
|
1538
|
+
taskState,
|
|
1539
|
+
isActive: globalIndex === state.currentIndex
|
|
1540
|
+
}
|
|
1541
|
+
),
|
|
1542
|
+
taskState.status === "running" && taskState.iterationHistory?.length ? /* @__PURE__ */ jsx6(
|
|
1543
|
+
IterationList,
|
|
1544
|
+
{
|
|
1545
|
+
iterationHistory: taskState.iterationHistory,
|
|
1546
|
+
tick,
|
|
1547
|
+
maxVisible: MAX_VISIBLE_ITERATIONS
|
|
1548
|
+
}
|
|
1549
|
+
) : null
|
|
1550
|
+
] }, globalIndex);
|
|
1551
|
+
})
|
|
1552
|
+
] }),
|
|
1553
|
+
activeTask && /* @__PURE__ */ jsx6(
|
|
1463
1554
|
LogPane,
|
|
1464
1555
|
{
|
|
1465
1556
|
lines: activeTask.lines,
|
|
@@ -1467,22 +1558,44 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1467
1558
|
maxLines: logPaneMaxLines
|
|
1468
1559
|
}
|
|
1469
1560
|
),
|
|
1470
|
-
state.endTime !== void 0 && state.writtenFiles.length > 0 && /* @__PURE__ */
|
|
1471
|
-
/* @__PURE__ */
|
|
1472
|
-
state.writtenFiles.map((f) => /* @__PURE__ */
|
|
1561
|
+
state.endTime !== void 0 && state.writtenFiles.length > 0 && /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
1562
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "files written:" }),
|
|
1563
|
+
state.writtenFiles.map((f) => /* @__PURE__ */ jsxs5(Text6, { color: theme.primary, children: [
|
|
1473
1564
|
" ",
|
|
1474
1565
|
f
|
|
1475
1566
|
] }, f))
|
|
1476
1567
|
] }),
|
|
1477
|
-
/* @__PURE__ */
|
|
1478
|
-
|
|
1568
|
+
isInterjecting && interjectChannel && /* @__PURE__ */ jsx6(
|
|
1569
|
+
InterjectInput,
|
|
1570
|
+
{
|
|
1571
|
+
onSubmit: (msg) => {
|
|
1572
|
+
interjectChannel.interject(msg);
|
|
1573
|
+
dispatch({
|
|
1574
|
+
type: "step:interjection",
|
|
1575
|
+
index: state.currentIndex,
|
|
1576
|
+
message: msg
|
|
1577
|
+
});
|
|
1578
|
+
setIsInterjecting(false);
|
|
1579
|
+
},
|
|
1580
|
+
onCancel: () => setIsInterjecting(false)
|
|
1581
|
+
}
|
|
1582
|
+
),
|
|
1583
|
+
/* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
|
|
1584
|
+
updateVersion && /* @__PURE__ */ jsxs5(Text6, { color: theme.warning, children: [
|
|
1479
1585
|
"v",
|
|
1480
1586
|
updateVersion,
|
|
1481
1587
|
" available \u2014 run: executant update"
|
|
1482
1588
|
] }),
|
|
1483
|
-
/* @__PURE__ */
|
|
1589
|
+
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: isInterjecting ? "typing interjection\u2026" : "press q to quit \xB7 i to interject" })
|
|
1484
1590
|
] }),
|
|
1485
|
-
isRawModeSupported && /* @__PURE__ */
|
|
1591
|
+
isRawModeSupported && /* @__PURE__ */ jsx6(
|
|
1592
|
+
KeyboardHandler,
|
|
1593
|
+
{
|
|
1594
|
+
onExit: exit,
|
|
1595
|
+
onInterject: interjectChannel ? () => setIsInterjecting(true) : void 0,
|
|
1596
|
+
isInterjecting
|
|
1597
|
+
}
|
|
1598
|
+
)
|
|
1486
1599
|
] });
|
|
1487
1600
|
}
|
|
1488
1601
|
|
|
@@ -1891,9 +2004,9 @@ ${issues}`
|
|
|
1891
2004
|
}
|
|
1892
2005
|
|
|
1893
2006
|
// src/ui/PlanApp.tsx
|
|
1894
|
-
import { useEffect as useEffect3, useReducer as useReducer2, useState as
|
|
1895
|
-
import { Box as
|
|
1896
|
-
import { jsx as
|
|
2007
|
+
import { useEffect as useEffect3, useReducer as useReducer2, useState as useState3 } from "react";
|
|
2008
|
+
import { Box as Box7, Text as Text7, useApp as useApp2, useStdin as useStdin2 } from "ink";
|
|
2009
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1897
2010
|
var truncate = (str, max) => str.length > max ? str.slice(0, max - 3) + "..." : str;
|
|
1898
2011
|
function buildInitial(description) {
|
|
1899
2012
|
return {
|
|
@@ -1969,7 +2082,7 @@ function StageProgress({ stage, totalStages, stageNames, tick, isActive, status
|
|
|
1969
2082
|
}
|
|
1970
2083
|
return { icon, color, name, bold, dim };
|
|
1971
2084
|
});
|
|
1972
|
-
return /* @__PURE__ */
|
|
2085
|
+
return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", marginBottom: 1, children: rows.map(({ icon, color, name, bold, dim }, i) => /* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsxs6(Text7, { color, dimColor: dim, bold, children: [
|
|
1973
2086
|
" ",
|
|
1974
2087
|
icon,
|
|
1975
2088
|
name ? ` ${name}` : ""
|
|
@@ -1994,7 +2107,7 @@ function PlanApp({ description, events: events2 }) {
|
|
|
1994
2107
|
};
|
|
1995
2108
|
}, [events2, exit]);
|
|
1996
2109
|
const { isRawModeSupported } = useStdin2();
|
|
1997
|
-
const [tick, setTick] =
|
|
2110
|
+
const [tick, setTick] = useState3(0);
|
|
1998
2111
|
const isActive = state.status === "running" || state.status === "retrying";
|
|
1999
2112
|
useInterval(() => {
|
|
2000
2113
|
if (isActive) setTick((t) => t + 1);
|
|
@@ -2002,19 +2115,19 @@ function PlanApp({ description, events: events2 }) {
|
|
|
2002
2115
|
const elapsed = formatHeaderElapsed(state.startTime);
|
|
2003
2116
|
const icon = isActive ? SPINNER[tick % SPINNER.length] : state.status === "complete" ? "\u2713" : "\u2717";
|
|
2004
2117
|
const iconColor = state.status === "complete" ? theme.success : state.status === "error" ? theme.error : theme.primary;
|
|
2005
|
-
return /* @__PURE__ */
|
|
2006
|
-
/* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2118
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", padding: 1, children: [
|
|
2119
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx7(BrandMark, { tick, isActive }) }),
|
|
2120
|
+
/* @__PURE__ */ jsxs6(Box7, { marginBottom: 1, children: [
|
|
2121
|
+
/* @__PURE__ */ jsxs6(Text7, { color: iconColor, children: [
|
|
2009
2122
|
icon,
|
|
2010
2123
|
" "
|
|
2011
2124
|
] }),
|
|
2012
|
-
/* @__PURE__ */
|
|
2013
|
-
/* @__PURE__ */
|
|
2125
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: theme.primary, children: "Generating plan" }),
|
|
2126
|
+
/* @__PURE__ */ jsxs6(Text7, { dimColor: true, children: [
|
|
2014
2127
|
" ",
|
|
2015
2128
|
elapsed
|
|
2016
2129
|
] }),
|
|
2017
|
-
state.status === "retrying" && /* @__PURE__ */
|
|
2130
|
+
state.status === "retrying" && /* @__PURE__ */ jsxs6(Text7, { color: theme.warning, children: [
|
|
2018
2131
|
" ",
|
|
2019
2132
|
"(attempt ",
|
|
2020
2133
|
state.attempt,
|
|
@@ -2023,11 +2136,11 @@ function PlanApp({ description, events: events2 }) {
|
|
|
2023
2136
|
")"
|
|
2024
2137
|
] })
|
|
2025
2138
|
] }),
|
|
2026
|
-
/* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2028
|
-
/* @__PURE__ */
|
|
2139
|
+
/* @__PURE__ */ jsxs6(Box7, { marginBottom: 1, children: [
|
|
2140
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " }),
|
|
2141
|
+
/* @__PURE__ */ jsx7(Text7, { children: truncate(state.description, 80) })
|
|
2029
2142
|
] }),
|
|
2030
|
-
/* @__PURE__ */
|
|
2143
|
+
/* @__PURE__ */ jsx7(
|
|
2031
2144
|
StageProgress,
|
|
2032
2145
|
{
|
|
2033
2146
|
stage: state.stage,
|
|
@@ -2038,23 +2151,23 @@ function PlanApp({ description, events: events2 }) {
|
|
|
2038
2151
|
status: state.status
|
|
2039
2152
|
}
|
|
2040
2153
|
),
|
|
2041
|
-
state.lines.length > 0 && /* @__PURE__ */
|
|
2042
|
-
state.status === "complete" && state.taskFile && /* @__PURE__ */
|
|
2043
|
-
/* @__PURE__ */
|
|
2154
|
+
state.lines.length > 0 && /* @__PURE__ */ jsx7(LogPane, { lines: state.lines, isActive, maxLines: 10 }),
|
|
2155
|
+
state.status === "complete" && state.taskFile && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
2156
|
+
/* @__PURE__ */ jsxs6(Text7, { color: theme.success, children: [
|
|
2044
2157
|
"\u2705 Task plan saved: ",
|
|
2045
2158
|
state.taskFile
|
|
2046
2159
|
] }),
|
|
2047
|
-
state.preview && /* @__PURE__ */
|
|
2048
|
-
/* @__PURE__ */
|
|
2049
|
-
/* @__PURE__ */
|
|
2160
|
+
state.preview && /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
2161
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Preview:" }),
|
|
2162
|
+
/* @__PURE__ */ jsx7(Text7, { children: state.preview })
|
|
2050
2163
|
] })
|
|
2051
2164
|
] }),
|
|
2052
|
-
state.status === "error" && /* @__PURE__ */
|
|
2165
|
+
state.status === "error" && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text7, { color: theme.error, children: [
|
|
2053
2166
|
"Error: ",
|
|
2054
2167
|
state.errorMessage
|
|
2055
2168
|
] }) }),
|
|
2056
|
-
isActive && /* @__PURE__ */
|
|
2057
|
-
isRawModeSupported && /* @__PURE__ */
|
|
2169
|
+
isActive && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "press q to quit" }) }),
|
|
2170
|
+
isRawModeSupported && /* @__PURE__ */ jsx7(KeyboardHandler, { onExit: exit })
|
|
2058
2171
|
] });
|
|
2059
2172
|
}
|
|
2060
2173
|
|
|
@@ -2555,6 +2668,33 @@ function extractJson(text) {
|
|
|
2555
2668
|
return text.slice(start, end + 1);
|
|
2556
2669
|
}
|
|
2557
2670
|
|
|
2671
|
+
// src/types.ts
|
|
2672
|
+
var InterjectChannel = class {
|
|
2673
|
+
sender = null;
|
|
2674
|
+
_queue = [];
|
|
2675
|
+
/** Called by runClaude when a Claude step starts to activate direct delivery. */
|
|
2676
|
+
register(sender) {
|
|
2677
|
+
this.sender = sender;
|
|
2678
|
+
for (const msg of this._queue) sender(msg);
|
|
2679
|
+
this._queue = [];
|
|
2680
|
+
}
|
|
2681
|
+
/** Called by runClaude when a Claude step ends. */
|
|
2682
|
+
unregister() {
|
|
2683
|
+
this.sender = null;
|
|
2684
|
+
}
|
|
2685
|
+
/** Called by the TUI. Delivers immediately if a Claude step is running, else queues. */
|
|
2686
|
+
interject(message) {
|
|
2687
|
+
if (this.sender) this.sender(message);
|
|
2688
|
+
else this._queue.push(message);
|
|
2689
|
+
}
|
|
2690
|
+
/** Drains and returns any queued messages (for non-Claude steps to consume). */
|
|
2691
|
+
consumeQueue() {
|
|
2692
|
+
const q = this._queue.slice();
|
|
2693
|
+
this._queue = [];
|
|
2694
|
+
return q;
|
|
2695
|
+
}
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2558
2698
|
// src/index.ts
|
|
2559
2699
|
var CURRENT_VERSION = JSON.parse(
|
|
2560
2700
|
readFileSync6(
|
|
@@ -2709,7 +2849,8 @@ try {
|
|
|
2709
2849
|
process.exit(1);
|
|
2710
2850
|
}
|
|
2711
2851
|
var options = { stepFilter, fromStep };
|
|
2712
|
-
var
|
|
2852
|
+
var channel = new InterjectChannel();
|
|
2853
|
+
var rawEvents = runWorkflow(workflow, options, channel);
|
|
2713
2854
|
var logger = createLogger(resolveLogDir(filePath), workflow.goal);
|
|
2714
2855
|
var events = withLogger(rawEvents, logger);
|
|
2715
2856
|
var updateCheck = checkForUpdate(CURRENT_VERSION);
|
|
@@ -2749,7 +2890,13 @@ if (ciMode) {
|
|
|
2749
2890
|
});
|
|
2750
2891
|
} else {
|
|
2751
2892
|
const inkApp = render(
|
|
2752
|
-
React3.createElement(App, {
|
|
2893
|
+
React3.createElement(App, {
|
|
2894
|
+
workflow,
|
|
2895
|
+
events,
|
|
2896
|
+
options,
|
|
2897
|
+
updateCheck,
|
|
2898
|
+
interjectChannel: channel
|
|
2899
|
+
})
|
|
2753
2900
|
);
|
|
2754
2901
|
if (workflow.selfImprove) {
|
|
2755
2902
|
inkApp.waitUntilExit().then(() => maybeRunRetrospective(filePath, workflow, logger)).catch(() => {
|