executant 1.4.3 → 1.4.5

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 CHANGED
@@ -1295,17 +1295,81 @@ async function runPass3Judge(description, workflow2) {
1295
1295
  function isNumericSequence(arr) {
1296
1296
  return arr.every((item, i) => item === String(i + 1));
1297
1297
  }
1298
+ function isLabeledSequence(arr) {
1299
+ if (arr.length < 2) return null;
1300
+ const m = arr[0].match(/^(.+\s)(\d+)$/);
1301
+ if (!m) return null;
1302
+ const prefix = m[1];
1303
+ return arr.every((item, i) => item === `${prefix}${i + 1}`) ? arr.length : null;
1304
+ }
1305
+ function parseSeqCommand(cmd) {
1306
+ const t = cmd.trim();
1307
+ const shorthand = t.match(/^seq\s+(\d+)$/);
1308
+ if (shorthand) return parseInt(shorthand[1], 10);
1309
+ const explicit = t.match(/^seq\s+1\s+(\d+)$/);
1310
+ if (explicit) return parseInt(explicit[1], 10);
1311
+ return null;
1312
+ }
1313
+ function extractCountFromName(name) {
1314
+ const m = name.match(/_of_(\d+)/);
1315
+ return m ? parseInt(m[1], 10) : null;
1316
+ }
1298
1317
  function normalizeWorkflow(workflow2) {
1299
- return {
1300
- ...workflow2,
1301
- steps: workflow2.steps.map((step) => {
1302
- if (Array.isArray(step.forEach) && isNumericSequence(step.forEach)) {
1318
+ const steps = workflow2.steps.map((step) => {
1319
+ if (Array.isArray(step.forEach)) {
1320
+ const arr = step.forEach;
1321
+ const isNumeric = isNumericSequence(arr);
1322
+ const labeledN = !isNumeric ? isLabeledSequence(arr) : null;
1323
+ if (isNumeric || labeledN !== null) {
1303
1324
  const { forEach, ...rest } = step;
1304
- return { ...rest, repeat: forEach.length };
1325
+ return { ...rest, repeat: isNumeric ? arr.length : labeledN };
1305
1326
  }
1306
- return step;
1307
- })
1308
- };
1327
+ }
1328
+ if (typeof step.forEach === "string") {
1329
+ const n = parseSeqCommand(step.forEach);
1330
+ if (n !== null) {
1331
+ const { forEach, ...rest } = step;
1332
+ return { ...rest, repeat: n };
1333
+ }
1334
+ }
1335
+ const prompt = typeof step.prompt === "string" ? step.prompt : "";
1336
+ if (prompt.includes("{{item}}") && step.forEach === void 0 && step.repeat === void 0) {
1337
+ const n = extractCountFromName(step.name);
1338
+ if (n !== null) return { ...step, repeat: n };
1339
+ }
1340
+ return step;
1341
+ });
1342
+ return { ...workflow2, steps: collapseSequentialSteps(steps) };
1343
+ }
1344
+ function collapseSequentialSteps(steps) {
1345
+ const result = [];
1346
+ let i = 0;
1347
+ while (i < steps.length) {
1348
+ const step = steps[i];
1349
+ if (step.forEach !== void 0 || step.repeat !== void 0) {
1350
+ result.push(step);
1351
+ i++;
1352
+ continue;
1353
+ }
1354
+ const m = step.name.match(/^(.+?)_1$/);
1355
+ if (!m) {
1356
+ result.push(step);
1357
+ i++;
1358
+ continue;
1359
+ }
1360
+ const prefix = m[1];
1361
+ let n = 1;
1362
+ while (i + n < steps.length && steps[i + n].name === `${prefix}_${n + 1}`) n++;
1363
+ if (n < 2) {
1364
+ result.push(step);
1365
+ i++;
1366
+ continue;
1367
+ }
1368
+ const { name, ...rest } = step;
1369
+ result.push({ ...rest, name: `${prefix}_{{item}}`, repeat: n });
1370
+ i += n;
1371
+ }
1372
+ return result;
1309
1373
  }
1310
1374
  async function* streamPlan(args) {
1311
1375
  const { description, taskFile } = args;
@@ -1371,7 +1435,7 @@ async function* streamPlan(args) {
1371
1435
  ${basePrompt}` : basePrompt,
1372
1436
  allowedTools: [],
1373
1437
  permissionMode: "bypassPermissions",
1374
- model: "opus",
1438
+ model: skipResearch ? "sonnet" : "opus",
1375
1439
  appendSystemPrompt: PLAN_SYSTEM_RULES,
1376
1440
  jsonSchema: WORKFLOW_JSON_SCHEMA
1377
1441
  };
@@ -65,6 +65,8 @@ If the user's goal mentions "N times", "repeat N", "N iterations", or "N passes"
65
65
  - Does any step use `forEach` with a sequential numeric array like `["1","2","3","4","5"]`?
66
66
  - This is always wrong — `repeat: N` must be used instead of a numeric forEach array
67
67
  - Reject and require the offending step be converted to `repeat: N`
68
+ - Does any single step's `prompt` describe doing something "N times" or "across N passes" inline, instead of using `repeat: N`? A step that says "do this 10 times" or "perform N passes" inside its prompt text rather than setting `repeat: N` is wrong — reject it and require it to be restructured as a single-pass prompt with `repeat: N` on the step
69
+ - Are there N consecutive steps with names like `step_1`, `step_2`, `step_3`? Sequential named steps are always wrong when they do the same thing — reject and require a single step with `repeat: N`
68
70
 
69
71
  ## Output Format
70
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executant",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "Harness for YAML-defined workflows that enables stepping through Claude sessions and bash commands",
5
5
  "repository": {
6
6
  "type": "git",