executant 1.4.2 → 1.4.3

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
@@ -1292,6 +1292,21 @@ async function runPass3Judge(description, workflow2) {
1292
1292
  return { pass: true, feedback: "", skipped: true };
1293
1293
  }
1294
1294
  }
1295
+ function isNumericSequence(arr) {
1296
+ return arr.every((item, i) => item === String(i + 1));
1297
+ }
1298
+ function normalizeWorkflow(workflow2) {
1299
+ return {
1300
+ ...workflow2,
1301
+ steps: workflow2.steps.map((step) => {
1302
+ if (Array.isArray(step.forEach) && isNumericSequence(step.forEach)) {
1303
+ const { forEach, ...rest } = step;
1304
+ return { ...rest, repeat: forEach.length };
1305
+ }
1306
+ return step;
1307
+ })
1308
+ };
1309
+ }
1295
1310
  async function* streamPlan(args) {
1296
1311
  const { description, taskFile } = args;
1297
1312
  const skipResearch = args.fast || isSimpleRequest(description);
@@ -1414,7 +1429,7 @@ ${issues}` };
1414
1429
  if (!judgeResult.pass) {
1415
1430
  yield { type: "plan:warn", message: `Judge rejected plan but retries exhausted: ${judgeResult.feedback}` };
1416
1431
  }
1417
- const { goal, vars, steps, ...rest } = zodResult.data;
1432
+ const { goal, vars, steps, ...rest } = normalizeWorkflow(zodResult.data);
1418
1433
  const ordered = { goal, ...vars && { vars }, steps, ...rest };
1419
1434
  const yamlContent = dumpYaml(ordered, {
1420
1435
  lineWidth: -1,
@@ -97,6 +97,11 @@ Before finalising your JSON, scan every `prompt` and `command` field you wrote.
97
97
  For each field, ask: "Does this contain a file path or directory path as a string literal?"
98
98
  If yes, extract it to `vars` and replace with `{{var_name}}`.
99
99
 
100
+ **Pre-Output Self-Review — Repeat (MANDATORY):**
101
+ Scan every `forEach` field you wrote.
102
+ Ask: "Is this array just sequential numbers like `["1","2","3"]` with no meaningful items?"
103
+ If yes, replace the entire `forEach` with `repeat: N` where N is the count. Sequential-number forEach arrays are ALWAYS wrong — they are a misuse of forEach and must be converted to `repeat: N`.
104
+
100
105
  ## When to Use Each Step Type
101
106
 
102
107
  **Use `prompt` steps (AI-assisted) for:**
@@ -59,6 +59,13 @@ Are all file paths declared in `vars`?
59
59
  - Do any `prompt` or `command` fields contain hardcoded file paths or directory paths?
60
60
  - Hardcoded paths in steps (not in `vars`) are a violation
61
61
 
62
+ ### 5. Repeat Misuse (if applicable)
63
+ If the user's goal mentions "N times", "repeat N", "N iterations", or "N passes":
64
+
65
+ - Does any step use `forEach` with a sequential numeric array like `["1","2","3","4","5"]`?
66
+ - This is always wrong — `repeat: N` must be used instead of a numeric forEach array
67
+ - Reject and require the offending step be converted to `repeat: N`
68
+
62
69
  ## Output Format
63
70
 
64
71
  Respond with ONLY a JSON object in this exact shape:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executant",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Harness for YAML-defined workflows that enables stepping through Claude sessions and bash commands",
5
5
  "repository": {
6
6
  "type": "git",