executant 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +121 -38
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -887,7 +887,7 @@ init_update();
887
887
 
888
888
  // src/ui/App.tsx
889
889
  import { useEffect as useEffect2, useReducer, useState } from "react";
890
- import { Box as Box5, Text as Text5, useApp, useStdin } from "ink";
890
+ import { Box as Box5, Text as Text5, useApp, useStdin, useStdout } from "ink";
891
891
 
892
892
  // src/ui/KeyboardHandler.tsx
893
893
  import { useInput } from "ink";
@@ -1022,7 +1022,7 @@ function reducer(state, event) {
1022
1022
  case "output:text": {
1023
1023
  const idx = event.index;
1024
1024
  if (idx >= state.tasks.length) return state;
1025
- return appendLine(state, idx, event.text);
1025
+ return appendLines(state, idx, event.text);
1026
1026
  }
1027
1027
  case "output:tool": {
1028
1028
  const idx = event.index;
@@ -1046,7 +1046,7 @@ function reducer(state, event) {
1046
1046
  case "log": {
1047
1047
  const idx = state.currentIndex;
1048
1048
  if (idx >= state.tasks.length) return state;
1049
- return appendLine(state, idx, `[${event.level}] ${event.text}`);
1049
+ return appendLines(state, idx, `[${event.level}] ${event.text}`);
1050
1050
  }
1051
1051
  default: {
1052
1052
  const _ = event;
@@ -1055,6 +1055,11 @@ function reducer(state, event) {
1055
1055
  }
1056
1056
  }
1057
1057
  }
1058
+ var ANSI_RE2 = /\x1B(?:\[[0-9;?]*[A-Za-z]|\][^\x07]*\x07)|[\r]/g;
1059
+ var MAX_LOG_LINES = 300;
1060
+ function normalizeLines(text) {
1061
+ return text.replace(ANSI_RE2, "").split("\n");
1062
+ }
1058
1063
  function updateTask(state, index, patch) {
1059
1064
  const tasks = state.tasks.map(
1060
1065
  (t, i) => i === index ? { ...t, ...patch } : t
@@ -1062,9 +1067,14 @@ function updateTask(state, index, patch) {
1062
1067
  return { ...state, tasks };
1063
1068
  }
1064
1069
  function appendLine(state, index, line) {
1070
+ return appendLines(state, index, line);
1071
+ }
1072
+ function appendLines(state, index, text) {
1073
+ const newLines = normalizeLines(text);
1065
1074
  const tasks = state.tasks.map((t, i) => {
1066
1075
  if (i !== index) return t;
1067
- const lines = [...t.lines, line];
1076
+ const combined = [...t.lines, ...newLines];
1077
+ const lines = combined.length > MAX_LOG_LINES ? combined.slice(-MAX_LOG_LINES) : combined;
1068
1078
  return { ...t, lines };
1069
1079
  });
1070
1080
  return { ...state, tasks };
@@ -1222,14 +1232,24 @@ function LogPane({ lines, isActive = false, maxLines = 15 }) {
1222
1232
  if (visible.length === 0) {
1223
1233
  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
1234
  }
1225
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: theme.border, paddingX: 1, children: visible.map((line, i) => /* @__PURE__ */ jsx3(
1226
- LogLine,
1235
+ return /* @__PURE__ */ jsx3(
1236
+ Box3,
1227
1237
  {
1228
- text: line,
1229
- cursor: isActive && i === visible.length - 1
1230
- },
1231
- i
1232
- )) });
1238
+ flexDirection: "column",
1239
+ marginTop: 1,
1240
+ borderStyle: "single",
1241
+ borderColor: theme.border,
1242
+ paddingX: 1,
1243
+ children: visible.map((line, i) => /* @__PURE__ */ jsx3(
1244
+ LogLine,
1245
+ {
1246
+ text: line,
1247
+ cursor: isActive && i === visible.length - 1
1248
+ },
1249
+ i
1250
+ ))
1251
+ }
1252
+ );
1233
1253
  }
1234
1254
  function LogLine({ text, cursor }) {
1235
1255
  const suffix = cursor ? /* @__PURE__ */ jsx3(Text3, { color: theme.primary, children: " \u258C" }) : null;
@@ -1242,18 +1262,41 @@ function LogLine({ text, cursor }) {
1242
1262
  suffix
1243
1263
  ] });
1244
1264
  }
