executant 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +216 -134
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,7 +70,9 @@ function stripPromptHeader(raw) {
|
|
|
70
70
|
return raw.replace(/^(#[^\n]*\n)+\n?/, "").trim();
|
|
71
71
|
}
|
|
72
72
|
function loadPrompt(name) {
|
|
73
|
-
return stripPromptHeader(
|
|
73
|
+
return stripPromptHeader(
|
|
74
|
+
readFileSync(join(PROMPTS_DIR, `${name}.txt`), "utf8")
|
|
75
|
+
);
|
|
74
76
|
}
|
|
75
77
|
function findOutermostBraces(text) {
|
|
76
78
|
const start = text.indexOf("{");
|
|
@@ -110,6 +112,29 @@ function formatTimestamp(d) {
|
|
|
110
112
|
function timestamp() {
|
|
111
113
|
return formatTimestamp(/* @__PURE__ */ new Date());
|
|
112
114
|
}
|
|
115
|
+
var ANSI_RE = /\x1B(?:\[[0-9;?]*[A-Za-z]|\][^\x07]*\x07)|[\r]/g;
|
|
116
|
+
function stripAnsi(s) {
|
|
117
|
+
return s.replace(ANSI_RE, "");
|
|
118
|
+
}
|
|
119
|
+
var TOOL_ARG = {
|
|
120
|
+
Read: (i) => String(i["file_path"] ?? i["path"] ?? ""),
|
|
121
|
+
Edit: (i) => String(i["file_path"] ?? ""),
|
|
122
|
+
Write: (i) => String(i["file_path"] ?? ""),
|
|
123
|
+
Bash: (i) => String(i["command"] ?? ""),
|
|
124
|
+
Glob: (i) => String(i["pattern"] ?? ""),
|
|
125
|
+
Grep: (i) => String(i["pattern"] ?? "")
|
|
126
|
+
};
|
|
127
|
+
function getToolArg(tool, input) {
|
|
128
|
+
const fn = TOOL_ARG[tool];
|
|
129
|
+
return fn ? fn(input) : JSON.stringify(input);
|
|
130
|
+
}
|
|
131
|
+
function formatToolCall(tool, input) {
|
|
132
|
+
const fn = TOOL_ARG[tool];
|
|
133
|
+
return fn ? `${tool}(${fn(input)})` : JSON.stringify({ tool, ...input });
|
|
134
|
+
}
|
|
135
|
+
function normalizeError(err) {
|
|
136
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
137
|
+
}
|
|
113
138
|
|
|
114
139
|
// src/load-workflow.ts
|
|
115
140
|
import { z } from "zod";
|
|
@@ -487,10 +512,6 @@ function buildExitError(code, plainLines) {
|
|
|
487
512
|
${plainLines.join("\n")}` : "";
|
|
488
513
|
return new Error(`claude exited with code ${code}${detail}`);
|
|
489
514
|
}
|
|
490
|
-
var ANSI_RE = /\x1B\[[0-9;]*[A-Za-z]|\x1B\][^\x07]*\x07|\r/g;
|
|
491
|
-
function stripAnsi(s) {
|
|
492
|
-
return s.replace(ANSI_RE, "");
|
|
493
|
-
}
|
|
494
515
|
function isObject(v) {
|
|
495
516
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
496
517
|
}
|
|
@@ -539,7 +560,7 @@ function shouldSkipStep(stepNumber, name, options2) {
|
|
|
539
560
|
const matchByIndex = /^\d+$/.test(options2.stepFilter) && parseInt(options2.stepFilter, 10) === stepNumber;
|
|
540
561
|
return !matchByIndex && name !== options2.stepFilter;
|
|
541
562
|
}
|
|
542
|
-
return options2.fromStep !== void 0 && stepNumber < options2.fromStep;
|
|
563
|
+
return options2.fromStep !== void 0 && stepNumber < options2.fromStep[0];
|
|
543
564
|
}
|
|
544
565
|
async function* runWorkflow(workflow2, options2 = {}) {
|
|
545
566
|
const workflowStart = Date.now();
|
|
@@ -552,8 +573,9 @@ async function* runWorkflow(workflow2, options2 = {}) {
|
|
|
552
573
|
}
|
|
553
574
|
const stepStart = Date.now();
|
|
554
575
|
yield { type: "step:start", index: i, name: task.name };
|
|
576
|
+
const from = options2.fromStep && options2.fromStep[0] === stepNumber ? options2.fromStep.slice(1) : void 0;
|
|
555
577
|
try {
|
|
556
|
-
for await (const event of runStep(task)) {
|
|
578
|
+
for await (const event of runStep(task, from)) {
|
|
557
579
|
if (event.type === "step:iteration" || event.type === "step:inner" || event.type === "output:text" || event.type === "output:tool") {
|
|
558
580
|
yield { ...event, index: i };
|
|
559
581
|
} else {
|
|
@@ -567,7 +589,7 @@ async function* runWorkflow(workflow2, options2 = {}) {
|
|
|
567
589
|
durationMs: Date.now() - stepStart
|
|
568
590
|
};
|
|
569
591
|
} catch (err) {
|
|
570
|
-
const error =
|
|
592
|
+
const error = normalizeError(err);
|
|
571
593
|
yield { type: "step:error", index: i, name: task.name, error };
|
|
572
594
|
if (!task.continueOnError) throw error;
|
|
573
595
|
}
|
|
@@ -578,7 +600,7 @@ async function* runWorkflow(workflow2, options2 = {}) {
|
|
|
578
600
|
durationMs: Date.now() - workflowStart
|
|
579
601
|
};
|
|
580
602
|
}
|
|
581
|
-
async function* runStep(task) {
|
|
603
|
+
async function* runStep(task, from) {
|
|
582
604
|
switch (task.type) {
|
|
583
605
|
case "log":
|
|
584
606
|
yield* runLog(task);
|
|
@@ -601,7 +623,7 @@ async function* runStep(task) {
|
|
|
601
623
|
break;
|
|
602
624
|
}
|
|
603
625
|
case "forEach":
|
|
604
|
-
yield* runForEach(task);
|
|
626
|
+
yield* runForEach(task, from);
|
|
605
627
|
break;
|
|
606
628
|
default: {
|
|
607
629
|
const _ = task;
|
|
@@ -612,32 +634,40 @@ async function* runStep(task) {
|
|
|
612
634
|
async function* runLog(task) {
|
|
613
635
|
yield { type: "output:text", index: -1, text: task.message };
|
|
614
636
|
}
|
|
615
|
-
async function* runForEach(task) {
|
|
637
|
+
async function* runForEach(task, from) {
|
|
616
638
|
const items = await resolveItems(task.forEach);
|
|
617
639
|
const total = items.length;
|
|
618
640
|
const innerTotal = task.inner.length;
|
|
641
|
+
const startIteration = from?.[0] ?? 1;
|
|
619
642
|
for (const [i, item] of items.entries()) {
|
|
620
|
-
|
|
643
|
+
const iteration = i + 1;
|
|
644
|
+
if (iteration < startIteration) continue;
|
|
645
|
+
yield { type: "step:iteration", index: -1, item, iteration, total };
|
|
646
|
+
const iterFrom = iteration === startIteration ? from?.slice(1) : void 0;
|
|
647
|
+
const startChild = iterFrom?.[0] ?? 1;
|
|
621
648
|
for (const [j, innerTask] of task.inner.entries()) {
|
|
649
|
+
const childIdx = j + 1;
|
|
650
|
+
if (childIdx < startChild) continue;
|
|
622
651
|
const substituted = substituteItem(innerTask, item);
|
|
623
652
|
if (innerTotal > 1) {
|
|
624
653
|
yield {
|
|
625
654
|
type: "step:inner",
|
|
626
655
|
index: -1,
|
|
627
|
-
iteration
|
|
656
|
+
iteration,
|
|
628
657
|
innerIndex: j,
|
|
629
658
|
innerTotal,
|
|
630
659
|
name: substituted.name
|
|
631
660
|
};
|
|
632
661
|
}
|
|
662
|
+
const childFrom = childIdx === startChild ? iterFrom?.slice(1) : void 0;
|
|
633
663
|
try {
|
|
634
|
-
for await (const event of runStep(substituted)) {
|
|
664
|
+
for await (const event of runStep(substituted, childFrom)) {
|
|
635
665
|
if (event.type !== "step:iteration" && event.type !== "step:inner") {
|
|
636
666
|
yield event;
|
|
637
667
|
}
|
|
638
668
|
}
|
|
639
669
|
} catch (err) {
|
|
640
|
-
const error =
|
|
670
|
+
const error = normalizeError(err);
|
|
641
671
|
if (!substituted.continueOnError) {
|
|
642
672
|
yield {
|
|
643
673
|
type: "log",
|
|
@@ -860,12 +890,6 @@ function buildJudgePrompt(stepName, instructions, output) {
|
|
|
860
890
|
OUTPUT: output
|
|
861
891
|
});
|
|
862
892
|
}
|
|
863
|
-
function formatToolCall(tool, input) {
|
|
864
|
-
if (tool === "Edit" || tool === "Write")
|
|
865
|
-
return `${tool}(${String(input["file_path"] ?? "")})`;
|
|
866
|
-
if (tool === "Bash") return `Bash(${String(input["command"] ?? "")})`;
|
|
867
|
-
return tool;
|
|
868
|
-
}
|
|
869
893
|
function buildFixSummary(toolCalls, claudeLines) {
|
|
870
894
|
if (toolCalls.length > 0) return toolCalls.join(", ");
|
|
871
895
|
return claudeLines.join(" ").trim() || "No changes made";
|
|
@@ -887,7 +911,7 @@ init_update();
|
|
|
887
911
|
|
|
888
912
|
// src/ui/App.tsx
|
|
889
913
|
import { useEffect as useEffect2, useReducer, useState } from "react";
|
|
890
|
-
import { Box as Box5, Text as Text5, useApp, useStdin } from "ink";
|
|
914
|
+
import { Box as Box5, Text as Text5, useApp, useStdin, useStdout } from "ink";
|
|
891
915
|
|
|
892
916
|
// src/ui/KeyboardHandler.tsx
|
|
893
917
|
import { useInput } from "ink";
|
|
@@ -956,10 +980,10 @@ function reducer(state, event) {
|
|
|
956
980
|
startTime: Date.now()
|
|
957
981
|
});
|
|
958
982
|
case "step:complete": {
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
)
|
|
983
|
+
const iterationHistory = finalizeIterations(
|
|
984
|
+
state.tasks[event.index]?.iterationHistory,
|
|
985
|
+
"complete"
|
|
986
|
+
);
|
|
963
987
|
return {
|
|
964
988
|
...updateTask(state, event.index, {
|
|
965
989
|
status: "complete",
|
|
@@ -970,10 +994,10 @@ function reducer(state, event) {
|
|
|
970
994
|
};
|
|
971
995
|
}
|
|
972
996
|
case "step:error": {
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
)
|
|
997
|
+
const iterationHistory = finalizeIterations(
|
|
998
|
+
state.tasks[event.index]?.iterationHistory,
|
|
999
|
+
"error"
|
|
1000
|
+
);
|
|
977
1001
|
return {
|
|
978
1002
|
...updateTask(state, event.index, {
|
|
979
1003
|
status: "error",
|
|
@@ -990,9 +1014,10 @@ function reducer(state, event) {
|
|
|
990
1014
|
currentIndex: event.index + 1
|
|
991
1015
|
};
|
|
992
1016
|
case "step:iteration": {
|
|
993
|
-
const prev = (
|
|
994
|
-
|
|
995
|
-
|
|
1017
|
+
const prev = finalizeIterations(
|
|
1018
|
+
state.tasks[event.index]?.iterationHistory,
|
|
1019
|
+
"complete"
|
|
1020
|
+
) ?? [];
|
|
996
1021
|
return updateTask(state, event.index, {
|
|
997
1022
|
iterationHistory: [
|
|
998
1023
|
...prev,
|
|
@@ -1022,13 +1047,13 @@ function reducer(state, event) {
|
|
|
1022
1047
|
case "output:text": {
|
|
1023
1048
|
const idx = event.index;
|
|
1024
1049
|
if (idx >= state.tasks.length) return state;
|
|
1025
|
-
return
|
|
1050
|
+
return appendLines(state, idx, event.text);
|
|
1026
1051
|
}
|
|
1027
1052
|
case "output:tool": {
|
|
1028
1053
|
const idx = event.index;
|
|
1029
1054
|
if (idx >= state.tasks.length) return state;
|
|
1030
1055
|
const formatted = formatToolCall2(event.tool, event.input);
|
|
1031
|
-
const next = formatted ?
|
|
1056
|
+
const next = formatted ? appendLines(state, idx, formatted) : state;
|
|
1032
1057
|
if (event.tool === "Write" && typeof event.input["file_path"] === "string") {
|
|
1033
1058
|
return {
|
|
1034
1059
|
...next,
|
|
@@ -1046,7 +1071,7 @@ function reducer(state, event) {
|
|
|
1046
1071
|
case "log": {
|
|
1047
1072
|
const idx = state.currentIndex;
|
|
1048
1073
|
if (idx >= state.tasks.length) return state;
|
|
1049
|
-
return
|
|
1074
|
+
return appendLines(state, idx, `[${event.level}] ${event.text}`);
|
|
1050
1075
|
}
|
|
1051
1076
|
default: {
|
|
1052
1077
|
const _ = event;
|
|
@@ -1055,16 +1080,28 @@ function reducer(state, event) {
|
|
|
1055
1080
|
}
|
|
1056
1081
|
}
|
|
1057
1082
|
}
|
|
1083
|
+
var MAX_LOG_LINES = 300;
|
|
1084
|
+
function normalizeLines(text) {
|
|
1085
|
+
return stripAnsi(text).split("\n");
|
|
1086
|
+
}
|
|
1087
|
+
function finalizeIterations(prev, status) {
|
|
1088
|
+
if (!prev?.length) return void 0;
|
|
1089
|
+
return prev.map(
|
|
1090
|
+
(r) => r.status === "running" ? { ...r, status, endTime: Date.now() } : r
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1058
1093
|
function updateTask(state, index, patch) {
|
|
1059
1094
|
const tasks = state.tasks.map(
|
|
1060
1095
|
(t, i) => i === index ? { ...t, ...patch } : t
|
|
1061
1096
|
);
|
|
1062
1097
|
return { ...state, tasks };
|
|
1063
1098
|
}
|
|
1064
|
-
function
|
|
1099
|
+
function appendLines(state, index, text) {
|
|
1100
|
+
const newLines = normalizeLines(text);
|
|
1065
1101
|
const tasks = state.tasks.map((t, i) => {
|
|
1066
1102
|
if (i !== index) return t;
|
|
1067
|
-
const
|
|
1103
|
+
const combined = [...t.lines, ...newLines];
|
|
1104
|
+
const lines = combined.length > MAX_LOG_LINES ? combined.slice(-MAX_LOG_LINES) : combined;
|
|
1068
1105
|
return { ...t, lines };
|
|
1069
1106
|
});
|
|
1070
1107
|
return { ...state, tasks };
|
|
@@ -1114,6 +1151,9 @@ var STATUS_COLOR = {
|
|
|
1114
1151
|
error: theme.error,
|
|
1115
1152
|
pending: theme.muted
|
|
1116
1153
|
};
|
|
1154
|
+
function statusIcon(status, tick) {
|
|
1155
|
+
return status === "running" ? SPINNER[tick % SPINNER.length] : STATUS_ICON[status] ?? "\xB7";
|
|
1156
|
+
}
|
|
1117
1157
|
var EXIT_DELAY_MS = 300;
|
|
1118
1158
|
function formatHeaderElapsed(start, end) {
|
|
1119
1159
|
const ms = (end ?? Date.now()) - start;
|
|
@@ -1148,9 +1188,6 @@ function TaskRow({ taskState, isActive, index, tick }) {
|
|
|
1148
1188
|
] })
|
|
1149
1189
|
] });
|
|
1150
1190
|
}
|
|
1151
|
-
function statusIcon(status, tick) {
|
|
1152
|
-
return status === "running" ? SPINNER[tick % SPINNER.length] : STATUS_ICON[status] ?? "\xB7";
|
|
1153
|
-
}
|
|
1154
1191
|
function statusColor(status, isActive) {
|
|
1155
1192
|
if (isActive && status === "running") return theme.primary;
|
|
1156
1193
|
return STATUS_COLOR[status] ?? theme.foreground;
|
|
@@ -1167,7 +1204,7 @@ function formatIterCount(history) {
|
|
|
1167
1204
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
1168
1205
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1169
1206
|
function IterationRow({ record, tick }) {
|
|
1170
|
-
const icon = record.status
|
|
1207
|
+
const icon = statusIcon(record.status, tick);
|
|
1171
1208
|
const color = STATUS_COLOR[record.status] ?? theme.primary;
|
|
1172
1209
|
const innerText = record.inner ? ` \u2014 ${stripItem(record.inner.name, record.item)} [${record.inner.index + 1}/${record.inner.total}]` : "";
|
|
1173
1210
|
const ms = (record.endTime ?? Date.now()) - record.startTime;
|
|
@@ -1222,14 +1259,24 @@ function LogPane({ lines, isActive = false, maxLines = 15 }) {
|
|
|
1222
1259
|
if (visible.length === 0) {
|
|
1223
1260
|
return /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: isActive ? "\u2838 waiting for output\u2026" : "\u2014 no output yet \u2014" }) });
|
|
1224
1261
|
}
|
|
1225
|
-
return /* @__PURE__ */ jsx3(
|
|
1226
|
-
|
|
1262
|
+
return /* @__PURE__ */ jsx3(
|
|
1263
|
+
Box3,
|
|
1227
1264
|
{
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1265
|
+
flexDirection: "column",
|
|
1266
|
+
marginTop: 1,
|
|
1267
|
+
borderStyle: "single",
|
|
1268
|
+
borderColor: theme.border,
|
|
1269
|
+
paddingX: 1,
|
|
1270
|
+
children: visible.map((line, i) => /* @__PURE__ */ jsx3(
|
|
1271
|
+
LogLine,
|
|
1272
|
+
{
|
|
1273
|
+
text: line,
|
|
1274
|
+
cursor: isActive && i === visible.length - 1
|
|
1275
|
+
},
|
|
1276
|
+
i
|
|
1277
|
+
))
|
|
1278
|
+
}
|
|
1279
|
+
);
|
|
1233
1280
|
}
|
|
1234
1281
|
function LogLine({ text, cursor }) {
|
|
1235
1282
|
const suffix = cursor ? /* @__PURE__ */ jsx3(Text3, { color: theme.primary, children: " \u258C" }) : null;
|
|
@@ -1242,18 +1289,41 @@ function LogLine({ text, cursor }) {
|
|
|
1242
1289
|
suffix
|
|
1243
1290
|
] });
|
|
1244
1291
|
}
|
|
1245
|
-
if (/^\s*\$\s/.test(text))
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1292
|
+
if (/^\s*\$\s/.test(text))
|
|
1293
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
|
|
1294
|
+
text,
|
|
1295
|
+
suffix
|
|
1296
|
+
] });
|
|
1297
|
+
if (text.startsWith("[warn]"))
|
|
1298
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
|
|
1299
|
+
text,
|
|
1300
|
+
suffix
|
|
1301
|
+
] });
|
|
1302
|
+
if (text.startsWith("[error]"))
|
|
1303
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
|
|
1304
|
+
text,
|
|
1305
|
+
suffix
|
|
1306
|
+
] });
|
|
1307
|
+
if (/^[\s]*(✓|✔|✅|done|success|compiled|built|passed)/i.test(text) && !/\b(error|fail|failed|warn|warning)\b/i.test(text))
|
|
1308
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.success, children: [
|
|
1309
|
+
text,
|
|
1310
|
+
suffix
|
|
1311
|
+
] });
|
|
1312
|
+
if (/\b(error|failed|fail)\b/i.test(text))
|
|
1313
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
|
|
1314
|
+
text,
|
|
1315
|
+
suffix
|
|
1316
|
+
] });
|
|
1317
|
+
if (/\b(warn|warning)\b/i.test(text))
|
|
1318
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
|
|
1319
|
+
text,
|
|
1320
|
+
suffix
|
|
1321
|
+
] });
|
|
1322
|
+
if (/^[·…⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/.test(text))
|
|
1323
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: theme.muted, children: [
|
|
1324
|
+
text,
|
|
1325
|
+
suffix
|
|
1326
|
+
] });
|
|
1257
1327
|
return /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1258
1328
|
text,
|
|
1259
1329
|
suffix
|
|
@@ -1324,6 +1394,13 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1324
1394
|
};
|
|
1325
1395
|
}, [events2, exit]);
|
|
1326
1396
|
const { isRawModeSupported } = useStdin();
|
|
1397
|
+
const { stdout } = useStdout();
|
|
1398
|
+
const terminalRows = stdout?.rows ?? 24;
|
|
1399
|
+
const FIXED_OVERHEAD = 12;
|
|
1400
|
+
const logPaneMaxLines = Math.max(
|
|
1401
|
+
5,
|
|
1402
|
+
terminalRows - FIXED_OVERHEAD - state.tasks.length
|
|
1403
|
+
);
|
|
1327
1404
|
const [tick, setTick] = useState(0);
|
|
1328
1405
|
useInterval(() => {
|
|
1329
1406
|
if (!state.endTime) setTick((t) => t + 1);
|
|
@@ -1376,7 +1453,8 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
|
|
|
1376
1453
|
LogPane,
|
|
1377
1454
|
{
|
|
1378
1455
|
lines: activeTask.lines,
|
|
1379
|
-
isActive: activeTask.status === "running"
|
|
1456
|
+
isActive: activeTask.status === "running",
|
|
1457
|
+
maxLines: logPaneMaxLines
|
|
1380
1458
|
}
|
|
1381
1459
|
),
|
|
1382
1460
|
state.endTime !== void 0 && state.writtenFiles.length > 0 && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
@@ -2005,17 +2083,6 @@ var INIT_STATE = {
|
|
|
2005
2083
|
judgeAttempt: 0,
|
|
2006
2084
|
recentOutput: []
|
|
2007
2085
|
};
|
|
2008
|
-
var TOOL_SUMMARY = {
|
|
2009
|
-
Read: (i) => String(i["file_path"] ?? i["path"] ?? ""),
|
|
2010
|
-
Edit: (i) => String(i["file_path"] ?? ""),
|
|
2011
|
-
Write: (i) => String(i["file_path"] ?? ""),
|
|
2012
|
-
Bash: (i) => String(i["command"] ?? ""),
|
|
2013
|
-
Glob: (i) => String(i["pattern"] ?? ""),
|
|
2014
|
-
Grep: (i) => String(i["pattern"] ?? "")
|
|
2015
|
-
};
|
|
2016
|
-
function toolSummary(tool, input) {
|
|
2017
|
-
return (TOOL_SUMMARY[tool] ?? ((i) => JSON.stringify(i)))(input);
|
|
2018
|
-
}
|
|
2019
2086
|
function appendLog(logFile, text) {
|
|
2020
2087
|
if (logFile) appendFileSync(logFile, text + "\n");
|
|
2021
2088
|
}
|
|
@@ -2087,21 +2154,21 @@ Step failed: ${error.message}
|
|
|
2087
2154
|
finalizeComplexSequence(s);
|
|
2088
2155
|
return s;
|
|
2089
2156
|
}
|
|
2090
|
-
function
|
|
2157
|
+
function buildHighlightHeader(ctx, s, title, extra = []) {
|
|
2091
2158
|
return [
|
|
2092
|
-
|
|
2159
|
+
`# ${title}`,
|
|
2093
2160
|
"",
|
|
2094
2161
|
`**Task:** ${ctx.slug}`,
|
|
2095
2162
|
`**Step:** ${s.stepName}`,
|
|
2163
|
+
...extra,
|
|
2096
2164
|
`**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
2097
2165
|
"",
|
|
2098
2166
|
"---",
|
|
2099
|
-
"",
|
|
2100
|
-
"## Claude's Tool Orchestration",
|
|
2101
|
-
"",
|
|
2102
|
-
"Claude used multiple tools to complete this step:",
|
|
2103
2167
|
""
|
|
2104
|
-
].join("\n");
|
|
2168
|
+
].join("\n") + "\n";
|
|
2169
|
+
}
|
|
2170
|
+
function complexSequenceHeader(ctx, s) {
|
|
2171
|
+
return buildHighlightHeader(ctx, s, "Complex Tool Sequence") + "## Claude's Tool Orchestration\n\nClaude used multiple tools to complete this step:\n\n";
|
|
2105
2172
|
}
|
|
2106
2173
|
function createComplexSequenceFile(ctx, s) {
|
|
2107
2174
|
const path = highlightPath(ctx, s.stepIndex, "complex_sequence");
|
|
@@ -2109,7 +2176,7 @@ function createComplexSequenceFile(ctx, s) {
|
|
|
2109
2176
|
return path;
|
|
2110
2177
|
}
|
|
2111
2178
|
function onTool(ctx, s, tool, input) {
|
|
2112
|
-
const desc =
|
|
2179
|
+
const desc = getToolArg(tool, input);
|
|
2113
2180
|
appendLog(s.logFile, ` [${tool}] ${desc}`);
|
|
2114
2181
|
const toolCount = s.toolCount + 1;
|
|
2115
2182
|
const complexSequenceFile = toolCount === 3 ? createComplexSequenceFile(ctx, s) : s.complexSequenceFile;
|
|
@@ -2125,23 +2192,9 @@ function onTool(ctx, s, tool, input) {
|
|
|
2125
2192
|
function saveJudgeHighlight(ctx, s, verdict, text) {
|
|
2126
2193
|
writeFileSync3(
|
|
2127
2194
|
highlightPath(ctx, s.stepIndex, `judge_${verdict}`),
|
|
2128
|
-
[
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
`**Task:** ${ctx.slug}`,
|
|
2132
|
-
`**Step:** ${s.stepName}`,
|
|
2133
|
-
`**Attempt:** ${s.judgeAttempt}`,
|
|
2134
|
-
`**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
2135
|
-
"",
|
|
2136
|
-
"---",
|
|
2137
|
-
"",
|
|
2138
|
-
text,
|
|
2139
|
-
"",
|
|
2140
|
-
"---",
|
|
2141
|
-
"",
|
|
2142
|
-
"*Auto-captured*",
|
|
2143
|
-
""
|
|
2144
|
-
].join("\n")
|
|
2195
|
+
buildHighlightHeader(ctx, s, `Judge Verdict: ${verdict}`, [
|
|
2196
|
+
`**Attempt:** ${s.judgeAttempt}`
|
|
2197
|
+
]) + [text, "", "---", "", "*Auto-captured*", ""].join("\n")
|
|
2145
2198
|
);
|
|
2146
2199
|
}
|
|
2147
2200
|
var LOG_MATCHERS = [
|
|
@@ -2156,19 +2209,11 @@ var LOG_MATCHERS = [
|
|
|
2156
2209
|
},
|
|
2157
2210
|
{
|
|
2158
2211
|
pattern: /\[self-healing\].*failed.*exit\s+(\d+)/i,
|
|
2159
|
-
apply: (ctx, s,
|
|
2212
|
+
apply: (ctx, s, _text, match) => {
|
|
2160
2213
|
const selfHealingFile = highlightPath(ctx, s.stepIndex, "self_healing");
|
|
2161
2214
|
writeFileSync3(
|
|
2162
2215
|
selfHealingFile,
|
|
2163
|
-
[
|
|
2164
|
-
"# Self-Healing Activation",
|
|
2165
|
-
"",
|
|
2166
|
-
`**Task:** ${ctx.slug}`,
|
|
2167
|
-
`**Step:** ${s.stepName}`,
|
|
2168
|
-
`**Timestamp:** ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
2169
|
-
"",
|
|
2170
|
-
"---",
|
|
2171
|
-
"",
|
|
2216
|
+
buildHighlightHeader(ctx, s, "Self-Healing Activation") + [
|
|
2172
2217
|
"## \u274C Failure Detected",
|
|
2173
2218
|
"",
|
|
2174
2219
|
`**Exit Code:** ${match[1]}`,
|
|
@@ -2215,14 +2260,15 @@ var LOG_MATCHERS = [
|
|
|
2215
2260
|
];
|
|
2216
2261
|
function onLogMessage(ctx, s, level, text) {
|
|
2217
2262
|
appendLog(s.logFile, `[${level}] ${text}`);
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2263
|
+
let state = s;
|
|
2264
|
+
for (const { pattern, apply } of LOG_MATCHERS) {
|
|
2265
|
+
const m = pattern.exec(text);
|
|
2266
|
+
if (m) {
|
|
2267
|
+
state = apply(ctx, state, text, m);
|
|
2268
|
+
break;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
return state;
|
|
2226
2272
|
}
|
|
2227
2273
|
function onWorkflowComplete(ctx, s) {
|
|
2228
2274
|
appendLog(
|
|
@@ -2330,7 +2376,13 @@ async function* withLogger(gen, logger2) {
|
|
|
2330
2376
|
}
|
|
2331
2377
|
|
|
2332
2378
|
// src/retrospective.ts
|
|
2333
|
-
import {
|
|
2379
|
+
import {
|
|
2380
|
+
existsSync as existsSync3,
|
|
2381
|
+
mkdirSync as mkdirSync4,
|
|
2382
|
+
readdirSync as readdirSync2,
|
|
2383
|
+
readFileSync as readFileSync5,
|
|
2384
|
+
writeFileSync as writeFileSync4
|
|
2385
|
+
} from "node:fs";
|
|
2334
2386
|
import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve3 } from "node:path";
|
|
2335
2387
|
import { spawnSync } from "node:child_process";
|
|
2336
2388
|
import { load as parseYaml2 } from "js-yaml";
|
|
@@ -2342,10 +2394,17 @@ var RetrospectiveOutputSchema = z4.object({
|
|
|
2342
2394
|
var RETROSPECTIVE_PROMPT = loadPrompt("retrospective-analysis");
|
|
2343
2395
|
async function runRetrospective(workflowFilePath, workflow2, highlightsDir, runTimestamp) {
|
|
2344
2396
|
try {
|
|
2345
|
-
await doRetrospective(
|
|
2397
|
+
await doRetrospective(
|
|
2398
|
+
workflowFilePath,
|
|
2399
|
+
workflow2,
|
|
2400
|
+
highlightsDir,
|
|
2401
|
+
runTimestamp
|
|
2402
|
+
);
|
|
2346
2403
|
} catch (err) {
|
|
2347
|
-
console.warn(
|
|
2348
|
-
|
|
2404
|
+
console.warn(
|
|
2405
|
+
`
|
|
2406
|
+
Self-improvement: retrospective failed: ${getErrorMessage(err)}`
|
|
2407
|
+
);
|
|
2349
2408
|
}
|
|
2350
2409
|
}
|
|
2351
2410
|
async function doRetrospective(workflowFilePath, workflow2, highlightsDir, runTimestamp) {
|
|
@@ -2356,13 +2415,17 @@ async function doRetrospective(workflowFilePath, workflow2, highlightsDir, runTi
|
|
|
2356
2415
|
const allFiles = readdirSync2(highlightsDir);
|
|
2357
2416
|
const runHighlights = allFiles.filter((f) => f.startsWith(runTimestamp) && f.endsWith(".md")).sort();
|
|
2358
2417
|
if (runHighlights.length === 0) {
|
|
2359
|
-
console.log(
|
|
2418
|
+
console.log(
|
|
2419
|
+
"\nSelf-improvement: no highlights for this run \u2014 task completed without issues, skipping."
|
|
2420
|
+
);
|
|
2360
2421
|
return;
|
|
2361
2422
|
}
|
|
2362
2423
|
const divider = "\u2501".repeat(51);
|
|
2363
2424
|
console.log(`
|
|
2364
2425
|
${divider}`);
|
|
2365
|
-
console.log(
|
|
2426
|
+
console.log(
|
|
2427
|
+
"Self-Improvement: Analyzing execution and generating improvements..."
|
|
2428
|
+
);
|
|
2366
2429
|
console.log(`${divider}
|
|
2367
2430
|
`);
|
|
2368
2431
|
console.log(`Found ${runHighlights.length} highlight(s) to analyze`);
|
|
@@ -2408,15 +2471,23 @@ ${content}`;
|
|
|
2408
2471
|
"--output-format",
|
|
2409
2472
|
"text"
|
|
2410
2473
|
],
|
|
2411
|
-
{
|
|
2474
|
+
{
|
|
2475
|
+
encoding: "utf8",
|
|
2476
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
2477
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2478
|
+
}
|
|
2412
2479
|
);
|
|
2413
2480
|
if (result.error) {
|
|
2414
|
-
console.warn(
|
|
2481
|
+
console.warn(
|
|
2482
|
+
`Self-improvement: failed to run claude: ${result.error.message}`
|
|
2483
|
+
);
|
|
2415
2484
|
return;
|
|
2416
2485
|
}
|
|
2417
2486
|
if (result.status !== 0) {
|
|
2418
2487
|
const stderr = result.stderr ?? "";
|
|
2419
|
-
console.warn(
|
|
2488
|
+
console.warn(
|
|
2489
|
+
`Self-improvement: claude exited with code ${result.status}${stderr ? ": " + stderr : ""}`
|
|
2490
|
+
);
|
|
2420
2491
|
return;
|
|
2421
2492
|
}
|
|
2422
2493
|
const response = result.stdout ?? "";
|
|
@@ -2424,13 +2495,17 @@ ${content}`;
|
|
|
2424
2495
|
try {
|
|
2425
2496
|
parsed = JSON.parse(extractJson(response));
|
|
2426
2497
|
} catch {
|
|
2427
|
-
console.warn(
|
|
2428
|
-
|
|
2498
|
+
console.warn(
|
|
2499
|
+
`Self-improvement: could not parse Claude response as JSON.
|
|
2500
|
+
Response: ${response.trim()}`
|
|
2501
|
+
);
|
|
2429
2502
|
return;
|
|
2430
2503
|
}
|
|
2431
2504
|
const zodResult = RetrospectiveOutputSchema.safeParse(parsed);
|
|
2432
2505
|
if (!zodResult.success) {
|
|
2433
|
-
console.warn(
|
|
2506
|
+
console.warn(
|
|
2507
|
+
"Self-improvement: response schema mismatch \u2014 improved YAML not saved."
|
|
2508
|
+
);
|
|
2434
2509
|
return;
|
|
2435
2510
|
}
|
|
2436
2511
|
const improvedYaml = zodResult.data.improved_yaml.trim();
|
|
@@ -2438,7 +2513,9 @@ Response: ${response.trim()}`);
|
|
|
2438
2513
|
try {
|
|
2439
2514
|
parseYaml2(improvedYaml);
|
|
2440
2515
|
} catch (err) {
|
|
2441
|
-
console.warn(
|
|
2516
|
+
console.warn(
|
|
2517
|
+
`Self-improvement: generated YAML is invalid (${getErrorMessage(err)}), skipping save.`
|
|
2518
|
+
);
|
|
2442
2519
|
return;
|
|
2443
2520
|
}
|
|
2444
2521
|
const startDir = dirname4(resolve3(workflowFilePath));
|
|
@@ -2463,7 +2540,8 @@ ${divider}`);
|
|
|
2463
2540
|
function extractJson(text) {
|
|
2464
2541
|
const start = text.indexOf("{");
|
|
2465
2542
|
const end = text.lastIndexOf("}");
|
|
2466
|
-
if (start === -1 || end === -1 || end <= start)
|
|
2543
|
+
if (start === -1 || end === -1 || end <= start)
|
|
2544
|
+
throw new Error("no JSON object found in response");
|
|
2467
2545
|
return text.slice(start, end + 1);
|
|
2468
2546
|
}
|
|
2469
2547
|
|
|
@@ -2513,7 +2591,7 @@ Version: ${CURRENT_VERSION}
|
|
|
2513
2591
|
Options:
|
|
2514
2592
|
--ci Headless mode \u2014 print events as NDJSON, no TUI
|
|
2515
2593
|
--step <name|index> Run only the named step or step at 1-based index
|
|
2516
|
-
--from-step <n>
|
|
2594
|
+
--from-step <n> Resume from step n (e.g. 3, 3.2, 2.5.4.3 \u2014 1-based path)
|
|
2517
2595
|
--help, -h Show this help
|
|
2518
2596
|
|
|
2519
2597
|
Commands:
|
|
@@ -2595,11 +2673,15 @@ for (let i = 0; i < rawArgs.length; i++) {
|
|
|
2595
2673
|
console.error("--from-step requires a value");
|
|
2596
2674
|
process.exit(1);
|
|
2597
2675
|
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2676
|
+
const raw = rawArgs[++i];
|
|
2677
|
+
const parts = raw.split(".").map(Number);
|
|
2678
|
+
if (parts.some(Number.isNaN) || parts.some((p) => p < 1)) {
|
|
2679
|
+
console.error(
|
|
2680
|
+
"--from-step must be N or N.M.K... (all 1-based, e.g. 3 or 3.2 or 2.5.4.3)"
|
|
2681
|
+
);
|
|
2601
2682
|
process.exit(1);
|
|
2602
2683
|
}
|
|
2684
|
+
fromStep = parts;
|
|
2603
2685
|
} else {
|
|
2604
2686
|
positional.push(a);
|
|
2605
2687
|
}
|