harnessed 4.0.1 → 4.1.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.
- package/dist/cli.mjs +157 -91
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1271,7 +1271,7 @@ var init_auto_install = __esm({
|
|
|
1271
1271
|
|
|
1272
1272
|
// package.json
|
|
1273
1273
|
var package_default = {
|
|
1274
|
-
version: "4.
|
|
1274
|
+
version: "4.1.1"};
|
|
1275
1275
|
|
|
1276
1276
|
// src/manifest/errors.ts
|
|
1277
1277
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -2583,6 +2583,7 @@ function getPackageRoot() {
|
|
|
2583
2583
|
|
|
2584
2584
|
// src/cli/gates.ts
|
|
2585
2585
|
var VALID_MASTERS = /* @__PURE__ */ new Set(["auto", "discuss", "plan", "task", "verify"]);
|
|
2586
|
+
var STAGE_MASTERS = /* @__PURE__ */ new Set(["discuss", "plan", "task", "verify"]);
|
|
2586
2587
|
var PARALLELISM_GATE = "judgments.parallelism-gate.agent-teams-upgrade.fires";
|
|
2587
2588
|
function defaultContext(task, stage) {
|
|
2588
2589
|
return {
|
|
@@ -2733,6 +2734,9 @@ function fireEntry(clause) {
|
|
|
2733
2734
|
if (clause.order !== void 0) entry.order = clause.order;
|
|
2734
2735
|
if (clause.mode !== void 0) entry.mode = clause.mode;
|
|
2735
2736
|
if (clause.gate !== void 0) entry.gate = clause.gate;
|
|
2737
|
+
if (STAGE_MASTERS.has(clause.sub)) {
|
|
2738
|
+
entry.is_master = true;
|
|
2739
|
+
}
|
|
2736
2740
|
return entry;
|
|
2737
2741
|
}
|
|
2738
2742
|
var DURATION_RE = /^(\d+)([dhmw])$/;
|
|
@@ -4460,7 +4464,9 @@ function buildOrchestratorBody(name, prompt) {
|
|
|
4460
4464
|
`2. Bash: \`harnessed gates ${name} --task "<locked spec>" --skip-sub clarify\` \u2192 parse the JSON \`{fire: [{sub, order, mode}], skip, parallelism: {escalate_to_teams}}\`.`,
|
|
4461
4465
|
`3. If \`parallelism.escalate_to_teams === true\`: this stage needs multiple subagents to coordinate (SendMessage / shared contract). Read \`~/.claude/rules/agent-teams.md\`, then \`TeamCreate\` \u2192 \`Agent(name, team_name, ...)\` per fired sub (prompt from \`harnessed prompt <sub>\`) \u2192 coordinate via SendMessage \u2192 \`SendMessage shutdown_request\` + \`TeamDelete\`.`,
|
|
4462
4466
|
`4. Otherwise, for each fired sub in \`order\` (serial subs sequentially, parallel subs concurrently via parallel Task calls):`,
|
|
4463
|
-
|
|
4467
|
+
` - **If the fired entry has \`is_master: true\`** (it is itself a stage master, e.g. \`/auto\` firing \`plan\`/\`task\`/\`verify\`): do NOT prompt+spawn it directly \u2014 that would yield a vague dispatcher. RECURSE: run that master's orchestration \u2014 \`harnessed gates <sub> --task "<spec>" --skip-sub clarify\` \u2192 repeat this whole step-4 loop for ITS fired subs. Only leaf subs (no \`is_master\`) reach the spawn loop below.`,
|
|
4468
|
+
` - **Else (leaf sub)** \u2014 spawn it:`,
|
|
4469
|
+
...spawnLoopSteps(" "),
|
|
4464
4470
|
`5. Report a per-sub fired/skipped summary to the user. ${name === "auto" ? "Then run the `retro` stage to capture lessons." : ""}`,
|
|
4465
4471
|
``,
|
|
4466
4472
|
`Do NOT pipe to \`harnessed run ${name}\` \u2014 that is the CI/headless path (SDK spawn, blocks the session, no Agent Teams, no clarification round-trip).`,
|
|
@@ -5706,94 +5712,6 @@ async function runWorkflow(yamlPath, vars, opts = {}) {
|
|
|
5706
5712
|
...skippedPhases.length > 0 ? { skippedPhases } : {}
|
|
5707
5713
|
};
|
|
5708
5714
|
}
|
|
5709
|
-
|
|
5710
|
-
// src/cli/prompt.ts
|
|
5711
|
-
var DEFAULT_MAX_ITERATIONS = 20;
|
|
5712
|
-
var DEFAULT_MODEL = "sonnet";
|
|
5713
|
-
var DEFAULT_SPECIALIST = "Implementation Engineer";
|
|
5714
|
-
var LANG_NAMES = {
|
|
5715
|
-
en: "English",
|
|
5716
|
-
"zh-Hans": "\u7B80\u4F53\u4E2D\u6587 (Simplified Chinese)",
|
|
5717
|
-
"zh-CN": "\u7B80\u4F53\u4E2D\u6587 (Simplified Chinese)",
|
|
5718
|
-
"zh-Hant": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)",
|
|
5719
|
-
"zh-TW": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)"
|
|
5720
|
-
};
|
|
5721
|
-
function buildLanguageSection() {
|
|
5722
|
-
const code = process.env.HARNESSED_USER_LANG;
|
|
5723
|
-
if (!code) return "";
|
|
5724
|
-
const name = LANG_NAMES[code] ?? code;
|
|
5725
|
-
return `
|
|
5726
|
-
## Language
|
|
5727
|
-
Respond in ${name}. Keep code, commands, file/identifier/API names, error messages, stack traces, URLs, commit hashes, and version numbers in their original form (do not translate or transliterate them).
|
|
5728
|
-
`;
|
|
5729
|
-
}
|
|
5730
|
-
var PROTOCOLS = `
|
|
5731
|
-
## Completion protocol
|
|
5732
|
-
When done emit: <promise>COMPLETE</promise>
|
|
5733
|
-
|
|
5734
|
-
## Clarification protocol
|
|
5735
|
-
If you hit a gray area you cannot decide (\u22652 reasonable options, missing key context, ambiguous requirement), do NOT self-decide. Emit:
|
|
5736
|
-
STATUS: NEEDS_CLARIFICATION
|
|
5737
|
-
1. <question>
|
|
5738
|
-
2. <question>
|
|
5739
|
-
The main session will relay these to the user and re-spawn you with answers.
|
|
5740
|
-
`;
|
|
5741
|
-
async function resolveMaxIterations2(sub, packageRoot) {
|
|
5742
|
-
try {
|
|
5743
|
-
const path = resolve(packageRoot, "workflows", "defaults.yaml");
|
|
5744
|
-
const raw = await readFile(path, "utf8");
|
|
5745
|
-
const doc = parse(raw);
|
|
5746
|
-
const entry = doc?.ralph_max_iterations?.[sub];
|
|
5747
|
-
if (typeof entry === "number" && Number.isFinite(entry)) {
|
|
5748
|
-
return entry;
|
|
5749
|
-
}
|
|
5750
|
-
if (entry && typeof entry === "object") {
|
|
5751
|
-
for (const v of Object.values(entry)) {
|
|
5752
|
-
if (typeof v === "number" && Number.isFinite(v)) {
|
|
5753
|
-
return v;
|
|
5754
|
-
}
|
|
5755
|
-
}
|
|
5756
|
-
}
|
|
5757
|
-
return DEFAULT_MAX_ITERATIONS;
|
|
5758
|
-
} catch {
|
|
5759
|
-
return DEFAULT_MAX_ITERATIONS;
|
|
5760
|
-
}
|
|
5761
|
-
}
|
|
5762
|
-
function registerPrompt(program2) {
|
|
5763
|
-
program2.command("prompt").description(
|
|
5764
|
-
"Print a spawn-ready prompt for a sub-workflow (role + checklist + protocols, no spawn)."
|
|
5765
|
-
).argument("<sub>", "sub-workflow name (e.g. task-code, verify-paranoid)").option("--task <text>", "task description prepended as a ## Task section").option("--json", "emit JSON {prompt, max_iterations, model, specialist} instead of text").action(async (sub, raw) => {
|
|
5766
|
-
const packageRoot = getPackageRoot();
|
|
5767
|
-
const workflowsDir = resolve(packageRoot, "workflows");
|
|
5768
|
-
const rolePrompts = await loadRolePrompts(workflowsDir);
|
|
5769
|
-
const def = buildAgentDef(sub, rolePrompts, void 0, void 0, void 0);
|
|
5770
|
-
const body = def.prompt;
|
|
5771
|
-
const taskSection = typeof raw.task === "string" && raw.task.length > 0 ? `## Task
|
|
5772
|
-
${raw.task}
|
|
5773
|
-
|
|
5774
|
-
` : "";
|
|
5775
|
-
const fullPrompt = `${taskSection}${body}
|
|
5776
|
-
${PROTOCOLS}${buildLanguageSection()}`;
|
|
5777
|
-
if (raw.json) {
|
|
5778
|
-
const maxIterations = await resolveMaxIterations2(sub, packageRoot);
|
|
5779
|
-
const rp = rolePrompts[sub];
|
|
5780
|
-
const model = def.model ?? DEFAULT_MODEL;
|
|
5781
|
-
const specialist = rp?.specialist ?? DEFAULT_SPECIALIST;
|
|
5782
|
-
console.log(
|
|
5783
|
-
JSON.stringify({
|
|
5784
|
-
prompt: fullPrompt,
|
|
5785
|
-
max_iterations: maxIterations,
|
|
5786
|
-
model,
|
|
5787
|
-
specialist
|
|
5788
|
-
})
|
|
5789
|
-
);
|
|
5790
|
-
process.exit(0);
|
|
5791
|
-
return;
|
|
5792
|
-
}
|
|
5793
|
-
console.log(fullPrompt);
|
|
5794
|
-
process.exit(0);
|
|
5795
|
-
});
|
|
5796
|
-
}
|
|
5797
5715
|
async function loadUserOverrides(packageRoot) {
|
|
5798
5716
|
const yamlPath = resolve(packageRoot, "workflows", "judgments", "user-overrides.yaml");
|
|
5799
5717
|
let raw;
|
|
@@ -6060,7 +5978,155 @@ async function getNextHint(workflowName) {
|
|
|
6060
5978
|
return null;
|
|
6061
5979
|
}
|
|
6062
5980
|
|
|
6063
|
-
// src/cli/
|
|
5981
|
+
// src/cli/prompt.ts
|
|
5982
|
+
var DEFAULT_MAX_ITERATIONS = 20;
|
|
5983
|
+
var DEFAULT_MODEL = "sonnet";
|
|
5984
|
+
var DEFAULT_SPECIALIST = "Implementation Engineer";
|
|
5985
|
+
var LANG_NAMES = {
|
|
5986
|
+
en: "English",
|
|
5987
|
+
"zh-Hans": "\u7B80\u4F53\u4E2D\u6587 (Simplified Chinese)",
|
|
5988
|
+
"zh-CN": "\u7B80\u4F53\u4E2D\u6587 (Simplified Chinese)",
|
|
5989
|
+
"zh-Hant": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)",
|
|
5990
|
+
"zh-TW": "\u7E41\u9AD4\u4E2D\u6587 (Traditional Chinese)"
|
|
5991
|
+
};
|
|
5992
|
+
async function buildToolsSection(sub, packageRoot) {
|
|
5993
|
+
try {
|
|
5994
|
+
const workflowsDir = resolve(packageRoot, "workflows");
|
|
5995
|
+
const subYaml = await resolveWorkflowYaml(sub, workflowsDir);
|
|
5996
|
+
if (!subYaml) return "";
|
|
5997
|
+
const wfRaw = await readFile(subYaml, "utf8");
|
|
5998
|
+
const wf = parse(wfRaw);
|
|
5999
|
+
const tools = Array.isArray(wf?.tools_available) ? wf.tools_available : [];
|
|
6000
|
+
if (tools.length === 0) return "";
|
|
6001
|
+
const capRaw = await readFile(resolve(workflowsDir, "capabilities.yaml"), "utf8");
|
|
6002
|
+
const capDoc = parse(capRaw);
|
|
6003
|
+
const caps = capDoc?.capabilities ?? {};
|
|
6004
|
+
const lines = [];
|
|
6005
|
+
for (const tool of tools) {
|
|
6006
|
+
const cmd = caps[tool]?.cmd;
|
|
6007
|
+
const impl = caps[tool]?.impl;
|
|
6008
|
+
lines.push(cmd ? `- Invoke \`${cmd}\` (${tool}${impl ? `, ${impl}` : ""})` : `- ${tool}`);
|
|
6009
|
+
}
|
|
6010
|
+
if (lines.length === 0) return "";
|
|
6011
|
+
return `
|
|
6012
|
+
## Tools \u2014 invoke these (not optional)
|
|
6013
|
+
This workflow's SoT declares the following upstream tools. Actually invoke them as part of your work \u2014 do NOT improvise a lightweight substitute. Persist artifacts in the upstream's native format (e.g. planning-with-files \u2192 \`task_plan.md\` / \`progress.md\` / \`findings.md\`; GSD \u2192 \`PLAN.md\` / \`STATE.md\`).
|
|
6014
|
+
${lines.join("\n")}
|
|
6015
|
+
`;
|
|
6016
|
+
} catch {
|
|
6017
|
+
return "";
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
async function buildDisciplinesSection(sub, packageRoot) {
|
|
6021
|
+
try {
|
|
6022
|
+
const workflowsDir = resolve(packageRoot, "workflows");
|
|
6023
|
+
const subYaml = await resolveWorkflowYaml(sub, workflowsDir);
|
|
6024
|
+
if (!subYaml) return "";
|
|
6025
|
+
const wfRaw = await readFile(subYaml, "utf8");
|
|
6026
|
+
const wf = parse(wfRaw);
|
|
6027
|
+
const applied = Array.isArray(wf?.disciplines_applied) ? wf.disciplines_applied : [];
|
|
6028
|
+
const names = applied.filter((d) => d !== "language");
|
|
6029
|
+
if (names.length === 0) return "";
|
|
6030
|
+
const blocks = [];
|
|
6031
|
+
for (const name of names) {
|
|
6032
|
+
try {
|
|
6033
|
+
const dRaw = await readFile(resolve(workflowsDir, "disciplines", `${name}.yaml`), "utf8");
|
|
6034
|
+
const dDoc = parse(dRaw);
|
|
6035
|
+
const rules = Array.isArray(dDoc?.rules) ? dDoc.rules : [];
|
|
6036
|
+
const descs = rules.map((r) => typeof r.description === "string" ? r.description.trim() : "").filter((s) => s.length > 0).map((s) => ` - ${s.replace(/\s+/g, " ")}`);
|
|
6037
|
+
if (descs.length > 0) blocks.push(`- **${name}**:
|
|
6038
|
+
${descs.join("\n")}`);
|
|
6039
|
+
} catch {
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
if (blocks.length === 0) return "";
|
|
6043
|
+
return `
|
|
6044
|
+
## Disciplines (always-on \u2014 L0 substrate)
|
|
6045
|
+
Follow these behavioral disciplines while doing the work:
|
|
6046
|
+
${blocks.join("\n")}
|
|
6047
|
+
`;
|
|
6048
|
+
} catch {
|
|
6049
|
+
return "";
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
6052
|
+
function buildLanguageSection() {
|
|
6053
|
+
const code = process.env.HARNESSED_USER_LANG;
|
|
6054
|
+
if (!code) return "";
|
|
6055
|
+
const name = LANG_NAMES[code] ?? code;
|
|
6056
|
+
return `
|
|
6057
|
+
## Language
|
|
6058
|
+
Respond in ${name}. Keep code, commands, file/identifier/API names, error messages, stack traces, URLs, commit hashes, and version numbers in their original form (do not translate or transliterate them).
|
|
6059
|
+
`;
|
|
6060
|
+
}
|
|
6061
|
+
var PROTOCOLS = `
|
|
6062
|
+
## Completion protocol
|
|
6063
|
+
When done emit: <promise>COMPLETE</promise>
|
|
6064
|
+
|
|
6065
|
+
## Clarification protocol
|
|
6066
|
+
If you hit a gray area you cannot decide (\u22652 reasonable options, missing key context, ambiguous requirement), do NOT self-decide. Emit:
|
|
6067
|
+
STATUS: NEEDS_CLARIFICATION
|
|
6068
|
+
1. <question>
|
|
6069
|
+
2. <question>
|
|
6070
|
+
The main session will relay these to the user and re-spawn you with answers.
|
|
6071
|
+
`;
|
|
6072
|
+
async function resolveMaxIterations2(sub, packageRoot) {
|
|
6073
|
+
try {
|
|
6074
|
+
const path = resolve(packageRoot, "workflows", "defaults.yaml");
|
|
6075
|
+
const raw = await readFile(path, "utf8");
|
|
6076
|
+
const doc = parse(raw);
|
|
6077
|
+
const entry = doc?.ralph_max_iterations?.[sub];
|
|
6078
|
+
if (typeof entry === "number" && Number.isFinite(entry)) {
|
|
6079
|
+
return entry;
|
|
6080
|
+
}
|
|
6081
|
+
if (entry && typeof entry === "object") {
|
|
6082
|
+
for (const v of Object.values(entry)) {
|
|
6083
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
6084
|
+
return v;
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
return DEFAULT_MAX_ITERATIONS;
|
|
6089
|
+
} catch {
|
|
6090
|
+
return DEFAULT_MAX_ITERATIONS;
|
|
6091
|
+
}
|
|
6092
|
+
}
|
|
6093
|
+
function registerPrompt(program2) {
|
|
6094
|
+
program2.command("prompt").description(
|
|
6095
|
+
"Print a spawn-ready prompt for a sub-workflow (role + checklist + protocols, no spawn)."
|
|
6096
|
+
).argument("<sub>", "sub-workflow name (e.g. task-code, verify-paranoid)").option("--task <text>", "task description prepended as a ## Task section").option("--json", "emit JSON {prompt, max_iterations, model, specialist} instead of text").action(async (sub, raw) => {
|
|
6097
|
+
const packageRoot = getPackageRoot();
|
|
6098
|
+
const workflowsDir = resolve(packageRoot, "workflows");
|
|
6099
|
+
const rolePrompts = await loadRolePrompts(workflowsDir);
|
|
6100
|
+
const def = buildAgentDef(sub, rolePrompts, void 0, void 0, void 0);
|
|
6101
|
+
const body = def.prompt;
|
|
6102
|
+
const taskSection = typeof raw.task === "string" && raw.task.length > 0 ? `## Task
|
|
6103
|
+
${raw.task}
|
|
6104
|
+
|
|
6105
|
+
` : "";
|
|
6106
|
+
const toolsSection = await buildToolsSection(sub, packageRoot);
|
|
6107
|
+
const disciplinesSection = await buildDisciplinesSection(sub, packageRoot);
|
|
6108
|
+
const fullPrompt = `${taskSection}${body}
|
|
6109
|
+
${toolsSection}${disciplinesSection}${PROTOCOLS}${buildLanguageSection()}`;
|
|
6110
|
+
if (raw.json) {
|
|
6111
|
+
const maxIterations = await resolveMaxIterations2(sub, packageRoot);
|
|
6112
|
+
const rp = rolePrompts[sub];
|
|
6113
|
+
const model = def.model ?? DEFAULT_MODEL;
|
|
6114
|
+
const specialist = rp?.specialist ?? DEFAULT_SPECIALIST;
|
|
6115
|
+
console.log(
|
|
6116
|
+
JSON.stringify({
|
|
6117
|
+
prompt: fullPrompt,
|
|
6118
|
+
max_iterations: maxIterations,
|
|
6119
|
+
model,
|
|
6120
|
+
specialist
|
|
6121
|
+
})
|
|
6122
|
+
);
|
|
6123
|
+
process.exit(0);
|
|
6124
|
+
return;
|
|
6125
|
+
}
|
|
6126
|
+
console.log(fullPrompt);
|
|
6127
|
+
process.exit(0);
|
|
6128
|
+
});
|
|
6129
|
+
}
|
|
6064
6130
|
var PACKAGE_ROOT2 = getPackageRoot();
|
|
6065
6131
|
var WORKFLOWS_DIR2 = join(PACKAGE_ROOT2, "workflows");
|
|
6066
6132
|
function registerResearch(program2) {
|