1245
- if (/^\s*\$\s/.test(text)) return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
1246
- text,
1247
- suffix
1248
- ] });
1249
- if (text.startsWith("[warn]")) return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
1250
- text,
1251
- suffix
1252
- ] });
1253
- if (text.startsWith("[error]")) return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
1254
- text,
1255
- suffix
1256
- ] });
1265
+ if (/^\s*\$\s/.test(text))
1266
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
1267
+ text,
1268
+ suffix
1269
+ ] });
1270
+ if (text.startsWith("[warn]"))
1271
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
1272
+ text,
1273
+ suffix
1274
+ ] });
1275
+ if (text.startsWith("[error]"))
1276
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
1277
+ text,
1278
+ suffix
1279
+ ] });
1280
+ if (/^[\s]*(✓|✔|✅|done|success|compiled|built|passed)/i.test(text) && !/\b(error|fail|failed|warn|warning)\b/i.test(text))
1281
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.success, children: [
1282
+ text,
1283
+ suffix
1284
+ ] });
1285
+ if (/\b(error|failed|fail)\b/i.test(text))
1286
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
1287
+ text,
1288
+ suffix
1289
+ ] });
1290
+ if (/\b(warn|warning)\b/i.test(text))
1291
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.warning, children: [
1292
+ text,
1293
+ suffix
1294
+ ] });
1295
+ if (/^[·…⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/.test(text))
1296
+ return /* @__PURE__ */ jsxs3(Text3, { color: theme.muted, children: [
1297
+ text,
1298
+ suffix
1299
+ ] });
1257
1300
  return /* @__PURE__ */ jsxs3(Text3, { children: [
1258
1301
  text,
1259
1302
  suffix
@@ -1324,6 +1367,13 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
1324
1367
  };
1325
1368
  }, [events2, exit]);
1326
1369
  const { isRawModeSupported } = useStdin();
1370
+ const { stdout } = useStdout();
1371
+ const terminalRows = stdout?.rows ?? 24;
1372
+ const FIXED_OVERHEAD = 12;
1373
+ const logPaneMaxLines = Math.max(
1374
+ 5,
1375
+ terminalRows - FIXED_OVERHEAD - state.tasks.length
1376
+ );
1327
1377
  const [tick, setTick] = useState(0);
1328
1378
  useInterval(() => {
1329
1379
  if (!state.endTime) setTick((t) => t + 1);
@@ -1376,7 +1426,8 @@ function App({ workflow: workflow2, events: events2, options: options2, updateCh
1376
1426
  LogPane,
1377
1427
  {
1378
1428
  lines: activeTask.lines,
1379
- isActive: activeTask.status === "running"
1429
+ isActive: activeTask.status === "running",
1430
+ maxLines: logPaneMaxLines
1380
1431
  }
1381
1432
  ),
1382
1433
  state.endTime !== void 0 && state.writtenFiles.length > 0 && /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", marginTop: 1, children: [
@@ -2330,7 +2381,13 @@ async function* withLogger(gen, logger2) {
2330
2381
  }
2331
2382
 
2332
2383
  // src/retrospective.ts
2333
- import { existsSync as existsSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
2384
+ import {
2385
+ existsSync as existsSync3,
2386
+ mkdirSync as mkdirSync4,
2387
+ readdirSync as readdirSync2,
2388
+ readFileSync as readFileSync5,
2389
+ writeFileSync as writeFileSync4
2390
+ } from "node:fs";
2334
2391
  import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve3 } from "node:path";
2335
2392
  import { spawnSync } from "node:child_process";
2336
2393
  import { load as parseYaml2 } from "js-yaml";
@@ -2342,10 +2399,17 @@ var RetrospectiveOutputSchema = z4.object({
2342
2399
  var RETROSPECTIVE_PROMPT = loadPrompt("retrospective-analysis");
2343
2400
  async function runRetrospective(workflowFilePath, workflow2, highlightsDir, runTimestamp) {
2344
2401
  try {
2345
- await doRetrospective(workflowFilePath, workflow2, highlightsDir, runTimestamp);
2402
+ await doRetrospective(
2403
+ workflowFilePath,
2404
+ workflow2,
2405
+ highlightsDir,
2406
+ runTimestamp
2407
+ );
2346
2408
  } catch (err) {
2347
- console.warn(`
2348
- Self-improvement: retrospective failed: ${getErrorMessage(err)}`);
2409
+ console.warn(
2410
+ `
2411
+ Self-improvement: retrospective failed: ${getErrorMessage(err)}`
2412
+ );
2349
2413
  }
2350
2414
  }
2351
2415
  async function doRetrospective(workflowFilePath, workflow2, highlightsDir, runTimestamp) {
@@ -2356,13 +2420,17 @@ async function doRetrospective(workflowFilePath, workflow2, highlightsDir, runTi
2356
2420
  const allFiles = readdirSync2(highlightsDir);
2357
2421
  const runHighlights = allFiles.filter((f) => f.startsWith(runTimestamp) && f.endsWith(".md")).sort();
2358
2422
  if (runHighlights.length === 0) {
2359
- console.log("\nSelf-improvement: no highlights for this run \u2014 task completed without issues, skipping.");
2423
+ console.log(
2424
+ "\nSelf-improvement: no highlights for this run \u2014 task completed without issues, skipping."
2425
+ );
2360
2426
  return;
2361
2427
  }
2362
2428
  const divider = "\u2501".repeat(51);
2363
2429
  console.log(`
2364
2430
  ${divider}`);
2365
- console.log("Self-Improvement: Analyzing execution and generating improvements...");
2431
+ console.log(
2432
+ "Self-Improvement: Analyzing execution and generating improvements..."
2433
+ );
2366
2434
  console.log(`${divider}
2367
2435
  `);
2368
2436
  console.log(`Found ${runHighlights.length} highlight(s) to analyze`);
@@ -2408,15 +2476,23 @@ ${content}`;
2408
2476
  "--output-format",
2409
2477
  "text"
2410
2478
  ],
2411
- { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 }
2479
+ {
2480
+ encoding: "utf8",
2481
+ maxBuffer: 10 * 1024 * 1024,
2482
+ stdio: ["ignore", "pipe", "pipe"]
2483
+ }
2412
2484
  );
2413
2485
  if (result.error) {
2414
- console.warn(`Self-improvement: failed to run claude: ${result.error.message}`);
2486
+ console.warn(
2487
+ `Self-improvement: failed to run claude: ${result.error.message}`
2488
+ );
2415
2489
  return;
2416
2490
  }
2417
2491
  if (result.status !== 0) {
2418
2492
  const stderr = result.stderr ?? "";
2419
- console.warn(`Self-improvement: claude exited with code ${result.status}${stderr ? ": " + stderr : ""}`);
2493
+ console.warn(
2494
+ `Self-improvement: claude exited with code ${result.status}${stderr ? ": " + stderr : ""}`
2495
+ );
2420
2496
  return;
2421
2497
  }
2422
2498
  const response = result.stdout ?? "";
@@ -2424,13 +2500,17 @@ ${content}`;
2424
2500
  try {
2425
2501
  parsed = JSON.parse(extractJson(response));
2426
2502
  } catch {
2427
- console.warn(`Self-improvement: could not parse Claude response as JSON.
2428
- Response: ${response.trim()}`);
2503
+ console.warn(
2504
+ `Self-improvement: could not parse Claude response as JSON.
2505
+ Response: ${response.trim()}`
2506
+ );
2429
2507
  return;
2430
2508
  }
2431
2509
  const zodResult = RetrospectiveOutputSchema.safeParse(parsed);
2432
2510
  if (!zodResult.success) {
2433
- console.warn("Self-improvement: response schema mismatch \u2014 improved YAML not saved.");
2511
+ console.warn(
2512
+ "Self-improvement: response schema mismatch \u2014 improved YAML not saved."
2513
+ );
2434
2514
  return;
2435
2515
  }
2436
2516
  const improvedYaml = zodResult.data.improved_yaml.trim();
@@ -2438,7 +2518,9 @@ Response: ${response.trim()}`);
2438
2518
  try {
2439
2519
  parseYaml2(improvedYaml);
2440
2520
  } catch (err) {
2441
- console.warn(`Self-improvement: generated YAML is invalid (${getErrorMessage(err)}), skipping save.`);
2521
+ console.warn(
2522
+ `Self-improvement: generated YAML is invalid (${getErrorMessage(err)}), skipping save.`
2523
+ );
2442
2524
  return;
2443
2525
  }
2444
2526
  const startDir = dirname4(resolve3(workflowFilePath));
@@ -2463,7 +2545,8 @@ ${divider}`);
2463
2545
  function extractJson(text) {
2464
2546
  const start = text.indexOf("{");
2465
2547
  const end = text.lastIndexOf("}");
2466
- if (start === -1 || end === -1 || end <= start) throw new Error("no JSON object found in response");
2548
+ if (start === -1 || end === -1 || end <= start)
2549
+ throw new Error("no JSON object found in response");
2467
2550
  return text.slice(start, end + 1);
2468
2551
  }
2469
2552
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executant",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "Harness for YAML-defined workflows that enables stepping through Claude sessions and bash commands",
5
5
  "repository": {
6
6
  "type": "git",