executant 1.17.0 → 1.18.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.
Files changed (2) hide show
  1. package/dist/index.js +193 -258
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -433,7 +433,7 @@ function resolveClaudePath() {
433
433
  );
434
434
  }
435
435
  }
436
- async function* runClaude(task, _channel) {
436
+ async function* runClaude(task) {
437
437
  yield {
438
438
  type: "log",
439
439
  level: "info",
@@ -628,7 +628,7 @@ ${queued.join("\n")}
628
628
  ---
629
629
  ${expanded.prompt}`
630
630
  } : expanded;
631
- yield* enriched.llmAsJudge ? runClaudeWithJudge(enriched, channel2) : runClaude(enriched, channel2);
631
+ yield* enriched.llmAsJudge ? runClaudeWithJudge(enriched) : runClaude(enriched);
632
632
  break;
633
633
  }
634
634
  case "forEach":
@@ -813,14 +813,14 @@ async function* runCommandWithHealing(task) {
813
813
  }
814
814
  }
815
815
  }
816
- async function* runClaudeWithJudge(task, channel2) {
816
+ async function* runClaudeWithJudge(task) {
817
817
  let judgeContext = "";
818
818
  for (let attempt = 0; attempt < MAX_JUDGE_RETRIES; attempt++) {
819
819
  const prompt = attempt === 0 ? task.prompt : `${task.prompt}
820
820
 
821
821
  ${fillTemplate(JUDGE_RETRY_CONTEXT, { FEEDBACK: judgeContext })}`;
822
822
  const lines = [];
823
- yield* collectLines(runClaude({ ...task, prompt }, channel2), lines);
823
+ yield* collectLines(runClaude({ ...task, prompt }), lines);
824
824
  yield {
825
825
  type: "log",
826
826
  level: "info",
@@ -1015,18 +1015,18 @@ function formatToolCall2(tool, input) {
1015
1015
  case "Read":
1016
1016
  case "Edit":
1017
1017
  case "Write":
1018
- return `[${tool}] ${input["file_path"] ?? JSON.stringify(input)}`;
1018
+ case "Glob":
1019
+ case "Grep":
1020
+ return `[${tool}] ${getToolArg(tool, input)}`;
1019
1021
  case "Bash":
1020
1022
  return `[Bash] ${input["description"] ?? ""}
1021
1023
  $ ${String(input["command"] ?? "").slice(0, 120)}`;
1022
- case "Glob":
1023
- return `[Glob] ${input["pattern"] ?? JSON.stringify(input)}`;
1024
- case "Grep":
1025
- return `[Grep] ${input["pattern"] ?? JSON.stringify(input)}`;
1026
1024
  case "TodoWrite": {
1027
1025
  const todos = input["todos"];
1028
1026
  if (Array.isArray(todos)) {
1029
- const inProgress = todos.filter((t) => typeof t === "object" && t !== null && t["status"] === "in_progress").map((t) => String(t["content"] ?? ""));
1027
+ const inProgress = todos.filter(
1028
+ (t) => typeof t === "object" && t !== null && t["status"] === "in_progress"
1029
+ ).map((t) => String(t["content"] ?? ""));
1030
1030
  if (inProgress.length > 0) return `[Task] ${inProgress.join(", ")}`;
1031
1031
  }
1032
1032
  return "";
@@ -1816,111 +1816,58 @@ function collapseSequentialSteps(steps) {
1816
1816
  { out: [], skip: 0 }
1817
1817
  ).out;
1818
1818
  }
1819
- async function* streamPlan(args) {
1820
- const { description, taskFile } = args;
1821
- const skipResearch = args.fast || isSimpleRequest(description);
1822
- yield { type: "plan:start", description };
1823
- let researchDoc;
1824
- if (skipResearch) {
1825
- yield { type: "plan:stages", names: ["Decompose to Steps", "Validate"] };
1826
- researchDoc = "No codebase research performed \u2014 the task is self-contained. Work directly from the user's original goal.";
1827
- } else {
1828
- yield {
1829
- type: "plan:stages",
1830
- names: ["Research & Planning", "Decompose to Steps", "Validate"]
1831
- };
1832
- yield {
1833
- type: "plan:stage",
1834
- stage: 1,
1835
- total: TOTAL_PLAN_STAGES,
1836
- name: "Research & Planning"
1837
- };
1838
- const researchLines = [];
1839
- try {
1840
- const researchTask = {
1841
- type: "claude",
1842
- name: "plan:research",
1843
- prompt: fillTemplate(PLAN_RESEARCH_PROMPT, {
1844
- DESCRIPTION: description
1845
- }),
1846
- allowedTools: ["Read", "Glob", "Grep"],
1847
- permissionMode: "bypassPermissions",
1848
- model: "opus",
1849
- appendSystemPrompt: METHODOLOGY
1850
- };
1851
- for await (const event of runClaude(researchTask)) {
1852
- if (event.type === "output:tool") {
1853
- yield { type: "plan:tool", tool: event.tool, input: event.input };
1854
- } else if (event.type === "output:text") {
1855
- researchLines.push(event.text);
1856
- yield { type: "plan:text", text: event.text };
1857
- }
1858
- }
1859
- } catch (err) {
1860
- yield {
1861
- type: "plan:error",
1862
- message: `Research pass failed: ${getErrorMessage(err)}`
1863
- };
1864
- return;
1865
- }
1866
- researchDoc = researchLines.join("\n");
1867
- if (!researchDoc.trim()) {
1868
- yield {
1869
- type: "plan:error",
1870
- message: "Research pass produced no output \u2014 cannot decompose"
1871
- };
1872
- return;
1873
- }
1874
- }
1875
- const stages = skipResearch ? { decompose: 1, validate: 2, total: 2 } : { decompose: 2, validate: 3, total: TOTAL_PLAN_STAGES };
1876
- yield {
1877
- type: "plan:stage",
1878
- stage: stages.decompose,
1879
- total: stages.total,
1880
- name: "Decompose to Steps"
1881
- };
1819
+ function writeWorkflowFile(taskFile, workflow2) {
1820
+ const { goal, vars, steps, ...rest } = normalizeWorkflow(workflow2);
1821
+ const ordered = { goal, ...vars && { vars }, steps, ...rest };
1822
+ const yamlContent = dumpYaml(ordered, {
1823
+ lineWidth: -1,
1824
+ noRefs: true,
1825
+ quotingType: '"',
1826
+ forceQuotes: false
1827
+ }).trimEnd();
1828
+ writeFileSync2(taskFile, yamlContent + "\n", "utf8");
1829
+ const lines = yamlContent.split("\n");
1830
+ return lines.slice(0, 30).join("\n") + (lines.length > 30 ? "\n..." : "");
1831
+ }
1832
+ async function* runRetryLoop(config) {
1833
+ const {
1834
+ maxRetries,
1835
+ retryStageName,
1836
+ retryStage,
1837
+ retryTotal,
1838
+ validateStage,
1839
+ validateTotal,
1840
+ schemaErrorLabel,
1841
+ judgeRejectLabel,
1842
+ description,
1843
+ taskFile,
1844
+ buildTask
1845
+ } = config;
1882
1846
  let retryPrefix = "";
1883
- for (let attempt = 0; attempt < MAX_PLAN_RETRIES; attempt++) {
1847
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1884
1848
  if (attempt > 0) {
1885
1849
  yield {
1886
1850
  type: "plan:retry",
1887
1851
  attempt: attempt + 1,
1888
- maxAttempts: MAX_PLAN_RETRIES,
1852
+ maxAttempts: maxRetries,
1889
1853
  reason: retryPrefix.replace(/\n/g, " ")
1890
1854
  };
1891
1855
  yield {
1892
1856
  type: "plan:stage",
1893
- stage: stages.decompose,
1894
- total: stages.total,
1895
- name: "Decompose to Steps"
1857
+ stage: retryStage,
1858
+ total: retryTotal,
1859
+ name: retryStageName
1896
1860
  };
1897
1861
  }
1898
- const basePrompt = fillTemplate(PLAN_DECOMPOSE_PROMPT, {
1899
- DESCRIPTION: description,
1900
- RESEARCH_DOC: researchDoc
1901
- });
1902
- const decomposeTask = {
1903
- type: "claude",
1904
- name: "plan:decompose",
1905
- prompt: retryPrefix ? `${retryPrefix}
1906
-
1907
- ${basePrompt}` : basePrompt,
1908
- allowedTools: [],
1909
- permissionMode: "bypassPermissions",
1910
- model: skipResearch ? "sonnet" : "opus",
1911
- appendSystemPrompt: `${METHODOLOGY}
1912
-
1913
- ${PLAN_SYSTEM_RULES}`,
1914
- jsonSchema: WORKFLOW_JSON_SCHEMA
1915
- };
1862
+ const task = buildTask(retryPrefix);
1916
1863
  let structuredOutput;
1917
- const decomposeTextLines = [];
1864
+ const textLines = [];
1918
1865
  try {
1919
- for await (const event of runClaude(decomposeTask)) {
1866
+ for await (const event of runClaude(task)) {
1920
1867
  if (event.type === "output:tool") {
1921
1868
  yield { type: "plan:tool", tool: event.tool, input: event.input };
1922
1869
  } else if (event.type === "output:text") {
1923
- decomposeTextLines.push(event.text);
1870
+ textLines.push(event.text);
1924
1871
  yield { type: "plan:text", text: event.text };
1925
1872
  } else if (event.type === "output:structured") {
1926
1873
  structuredOutput = event.data;
@@ -1928,19 +1875,19 @@ ${PLAN_SYSTEM_RULES}`,
1928
1875
  }
1929
1876
  } catch (err) {
1930
1877
  const msg = getErrorMessage(err);
1931
- if (attempt === MAX_PLAN_RETRIES - 1) {
1878
+ if (attempt === maxRetries - 1) {
1932
1879
  yield { type: "plan:error", message: msg };
1933
1880
  return;
1934
1881
  }
1935
1882
  retryPrefix = fillTemplate(PLAN_RETRY_PARSE_ERROR, {
1936
1883
  ERROR: msg,
1937
- EXCERPT: decomposeTextLines.join("\n")
1884
+ EXCERPT: textLines.join("\n")
1938
1885
  });
1939
1886
  continue;
1940
1887
  }
1941
1888
  if (structuredOutput === void 0) {
1942
1889
  const issues = "No structured output returned \u2014 ensure the response is a JSON object";
1943
- if (attempt === MAX_PLAN_RETRIES - 1) {
1890
+ if (attempt === maxRetries - 1) {
1944
1891
  yield { type: "plan:error", message: issues };
1945
1892
  return;
1946
1893
  }
@@ -1950,10 +1897,10 @@ ${PLAN_SYSTEM_RULES}`,
1950
1897
  const zodResult = WorkflowSchema.safeParse(structuredOutput);
1951
1898
  if (!zodResult.success) {
1952
1899
  const issues = formatZodIssues(zodResult.error.issues);
1953
- if (attempt === MAX_PLAN_RETRIES - 1) {
1900
+ if (attempt === maxRetries - 1) {
1954
1901
  yield {
1955
1902
  type: "plan:error",
1956
- message: `Plan did not match expected schema:
1903
+ message: `${schemaErrorLabel} did not match expected schema:
1957
1904
  ${issues}`
1958
1905
  };
1959
1906
  return;
@@ -1963,8 +1910,8 @@ ${issues}`
1963
1910
  }
1964
1911
  yield {
1965
1912
  type: "plan:stage",
1966
- stage: stages.validate,
1967
- total: stages.total,
1913
+ stage: validateStage,
1914
+ total: validateTotal,
1968
1915
  name: "Validate"
1969
1916
  };
1970
1917
  const judgeResult = await runPass3Judge(description, zodResult.data);
@@ -1974,7 +1921,7 @@ ${issues}`
1974
1921
  message: "Judge skipped due to error \u2014 proceeding without validation"
1975
1922
  };
1976
1923
  }
1977
- if (!judgeResult.pass && attempt < MAX_PLAN_RETRIES - 1) {
1924
+ if (!judgeResult.pass && attempt < maxRetries - 1) {
1978
1925
  retryPrefix = fillTemplate(PLAN_RETRY_JUDGE, {
1979
1926
  FEEDBACK: judgeResult.feedback
1980
1927
  });
@@ -1983,37 +1930,120 @@ ${issues}`
1983
1930
  if (!judgeResult.pass) {
1984
1931
  yield {
1985
1932
  type: "plan:warn",
1986
- message: `Judge rejected plan but retries exhausted: ${judgeResult.feedback}`
1933
+ message: `Judge rejected ${judgeRejectLabel} but retries exhausted: ${judgeResult.feedback}`
1987
1934
  };
1988
1935
  }
1989
- const { goal, vars, steps, ...rest } = normalizeWorkflow(zodResult.data);
1990
- const ordered = { goal, ...vars && { vars }, steps, ...rest };
1991
- const yamlContent = dumpYaml(ordered, {
1992
- lineWidth: -1,
1993
- noRefs: true,
1994
- quotingType: '"',
1995
- forceQuotes: false
1996
- }).trimEnd();
1997
- writeFileSync2(taskFile, yamlContent + "\n", "utf8");
1998
- const yamlLines = yamlContent.split("\n");
1999
- const preview = yamlLines.slice(0, 30).join("\n") + (yamlLines.length > 30 ? "\n..." : "");
1936
+ const preview = writeWorkflowFile(taskFile, zodResult.data);
2000
1937
  yield { type: "plan:complete", taskFile, preview };
2001
1938
  return;
2002
1939
  }
2003
1940
  yield {
2004
1941
  type: "plan:error",
2005
- message: "Plan generation failed after maximum retries"
1942
+ message: `${schemaErrorLabel} generation failed after maximum retries`
1943
+ };
1944
+ }
1945
+ async function* streamPlan(args) {
1946
+ const { description, taskFile } = args;
1947
+ const skipResearch = args.fast || isSimpleRequest(description);
1948
+ yield { type: "plan:start", description };
1949
+ let researchDoc;
1950
+ if (skipResearch) {
1951
+ yield { type: "plan:stages", names: ["Decompose to Steps", "Validate"] };
1952
+ researchDoc = "No codebase research performed \u2014 the task is self-contained. Work directly from the user's original goal.";
1953
+ } else {
1954
+ yield {
1955
+ type: "plan:stages",
1956
+ names: ["Research & Planning", "Decompose to Steps", "Validate"]
1957
+ };
1958
+ yield {
1959
+ type: "plan:stage",
1960
+ stage: 1,
1961
+ total: TOTAL_PLAN_STAGES,
1962
+ name: "Research & Planning"
1963
+ };
1964
+ const researchLines = [];
1965
+ try {
1966
+ const researchTask = {
1967
+ type: "claude",
1968
+ name: "plan:research",
1969
+ prompt: fillTemplate(PLAN_RESEARCH_PROMPT, {
1970
+ DESCRIPTION: description
1971
+ }),
1972
+ allowedTools: ["Read", "Glob", "Grep"],
1973
+ permissionMode: "bypassPermissions",
1974
+ model: "opus",
1975
+ appendSystemPrompt: METHODOLOGY
1976
+ };
1977
+ for await (const event of runClaude(researchTask)) {
1978
+ if (event.type === "output:tool") {
1979
+ yield { type: "plan:tool", tool: event.tool, input: event.input };
1980
+ } else if (event.type === "output:text") {
1981
+ researchLines.push(event.text);
1982
+ yield { type: "plan:text", text: event.text };
1983
+ }
1984
+ }
1985
+ } catch (err) {
1986
+ yield {
1987
+ type: "plan:error",
1988
+ message: `Research pass failed: ${getErrorMessage(err)}`
1989
+ };
1990
+ return;
1991
+ }
1992
+ researchDoc = researchLines.join("\n");
1993
+ if (!researchDoc.trim()) {
1994
+ yield {
1995
+ type: "plan:error",
1996
+ message: "Research pass produced no output \u2014 cannot decompose"
1997
+ };
1998
+ return;
1999
+ }
2000
+ }
2001
+ const stages = skipResearch ? { decompose: 1, validate: 2, total: 2 } : { decompose: 2, validate: 3, total: TOTAL_PLAN_STAGES };
2002
+ yield {
2003
+ type: "plan:stage",
2004
+ stage: stages.decompose,
2005
+ total: stages.total,
2006
+ name: "Decompose to Steps"
2006
2007
  };
2008
+ yield* runRetryLoop({
2009
+ maxRetries: MAX_PLAN_RETRIES,
2010
+ retryStageName: "Decompose to Steps",
2011
+ retryStage: stages.decompose,
2012
+ retryTotal: stages.total,
2013
+ validateStage: stages.validate,
2014
+ validateTotal: stages.total,
2015
+ schemaErrorLabel: "Plan",
2016
+ judgeRejectLabel: "plan",
2017
+ description,
2018
+ taskFile,
2019
+ buildTask: (retryPrefix) => {
2020
+ const basePrompt = fillTemplate(PLAN_DECOMPOSE_PROMPT, {
2021
+ DESCRIPTION: description,
2022
+ RESEARCH_DOC: researchDoc
2023
+ });
2024
+ return {
2025
+ type: "claude",
2026
+ name: "plan:decompose",
2027
+ prompt: retryPrefix ? `${retryPrefix}
2028
+
2029
+ ${basePrompt}` : basePrompt,
2030
+ allowedTools: [],
2031
+ permissionMode: "bypassPermissions",
2032
+ model: skipResearch ? "sonnet" : "opus",
2033
+ appendSystemPrompt: `${METHODOLOGY}
2034
+
2035
+ ${PLAN_SYSTEM_RULES}`,
2036
+ jsonSchema: WORKFLOW_JSON_SCHEMA
2037
+ };
2038
+ }
2039
+ });
2007
2040
  }
2008
2041
 
2009
2042
  // src/refine.ts
2010
- import { existsSync as existsSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
2011
- import { load as loadYaml, dump as dumpYaml2 } from "js-yaml";
2043
+ import { existsSync as existsSync2, readFileSync as readFileSync5 } from "node:fs";
2044
+ import { load as loadYaml } from "js-yaml";
2012
2045
  var PLAN_REFINE_PROMPT = loadPrompt("plan-refine");
2013
2046
  var PLAN_SYSTEM_RULES2 = loadPrompt("plan-system-rules");
2014
- var PLAN_RETRY_PARSE_ERROR2 = loadPrompt("plan-retry-parse-error");
2015
- var PLAN_RETRY_SCHEMA_ERROR2 = loadPrompt("plan-retry-schema-error");
2016
- var PLAN_RETRY_JUDGE2 = loadPrompt("plan-retry-judge");
2017
2047
  var MAX_REFINE_RETRIES = 3;
2018
2048
  function parseRefineArgs(rawArgs2) {
2019
2049
  if (rawArgs2[0] === "-h" || rawArgs2[0] === "--help") {
@@ -2094,122 +2124,39 @@ async function* streamRefine(args) {
2094
2124
  yield { type: "plan:start", description };
2095
2125
  yield { type: "plan:stages", names: ["Refine", "Validate"] };
2096
2126
  yield { type: "plan:stage", stage: 1, total: 2, name: "Refine" };
2097
- let retryPrefix = "";
2098
- for (let attempt = 0; attempt < MAX_REFINE_RETRIES; attempt++) {
2099
- if (attempt > 0) {
2100
- yield {
2101
- type: "plan:retry",
2102
- attempt: attempt + 1,
2103
- maxAttempts: MAX_REFINE_RETRIES,
2104
- reason: retryPrefix.replace(/\n/g, " ")
2105
- };
2106
- yield { type: "plan:stage", stage: 1, total: 2, name: "Refine" };
2107
- }
2108
- const basePrompt = fillTemplate(PLAN_REFINE_PROMPT, {
2109
- DESCRIPTION: description,
2110
- EXISTING_YAML: existingYaml,
2111
- INSTRUCTIONS: instructions
2112
- });
2113
- const refineTask = {
2114
- type: "claude",
2115
- name: "plan:refine",
2116
- prompt: retryPrefix ? `${retryPrefix}
2127
+ yield* runRetryLoop({
2128
+ maxRetries: MAX_REFINE_RETRIES,
2129
+ retryStageName: "Refine",
2130
+ retryStage: 1,
2131
+ retryTotal: 2,
2132
+ validateStage: 2,
2133
+ validateTotal: 2,
2134
+ schemaErrorLabel: "Refined plan",
2135
+ judgeRejectLabel: "refinement",
2136
+ description,
2137
+ taskFile,
2138
+ buildTask: (retryPrefix) => {
2139
+ const basePrompt = fillTemplate(PLAN_REFINE_PROMPT, {
2140
+ DESCRIPTION: description,
2141
+ EXISTING_YAML: existingYaml,
2142
+ INSTRUCTIONS: instructions
2143
+ });
2144
+ return {
2145
+ type: "claude",
2146
+ name: "plan:refine",
2147
+ prompt: retryPrefix ? `${retryPrefix}
2117
2148
 
2118
2149
  ${basePrompt}` : basePrompt,
2119
- allowedTools: [],
2120
- permissionMode: "bypassPermissions",
2121
- model: "sonnet",
2122
- appendSystemPrompt: `${METHODOLOGY}
2150
+ allowedTools: [],
2151
+ permissionMode: "bypassPermissions",
2152
+ model: "sonnet",
2153
+ appendSystemPrompt: `${METHODOLOGY}
2123
2154
 
2124
2155
  ${PLAN_SYSTEM_RULES2}`,
2125
- jsonSchema: WORKFLOW_JSON_SCHEMA
2126
- };
2127
- let structuredOutput;
2128
- const textLines = [];
2129
- try {
2130
- for await (const event of runClaude(refineTask)) {
2131
- if (event.type === "output:tool") {
2132
- yield { type: "plan:tool", tool: event.tool, input: event.input };
2133
- } else if (event.type === "output:text") {
2134
- textLines.push(event.text);
2135
- yield { type: "plan:text", text: event.text };
2136
- } else if (event.type === "output:structured") {
2137
- structuredOutput = event.data;
2138
- }
2139
- }
2140
- } catch (err) {
2141
- const msg = getErrorMessage(err);
2142
- if (attempt === MAX_REFINE_RETRIES - 1) {
2143
- yield { type: "plan:error", message: msg };
2144
- return;
2145
- }
2146
- retryPrefix = fillTemplate(PLAN_RETRY_PARSE_ERROR2, {
2147
- ERROR: msg,
2148
- EXCERPT: textLines.join("\n")
2149
- });
2150
- continue;
2151
- }
2152
- if (structuredOutput === void 0) {
2153
- const issues = "No structured output returned \u2014 ensure the response is a JSON object";
2154
- if (attempt === MAX_REFINE_RETRIES - 1) {
2155
- yield { type: "plan:error", message: issues };
2156
- return;
2157
- }
2158
- retryPrefix = fillTemplate(PLAN_RETRY_SCHEMA_ERROR2, { ISSUES: issues });
2159
- continue;
2160
- }
2161
- const zodResult = WorkflowSchema.safeParse(structuredOutput);
2162
- if (!zodResult.success) {
2163
- const issues = formatZodIssues(zodResult.error.issues);
2164
- if (attempt === MAX_REFINE_RETRIES - 1) {
2165
- yield {
2166
- type: "plan:error",
2167
- message: `Refined plan did not match expected schema:
2168
- ${issues}`
2169
- };
2170
- return;
2171
- }
2172
- retryPrefix = fillTemplate(PLAN_RETRY_SCHEMA_ERROR2, { ISSUES: issues });
2173
- continue;
2174
- }
2175
- yield { type: "plan:stage", stage: 2, total: 2, name: "Validate" };
2176
- const judgeResult = await runPass3Judge(description, zodResult.data);
2177
- if (judgeResult.skipped) {
2178
- yield {
2179
- type: "plan:warn",
2180
- message: "Judge skipped due to error \u2014 proceeding without validation"
2181
- };
2182
- }
2183
- if (!judgeResult.pass && attempt < MAX_REFINE_RETRIES - 1) {
2184
- retryPrefix = fillTemplate(PLAN_RETRY_JUDGE2, {
2185
- FEEDBACK: judgeResult.feedback
2186
- });
2187
- continue;
2188
- }
2189
- if (!judgeResult.pass) {
2190
- yield {
2191
- type: "plan:warn",
2192
- message: `Judge rejected refinement but retries exhausted: ${judgeResult.feedback}`
2156
+ jsonSchema: WORKFLOW_JSON_SCHEMA
2193
2157
  };
2194
2158
  }
2195
- const { goal, vars, steps, ...rest } = normalizeWorkflow(zodResult.data);
2196
- const ordered = { goal, ...vars && { vars }, steps, ...rest };
2197
- const yamlContent = dumpYaml2(ordered, {
2198
- lineWidth: -1,
2199
- noRefs: true,
2200
- quotingType: '"',
2201
- forceQuotes: false
2202
- }).trimEnd();
2203
- writeFileSync3(taskFile, yamlContent + "\n", "utf8");
2204
- const yamlLines = yamlContent.split("\n");
2205
- const preview = yamlLines.slice(0, 30).join("\n") + (yamlLines.length > 30 ? "\n..." : "");
2206
- yield { type: "plan:complete", taskFile, preview };
2207
- return;
2208
- }
2209
- yield {
2210
- type: "plan:error",
2211
- message: "Refine failed after maximum retries"
2212
- };
2159
+ });
2213
2160
  }
2214
2161
 
2215
2162
  // src/ui/PlanApp.tsx
@@ -2386,7 +2333,7 @@ import {
2386
2333
  existsSync as existsSync3,
2387
2334
  mkdirSync as mkdirSync3,
2388
2335
  readdirSync,
2389
- writeFileSync as writeFileSync4
2336
+ writeFileSync as writeFileSync3
2390
2337
  } from "node:fs";
2391
2338
  import { dirname as dirname3, join as join3, resolve as resolve2 } from "node:path";
2392
2339
  function findExecutantLocalDir(startDir) {
@@ -2425,7 +2372,7 @@ function onWorkflowStart(ctx, s) {
2425
2372
  mkdirSync3(ctx.logDir, { recursive: true });
2426
2373
  mkdirSync3(ctx.highlightsDir, { recursive: true });
2427
2374
  const logFile = join3(ctx.logDir, `${ctx.ts}_${ctx.slug}.log`);
2428
- writeFileSync4(
2375
+ writeFileSync3(
2429
2376
  logFile,
2430
2377
  `# Execution Log
2431
2378
  Task: ${ctx.slug}
@@ -2504,7 +2451,7 @@ function complexSequenceHeader(ctx, s) {
2504
2451
  }
2505
2452
  function createComplexSequenceFile(ctx, s) {
2506
2453
  const path = highlightPath(ctx, s.stepIndex, "complex_sequence");
2507
- writeFileSync4(path, complexSequenceHeader(ctx, s));
2454
+ writeFileSync3(path, complexSequenceHeader(ctx, s));
2508
2455
  return path;
2509
2456
  }
2510
2457
  function onTool(ctx, s, tool, input) {
@@ -2522,7 +2469,7 @@ function onTool(ctx, s, tool, input) {
2522
2469
  return { ...s, toolCount, complexSequenceFile };
2523
2470
  }
2524
2471
  function saveJudgeHighlight(ctx, s, verdict, text) {
2525
- writeFileSync4(
2472
+ writeFileSync3(
2526
2473
  highlightPath(ctx, s.stepIndex, `judge_${verdict}`),
2527
2474
  buildHighlightHeader(ctx, s, `Judge Verdict: ${verdict}`, [
2528
2475
  `**Attempt:** ${s.judgeAttempt}`
@@ -2543,7 +2490,7 @@ var LOG_MATCHERS = [
2543
2490
  pattern: /\[self-healing\].*failed.*exit\s+(\d+)/i,
2544
2491
  apply: (ctx, s, _text, match) => {
2545
2492
  const selfHealingFile = highlightPath(ctx, s.stepIndex, "self_healing");
2546
- writeFileSync4(
2493
+ writeFileSync3(
2547
2494
  selfHealingFile,
2548
2495
  buildHighlightHeader(ctx, s, "Self-Healing Activation") + [
2549
2496
  "## \u274C Failure Detected",
@@ -2614,7 +2561,7 @@ ${"\u2501".repeat(51)}
2614
2561
  );
2615
2562
  const indexFile = join3(ctx.highlightsDir, "README.md");
2616
2563
  if (!existsSync3(indexFile)) {
2617
- writeFileSync4(
2564
+ writeFileSync3(
2618
2565
  indexFile,
2619
2566
  [
2620
2567
  "# Execution Highlights",
@@ -2713,7 +2660,7 @@ import {
2713
2660
  mkdirSync as mkdirSync4,
2714
2661
  readdirSync as readdirSync2,
2715
2662
  readFileSync as readFileSync6,
2716
- writeFileSync as writeFileSync5
2663
+ writeFileSync as writeFileSync4
2717
2664
  } from "node:fs";
2718
2665
  import { basename as basename2, dirname as dirname4, join as join4, resolve as resolve3 } from "node:path";
2719
2666
  import { spawnSync } from "node:child_process";
@@ -2858,8 +2805,8 @@ Response: ${response.trim()}`
2858
2805
  const slug = slugify(taskName, 40);
2859
2806
  const improvedFile = join4(backlogDir, `${ts}-${slug}-improved.yaml`);
2860
2807
  const changelogFile = join4(backlogDir, `${ts}-${slug}-changelog.md`);
2861
- writeFileSync5(improvedFile, improvedYaml + "\n", "utf8");
2862
- writeFileSync5(changelogFile, changelog + "\n", "utf8");
2808
+ writeFileSync4(improvedFile, improvedYaml + "\n", "utf8");
2809
+ writeFileSync4(changelogFile, changelog + "\n", "utf8");
2863
2810
  console.log(`\u2705 Improved task saved: ${improvedFile}`);
2864
2811
  console.log(`\u2705 Changelog saved: ${changelogFile}`);
2865
2812
  console.log(`
@@ -2879,22 +2826,10 @@ function extractJson(text) {
2879
2826
 
2880
2827
  // src/types.ts
2881
2828
  var InterjectChannel = class {
2882
- sender = null;
2883
2829
  _queue = [];
2884
- /** Called by runClaude when a Claude step starts to activate direct delivery. */
2885
- register(sender) {
2886
- this.sender = sender;
2887
- for (const msg of this._queue) sender(msg);
2888
- this._queue = [];
2889
- }
2890
- /** Called by runClaude when a Claude step ends. */
2891
- unregister() {
2892
- this.sender = null;
2893
- }
2894
- /** Called by the TUI. Delivers immediately if a Claude step is running, else queues. */
2830
+ /** Called by the TUI when the user submits an interjection message. */
2895
2831
  interject(message) {
2896
- if (this.sender) this.sender(message);
2897
- else this._queue.push(message);
2832
+ this._queue.push(message);
2898
2833
  }
2899
2834
  /** Drains and returns any queued messages (for non-Claude steps to consume). */
2900
2835
  consumeQueue() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executant",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Harness for YAML-defined workflows that enables stepping through Claude sessions and bash commands",
5
5
  "repository": {
6
6
  "type": "git",