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.
- package/dist/index.js +193 -258
- 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
|
|
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
|
|
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
|
|
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 }
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
1820
|
-
const {
|
|
1821
|
-
const
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
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 <
|
|
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:
|
|
1852
|
+
maxAttempts: maxRetries,
|
|
1889
1853
|
reason: retryPrefix.replace(/\n/g, " ")
|
|
1890
1854
|
};
|
|
1891
1855
|
yield {
|
|
1892
1856
|
type: "plan:stage",
|
|
1893
|
-
stage:
|
|
1894
|
-
total:
|
|
1895
|
-
name:
|
|
1857
|
+
stage: retryStage,
|
|
1858
|
+
total: retryTotal,
|
|
1859
|
+
name: retryStageName
|
|
1896
1860
|
};
|
|
1897
1861
|
}
|
|
1898
|
-
const
|
|
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
|
|
1864
|
+
const textLines = [];
|
|
1918
1865
|
try {
|
|
1919
|
-
for await (const event of runClaude(
|
|
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
|
-
|
|
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 ===
|
|
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:
|
|
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 ===
|
|
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 ===
|
|
1900
|
+
if (attempt === maxRetries - 1) {
|
|
1954
1901
|
yield {
|
|
1955
1902
|
type: "plan:error",
|
|
1956
|
-
message:
|
|
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:
|
|
1967
|
-
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 <
|
|
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
|
|
1933
|
+
message: `Judge rejected ${judgeRejectLabel} but retries exhausted: ${judgeResult.feedback}`
|
|
1987
1934
|
};
|
|
1988
1935
|
}
|
|
1989
|
-
const
|
|
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:
|
|
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
|
|
2011
|
-
import { load as loadYaml
|
|
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
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2150
|
+
allowedTools: [],
|
|
2151
|
+
permissionMode: "bypassPermissions",
|
|
2152
|
+
model: "sonnet",
|
|
2153
|
+
appendSystemPrompt: `${METHODOLOGY}
|
|
2123
2154
|
|
|
2124
2155
|
${PLAN_SYSTEM_RULES2}`,
|
|
2125
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2862
|
-
|
|
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
|
|
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
|
-
|
|
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() {
|