harnessed 3.9.25 → 3.9.26

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 CHANGED
@@ -1231,7 +1231,7 @@ var init_auto_install = __esm({
1231
1231
 
1232
1232
  // package.json
1233
1233
  var package_default = {
1234
- version: "3.9.25"};
1234
+ version: "3.9.26"};
1235
1235
 
1236
1236
  // src/manifest/errors.ts
1237
1237
  function instancePathToKeyPath(instancePath) {
@@ -4027,11 +4027,91 @@ async function loadRolePrompts(workflowsDir) {
4027
4027
  const doc = parse(raw);
4028
4028
  return doc?.prompts ?? {};
4029
4029
  }
4030
- function generateCommandFile(name, prompt, _capabilities, _installedPlugins, _installedUserSkills) {
4031
- const isMaster = prompt.is_master === true;
4032
- const argHint = isMaster ? "[task description]" : "[requirement text or omit]";
4030
+ var INTERACTIVE_COMMANDS = /* @__PURE__ */ new Set([
4031
+ "discuss",
4032
+ "discuss-strategic",
4033
+ "discuss-phase",
4034
+ "discuss-subtask",
4035
+ "task-clarify"
4036
+ ]);
4037
+ var HYBRID_COMMANDS = /* @__PURE__ */ new Set(["auto", "task"]);
4038
+ var MARKER = `<!-- harnessed-generated:v3.4.4 -->`;
4039
+ function buildInteractiveBody(name, prompt) {
4040
+ return [
4041
+ `# /${name}`,
4042
+ ``,
4043
+ prompt.description,
4044
+ ``,
4045
+ `## How to run (interactive \u2014 in THIS session)`,
4046
+ ``,
4047
+ `Clarification requires real user dialogue. Spawned subagents cannot ask the user questions, so run this stage directly in this session \u2014 do NOT spawn \`harnessed run ${name}\`.`,
4048
+ ``,
4049
+ `1. Evaluate the clarification criteria for "$ARGUMENTS":`,
4050
+ ` - Strategic layer: new feature / new milestone / unclear business scope \u2192 run gstack \`/office-hours\` + \`/plan-ceo-review\``,
4051
+ ` - Phase layer: \u22652 open implementation decisions / unclear cross-phase API contract \u2192 run GSD \`/gsd-discuss-phase\``,
4052
+ ` - Subtask layer: \u22652 distinct approaches / core algorithm / API contract design / high error cost \u2192 run superpowers brainstorming`,
4053
+ `2. For each layer that fires, hold the dialogue with the user (use AskUserQuestion for option-style decisions). Lock every open decision.`,
4054
+ `3. Skip layers that don't fire \u2014 state which were skipped and why (transparent skip).`,
4055
+ `4. Persist locked decisions to \`.planning/\` via planning-with-files (\`findings.md\` / \`task_plan.md\`).`,
4056
+ ``,
4057
+ `Output: a locked spec the execution stages can consume without further user input.`,
4058
+ ``,
4059
+ `## Notes`,
4060
+ ``,
4061
+ `- This file is generated by \`harnessed setup\`. Re-run \`harnessed setup\` after a harnessed upgrade to refresh.`,
4062
+ `- The downstream execution command (\`/plan\` \u2192 \`/task\` \u2192 \`/verify\`) embeds the locked spec in its task text.`,
4063
+ ``,
4064
+ MARKER,
4065
+ ``
4066
+ ].join("\n");
4067
+ }
4068
+ function buildHybridBody(name, prompt) {
4069
+ const chain = name === "auto" ? [
4070
+ `3. Then run the execution stages in order via the Bash tool (each stage's output informs the next):`,
4071
+ ``,
4072
+ "```bash",
4073
+ `echo "<locked spec + $ARGUMENTS>" | harnessed run plan --task-stdin`,
4074
+ `echo "<locked spec + plan output>" | harnessed run task --task-stdin --skip-sub clarify`,
4075
+ `echo "<locked spec + task output>" | harnessed run verify --task-stdin`,
4076
+ `echo "<summary of all stages>" | harnessed run retro --task-stdin`,
4077
+ "```",
4078
+ ``,
4079
+ ` Do NOT run \`harnessed run auto\` \u2014 that would re-spawn the discuss stage headlessly, defeating the interactive clarification you just did.`,
4080
+ ` \`--skip-sub clarify\` tells the task master that clarification is already done.`
4081
+ ] : [
4082
+ `3. Then spawn the execution subs via the Bash tool:`,
4083
+ ``,
4084
+ "```bash",
4085
+ `echo "<locked spec + $ARGUMENTS>" | harnessed run task --task-stdin --skip-sub clarify`,
4086
+ "```",
4087
+ ``,
4088
+ ` \`--skip-sub clarify\` tells the task master that clarification is already done in this session.`
4089
+ ];
4090
+ return [
4091
+ `# /${name}`,
4092
+ ``,
4093
+ prompt.description,
4094
+ ``,
4095
+ `## How to run (hybrid \u2014 clarify in THIS session, then spawn execution)`,
4096
+ ``,
4097
+ `1. FIRST clarify interactively in this session (spawned subagents cannot ask the user questions):`,
4098
+ ` - Evaluate the subtask clarification criteria for "$ARGUMENTS": \u22652 distinct approaches / core algorithm / API contract design / high error cost \u2192 brainstorm with the user (AskUserQuestion for option-style decisions) and lock every open decision.`,
4099
+ ` - If criteria don't fire (CRUD / known pattern / single obvious implementation), skip with a one-line transparent declaration.`,
4100
+ `2. Embed the locked decisions into the task text passed downstream.`,
4101
+ ...chain,
4102
+ ``,
4103
+ `## Notes`,
4104
+ ``,
4105
+ `- This file is generated by \`harnessed setup\`. Re-run \`harnessed setup\` after a harnessed upgrade to refresh.`,
4106
+ `- Execution stages (code / test / deliver / verify) spawn headless subagents \u2014 appropriate since they need no user dialogue.`,
4107
+ ``,
4108
+ MARKER,
4109
+ ``
4110
+ ].join("\n");
4111
+ }
4112
+ function buildSpawnBody(name, prompt) {
4033
4113
  const stagedNote = name === "auto" ? "\n- For stage-by-stage review, append `--staged` (pauses between stages for user review)." : "";
4034
- const body = [
4114
+ return [
4035
4115
  `# /${name}`,
4036
4116
  ``,
4037
4117
  prompt.description,
@@ -4054,9 +4134,21 @@ function generateCommandFile(name, prompt, _capabilities, _installedPlugins, _in
4054
4134
  `- The sister \`~/.claude/skills/${name}/SKILL.md\` is the Skill-tool entry point (Claude loads it when triggers match \`trigger_phrases:\`). Both files invoke the same \`harnessed run ${name}\` Bash command.${stagedNote}`,
4055
4135
  `- Workflow runtime: \`src/workflow/run.ts\` walks \`workflows/${nameToYamlHintPath(name)}\` with disciplines + judgments + master orchestration applied per the yaml \`delegates_to[]\` + \`gate\` clauses.`,
4056
4136
  ``,
4057
- `<!-- harnessed-generated:v3.4.4 -->`,
4137
+ MARKER,
4058
4138
  ``
4059
4139
  ].join("\n");
4140
+ }
4141
+ function generateCommandFile(name, prompt, _capabilities, _installedPlugins, _installedUserSkills) {
4142
+ const isMaster = prompt.is_master === true;
4143
+ const argHint = isMaster ? "[task description]" : "[requirement text or omit]";
4144
+ let body;
4145
+ if (INTERACTIVE_COMMANDS.has(name)) {
4146
+ body = buildInteractiveBody(name, prompt);
4147
+ } else if (HYBRID_COMMANDS.has(name)) {
4148
+ body = buildHybridBody(name, prompt);
4149
+ } else {
4150
+ body = buildSpawnBody(name, prompt);
4151
+ }
4060
4152
  const warnings = [];
4061
4153
  const frontmatter = [
4062
4154
  "---",
@@ -5042,8 +5134,17 @@ async function runMasterOrchestrator(masterName, context, packageRoot, spawnDriv
5042
5134
  }
5043
5135
  effectiveOpts = pre.opts;
5044
5136
  }
5137
+ const skipSubs = new Set(Array.isArray(context.skip_subs) ? context.skip_subs : []);
5045
5138
  const gateEvalled = [];
5046
5139
  for (const clause of master.delegates_to) {
5140
+ if (skipSubs.has(clause.sub)) {
5141
+ gateEvalled.push({
5142
+ clause,
5143
+ passes: false,
5144
+ reason: `skipped via skip_subs (done interactively in main session)`
5145
+ });
5146
+ continue;
5147
+ }
5047
5148
  if (!clause.gate) {
5048
5149
  gateEvalled.push({ clause, passes: true });
5049
5150
  continue;
@@ -5449,7 +5550,10 @@ function registerRun(program2) {
5449
5550
  ).option("--model <model>", "subagent model: 'haiku' | 'sonnet' | 'opus'").option(
5450
5551
  "--dry-run",
5451
5552
  "validate yaml load + walk runtime without spawning (Phase 1 default for verification)"
5452
- ).option("--staged", "/auto super-master: pause between stages for user review").option("--list", "print all known workflow names and exit").action(async (name, raw) => {
5553
+ ).option("--staged", "/auto super-master: pause between stages for user review").option(
5554
+ "--skip-sub <names>",
5555
+ "comma-separated sub names to skip (work already done interactively in main session, e.g. clarify)"
5556
+ ).option("--list", "print all known workflow names and exit").action(async (name, raw) => {
5453
5557
  if (raw.list) {
5454
5558
  const names = await listWorkflowNames(WORKFLOWS_DIR);
5455
5559
  for (const n of names) console.log(n);
@@ -5541,6 +5645,10 @@ function registerRun(program2) {
5541
5645
  ...raw.model ? { modelOverride: raw.model } : {},
5542
5646
  ...raw.maxIterations ? { maxIterations: raw.maxIterations } : {},
5543
5647
  ...raw.staged ? { staged: true } : {},
5648
+ // v3.9.26 Option A — skip subs done interactively in main session.
5649
+ ...typeof raw.skipSub === "string" && raw.skipSub.length > 0 ? {
5650
+ skip_subs: raw.skipSub.split(",").map((s) => s.trim()).filter((s) => s.length > 0)
5651
+ } : {},
5544
5652
  ...matchedTriggers.length > 0 ? { user_overrides: matchedTriggers } : {}
5545
5653
  };
5546
5654
  if (raw.dryRun) {
@@ -6279,20 +6387,19 @@ function printGrouped(b, prefix = "") {
6279
6387
  const GROUP_ORDER = ["mcp-tool", "command", "cli-binary", "other"];
6280
6388
  const groupOf = (n) => b.componentTypes[n] ?? "other";
6281
6389
  const buckets = {};
6390
+ const push = (group, entry) => {
6391
+ if (!buckets[group]) buckets[group] = [];
6392
+ buckets[group].push(entry);
6393
+ };
6282
6394
  for (const n of b.failed) {
6283
6395
  const m = n.match(/^([^:]+):/);
6284
6396
  const baseName = m?.[1] ?? n;
6285
- (buckets[groupOf(baseName)] ??= []).push({ status: "failed", name: n });
6397
+ push(groupOf(baseName), { status: "failed", name: n });
6286
6398
  }
6287
- for (const n of b.installed) (buckets[groupOf(n)] ??= []).push({ status: "installed", name: n });
6399
+ for (const n of b.installed) push(groupOf(n), { status: "installed", name: n });
6288
6400
  for (const s of b.skipped)
6289
- (buckets[groupOf(s.name)] ??= []).push({
6290
- status: "skipped",
6291
- name: s.name,
6292
- suffix: ` \u2014 ${s.reason}`
6293
- });
6294
- for (const n of b.alreadyInstalled)
6295
- (buckets[groupOf(n)] ??= []).push({ status: "already-installed", name: n });
6401
+ push(groupOf(s.name), { status: "skipped", name: s.name, suffix: ` \u2014 ${s.reason}` });
6402
+ for (const n of b.alreadyInstalled) push(groupOf(n), { status: "already-installed", name: n });
6296
6403
  for (const g of GROUP_ORDER) {
6297
6404
  const entries = buckets[g];
6298
6405
  if (!entries || entries.length === 0) continue;
@@ -6788,7 +6895,8 @@ async function removeSettingsEnv(settingsPath3) {
6788
6895
  if (!changed) return false;
6789
6896
  if (Object.keys(env).length === 0) delete data.env;
6790
6897
  else data.env = env;
6791
- await writeFile(settingsPath3, JSON.stringify(data, null, 2) + "\n", "utf8");
6898
+ await writeFile(settingsPath3, `${JSON.stringify(data, null, 2)}
6899
+ `, "utf8");
6792
6900
  return true;
6793
6901
  }
6794
6902
  async function runUnifiedUninstall(home, dryRun) {