hatch3r 2.0.0 → 2.1.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/cli/index.js CHANGED
@@ -18,15 +18,15 @@ var __export = (target, all) => {
18
18
  // src/version.ts
19
19
  function resolveVersion() {
20
20
  try {
21
- return "2.0.0";
21
+ return "2.1.0";
22
22
  } catch (defineMissing) {
23
23
  void defineMissing;
24
24
  try {
25
25
  const { readFileSync: readFileSync5 } = __require("fs");
26
26
  const { fileURLToPath: fileURLToPath11 } = __require("url");
27
- const { dirname: dirname28, resolve: resolve9 } = __require("path");
27
+ const { dirname: dirname28, resolve: resolve10 } = __require("path");
28
28
  const here = fileURLToPath11(import.meta.url);
29
- const pkgPath = resolve9(dirname28(here), "..", "package.json");
29
+ const pkgPath = resolve10(dirname28(here), "..", "package.json");
30
30
  const raw = readFileSync5(pkgPath, "utf8");
31
31
  const parsed = JSON.parse(raw);
32
32
  return parsed.version ?? "0.0.0-dev";
@@ -7078,6 +7078,7 @@ init_managedBlocks();
7078
7078
  init_types();
7079
7079
  import { readFile as readFile15, readdir as readdir10 } from "fs/promises";
7080
7080
  import { join as join21 } from "path";
7081
+ import { parse as parseYaml4 } from "yaml";
7081
7082
 
7082
7083
  // src/models/aliases.ts
7083
7084
  var MODEL_ALIASES = {
@@ -10009,6 +10010,18 @@ function output(path, content, managedContent, sourceFiles) {
10009
10010
  function defaultModelFormat(model) {
10010
10011
  return { text: `**Recommended model:** \`${model}\`` };
10011
10012
  }
10013
+ function toYamlDoubleQuotedScalar(value) {
10014
+ const singleLine = value.replace(/[\r\n]+/g, " ");
10015
+ const escaped = singleLine.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
10016
+ return `"${escaped}"`;
10017
+ }
10018
+ function extractArgumentHint(rawContent) {
10019
+ const match = rawContent.match(/^---\n([\s\S]*?)\n---/);
10020
+ if (!match) return void 0;
10021
+ const parsed = parseYaml4(match[1]);
10022
+ const hint = parsed?.["argument-hint"];
10023
+ return typeof hint === "string" && hint.trim().length > 0 ? hint : void 0;
10024
+ }
10012
10025
  var BaseAdapter = class _BaseAdapter {
10013
10026
  /**
10014
10027
  * Per-invocation diagnostics surfaced from canonical reads, customization
@@ -10407,7 +10420,19 @@ var BaseAdapter = class _BaseAdapter {
10407
10420
  }
10408
10421
  return results;
10409
10422
  }
10410
- /** Process skills and output each with YAML frontmatter (name, description) at the path returned by `pathFn`. */
10423
+ /**
10424
+ * Process skills and output each with YAML frontmatter (`name`,
10425
+ * `description`) at the path returned by `pathFn`.
10426
+ *
10427
+ * The `description:` is rendered as a YAML double-quoted scalar via
10428
+ * {@link toYamlDoubleQuotedScalar} — the same treatment {@link processCommandsWithFm}
10429
+ * applies to command descriptions. A skill description carrying `: ` (or a
10430
+ * leading YAML indicator / `"` / `#`) would otherwise make the byte-0
10431
+ * `description:` line an invalid plain scalar, and the `/` picker in Claude
10432
+ * Code / Cursor / Copilot would fall back to showing the `HATCH3R:BEGIN`
10433
+ * managed-block marker. `name` is the canonical id (a `hatch3r-` slug with no
10434
+ * quoting hazard) and stays unquoted, mirroring the command helper.
10435
+ */
10411
10436
  async processSkillsWithFm(ctx, pathFn) {
10412
10437
  if (!ctx.features.skills) return [];
10413
10438
  const results = [];
@@ -10421,7 +10446,7 @@ var BaseAdapter = class _BaseAdapter {
10421
10446
  const desc = overrides.description ?? skill.description;
10422
10447
  const fm = `---
10423
10448
  name: ${skill.id}
10424
- description: ${desc}
10449
+ description: ${toYamlDoubleQuotedScalar(desc)}
10425
10450
  ---`;
10426
10451
  results.push(output(pathFn(skill.id), `${fm}
10427
10452
 
@@ -10505,7 +10530,7 @@ ${wrapManagedFor(pathFn(skill.id), content)}`, content, this.singleSource(skill)
10505
10530
  if (skip) continue;
10506
10531
  const content = this.substituteCanonicalContent(raw, ctx);
10507
10532
  const desc = overrides.description ?? skill.description;
10508
- const fmLines = [`name: ${skill.id}`, `description: ${desc}`];
10533
+ const fmLines = [`name: ${skill.id}`, `description: ${toYamlDoubleQuotedScalar(desc)}`];
10509
10534
  if (opts?.emitAllowedTools && skill.allowedTools && skill.allowedTools.length > 0) {
10510
10535
  fmLines.push(`allowed-tools: [${skill.allowedTools.map((t) => `"${t}"`).join(", ")}]`);
10511
10536
  }
@@ -10533,6 +10558,57 @@ ${wrapManagedFor(pathFn(skill.id), content)}`, content, this.singleSource(skill)
10533
10558
  }
10534
10559
  return results;
10535
10560
  }
10561
+ /**
10562
+ * Process commands and output each with YAML frontmatter (`name`,
10563
+ * `description`) prepended before the managed block, at the path returned by
10564
+ * `pathFn`.
10565
+ *
10566
+ * Twin of {@link processCommandsRaw} (same user-facing canonical read,
10567
+ * customization, token substitution, feature gate, and warnings handling) —
10568
+ * and the command analogue of {@link processSkillsWithFm} — but emits a
10569
+ * `---\nname:\ndescription:\n---` stub at byte 0 so the slash-command picker
10570
+ * in Claude Code / Cursor / Copilot shows the real description instead of the
10571
+ * `HATCH3R:BEGIN` managed-block marker (the picker reads `description:` at
10572
+ * byte 0). It uses {@link applyCustomization} (frontmatter-stripped body),
10573
+ * not {@link applyCustomizationRaw} (full file), so the canonical frontmatter
10574
+ * is not re-emitted inside the managed block. The description (and any
10575
+ * `argument-hint`) is rendered as a YAML double-quoted scalar via
10576
+ * {@link toYamlDoubleQuotedScalar} because command descriptions routinely
10577
+ * carry `: ` / `--`, and `argument-hint` values such as `[service-name]`
10578
+ * would otherwise parse as a YAML flow sequence, either of which breaks an
10579
+ * unquoted plain scalar.
10580
+ *
10581
+ * The canonical `argument-hint` field (when present) is re-emitted in the
10582
+ * stub so the picker's argument affordance survives at byte 0 (D5-33) — the
10583
+ * pre-fix `processCommandsRaw` carried it only below the marker where the
10584
+ * picker could not read it. Other canonical command frontmatter (governance
10585
+ * metadata like `orchestrator`/`agentPipeline`/`triage_tiers`) is
10586
+ * intentionally NOT hoisted: it is hatch3r-authoring data with no runtime or
10587
+ * picker consumer.
10588
+ */
10589
+ async processCommandsWithFm(ctx, pathFn) {
10590
+ if (!ctx.features.commands) return [];
10591
+ const results = [];
10592
+ const commands = await this.readUserFacingCanonicalFiles(ctx.canonicalRoot, "commands", ctx.userRepoRoot);
10593
+ for (const cmd of commands) {
10594
+ this.throwIfAborted(ctx);
10595
+ const { content: raw, skip, overrides, warnings } = await applyCustomization(ctx.projectRoot, cmd);
10596
+ this.warnings.push(...warnings);
10597
+ if (skip) continue;
10598
+ const content = this.substituteCanonicalContent(raw, ctx);
10599
+ const desc = overrides.description ?? cmd.description;
10600
+ const fmLines = [`name: ${cmd.id}`, `description: ${toYamlDoubleQuotedScalar(desc)}`];
10601
+ const argumentHint = extractArgumentHint(cmd.rawContent);
10602
+ if (argumentHint) fmLines.push(`argument-hint: ${toYamlDoubleQuotedScalar(argumentHint)}`);
10603
+ const fm = `---
10604
+ ${fmLines.join("\n")}
10605
+ ---`;
10606
+ results.push(output(pathFn(cmd.id), `${fm}
10607
+
10608
+ ${wrapManagedFor(pathFn(cmd.id), content)}`, content, this.singleSource(cmd)));
10609
+ }
10610
+ return results;
10611
+ }
10536
10612
  /**
10537
10613
  * Emit companion/reference content from a canonical subdirectory.
10538
10614
  *
@@ -10848,9 +10924,12 @@ ${CACHE_BREAKPOINT_SENTINEL_END}`;
10848
10924
  function rewrapWithCacheBreakpoints(out) {
10849
10925
  if (!out.managedContent) return out;
10850
10926
  const wrappedBody = withCacheBreakpoints(out.managedContent);
10927
+ const originalBlock = wrapManagedFor(out.path, out.managedContent);
10928
+ const blockIdx = out.content.indexOf(originalBlock);
10929
+ const prefix = blockIdx > 0 ? out.content.slice(0, blockIdx) : "";
10851
10930
  return {
10852
10931
  ...out,
10853
- content: wrapManagedFor(out.path, wrappedBody),
10932
+ content: `${prefix}${wrapManagedFor(out.path, wrappedBody)}`,
10854
10933
  managedContent: wrappedBody
10855
10934
  };
10856
10935
  }
@@ -10945,6 +11024,7 @@ var AGENT_TEAMS_SECTION_MINIMAL = [
10945
11024
  "",
10946
11025
  "Use `/hatch3r-agent-team` for guided team creation."
10947
11026
  ];
11027
+ var AGENT_TEAM_DESCRIPTION = "Launch a Claude Code Agent Team that runs the hatch3r 4-phase pipeline \u2014 Research, Implement, Review, then parallel Quality gates \u2014 across coordinating teammates.";
10948
11028
  var AGENT_TEAM_COMMAND = `# hatch3r Agent Team
10949
11029
 
10950
11030
  Create a Claude Code Agent Team that follows the hatch3r 4-phase pipeline.
@@ -11403,12 +11483,12 @@ ${wrapManagedFor(agentPath, body)}`, body, claudeSingleSource(agent)));
11403
11483
  ".claude/hooks/pretooluse-allowlist.mjs",
11404
11484
  buildClaudePreToolUseHookScript()
11405
11485
  ));
11406
- const skillOutputs = await this.processSkillsRawCliFiltered(
11486
+ const skillOutputs = await this.processSkillsWithFmCliFiltered(
11407
11487
  ctx,
11408
11488
  (id) => `.claude/skills/${toPrefixedId(id)}/SKILL.md`
11409
11489
  );
11410
11490
  results.push(...skillOutputs.map(rewrapWithCacheBreakpoints));
11411
- const commandOutputs = await this.processCommandsRaw(
11491
+ const commandOutputs = await this.processCommandsWithFm(
11412
11492
  ctx,
11413
11493
  (id) => `.claude/commands/${toPrefixedId(id)}.md`
11414
11494
  );
@@ -11448,7 +11528,18 @@ ${wrapManagedFor(agentPath, body)}`, body, claudeSingleSource(agent)));
11448
11528
  );
11449
11529
  }
11450
11530
  const agentTeamBody = withCacheBreakpoints(AGENT_TEAM_COMMAND);
11451
- results.push(output(".claude/commands/hatch3r-agent-team.md", wrapManagedFor(".claude/commands/hatch3r-agent-team.md", agentTeamBody), agentTeamBody));
11531
+ const agentTeamPath = ".claude/commands/hatch3r-agent-team.md";
11532
+ const agentTeamFm = `---
11533
+ name: hatch3r-agent-team
11534
+ description: "${AGENT_TEAM_DESCRIPTION}"
11535
+ ---`;
11536
+ results.push(output(
11537
+ agentTeamPath,
11538
+ `${agentTeamFm}
11539
+
11540
+ ${wrapManagedFor(agentTeamPath, agentTeamBody)}`,
11541
+ agentTeamBody
11542
+ ));
11452
11543
  return results;
11453
11544
  }
11454
11545
  };
@@ -11714,7 +11805,7 @@ ${wrapManagedFor(agentPath, content)}`, content, copilotSingleSource(agent)));
11714
11805
  }
11715
11806
  }
11716
11807
  results.push(
11717
- ...await this.processCommandsRaw(ctx, (id) => `.github/prompts/${toPrefixedId(id)}.prompt.md`)
11808
+ ...await this.processCommandsWithFm(ctx, (id) => `.github/prompts/${toPrefixedId(id)}.prompt.md`)
11718
11809
  );
11719
11810
  if (ctx.features.githubAgents && !ctx.features.agents) {
11720
11811
  const ghAgents = await this.readTrackedCanonicalFiles(ctx.canonicalRoot, "github-agents", ctx.userRepoRoot);
@@ -11883,7 +11974,7 @@ ${lines.join("\n")}
11883
11974
  ...await this.processSkillsWithFmCliFiltered(ctx, (id) => `.cursor/skills/${toPrefixedId(id)}/SKILL.md`)
11884
11975
  );
11885
11976
  results.push(
11886
- ...await this.processCommandsRaw(ctx, (id) => `.cursor/commands/${toPrefixedId(id)}.md`)
11977
+ ...await this.processCommandsWithFm(ctx, (id) => `.cursor/commands/${toPrefixedId(id)}.md`)
11887
11978
  );
11888
11979
  const companionMappings = [
11889
11980
  ["agents/modes", ctx.features.agents, (f) => `.cursor/agents/modes/${f}`],
@@ -12528,7 +12619,7 @@ import { readFile as readFile28 } from "fs/promises";
12528
12619
  // src/detect/repoAnalyzer.ts
12529
12620
  import { access as access10, readFile as readFile19, readdir as readdir12, stat as stat8 } from "fs/promises";
12530
12621
  import { join as join29 } from "path";
12531
- import { parse as parseYaml4, YAMLParseError } from "yaml";
12622
+ import { parse as parseYaml5, YAMLParseError } from "yaml";
12532
12623
 
12533
12624
  // src/detect/conventionConflict.ts
12534
12625
  var LINTER_TOOL_DIMENSION = {
@@ -12808,7 +12899,7 @@ async function collectWorkspaceGlobs(rootDir) {
12808
12899
  return globs;
12809
12900
  }
12810
12901
  function parsePnpmWorkspacePackages(raw) {
12811
- const doc = parseYaml4(raw);
12902
+ const doc = parseYaml5(raw);
12812
12903
  if (!doc || typeof doc !== "object") return [];
12813
12904
  const packages = doc.packages;
12814
12905
  if (!Array.isArray(packages)) return [];
@@ -13591,7 +13682,13 @@ var FEATURE_CHOICES = [
13591
13682
  { name: "Commands", value: "commands" },
13592
13683
  { name: "MCP", value: "mcp" },
13593
13684
  { name: "Hooks", value: "hooks" },
13594
- { name: "GitHub agents", value: "githubAgents" }
13685
+ { name: "GitHub agents", value: "githubAgents" },
13686
+ // 2.1.0 (Task D): handoffs was a live control surface (DEFAULT_FEATURES.handoffs
13687
+ // = true, threaded into bridge-orchestration emission) but absent from the
13688
+ // picker, so the config feature-rebuild loop silently forced it false on every
13689
+ // run. Listed here (default checked) so it round-trips; the rebuild loop is
13690
+ // additionally hardened to preserve unlisted features (see config.ts).
13691
+ { name: "Handoffs", value: "handoffs" }
13595
13692
  ];
13596
13693
  var MCP_CHOICES = Object.entries(AVAILABLE_MCP_SERVERS).map(([id, meta]) => ({
13597
13694
  name: `${id}: ${meta.description}`,
@@ -14864,6 +14961,50 @@ function mcpServersStep(opts) {
14864
14961
  }
14865
14962
  };
14866
14963
  }
14964
+ function maturityStep(opts) {
14965
+ return {
14966
+ id: "maturity",
14967
+ async run(_state, previous) {
14968
+ const answer = await inquirer6.prompt([
14969
+ {
14970
+ type: "select",
14971
+ name: "maturity",
14972
+ message: opts.message,
14973
+ choices: [
14974
+ { name: "solo \u2014 individual / hobby; universal floor, warn-only gates", value: "solo" },
14975
+ { name: "team \u2014 shared repo; + duplication/design discipline, strict gates", value: "team" },
14976
+ { name: "scaleup \u2014 multi-team; + SLOs, tracing, performance budgets", value: "scaleup" },
14977
+ { name: "enterprise \u2014 regulated; + mutation/contract testing, AI evals, FinOps", value: "enterprise" }
14978
+ ],
14979
+ default: previous ?? opts.defaultMaturity
14980
+ }
14981
+ ]);
14982
+ return isBack(answer.maturity) ? BACK : answer.maturity;
14983
+ }
14984
+ };
14985
+ }
14986
+ function confidenceFloorStep(opts) {
14987
+ return {
14988
+ id: "confidenceFloor",
14989
+ async run(state, previous) {
14990
+ const resolvedDefault = typeof opts.defaultFloor === "function" ? opts.defaultFloor(state) : opts.defaultFloor;
14991
+ const answer = await inquirer6.prompt([
14992
+ {
14993
+ type: "select",
14994
+ name: "confidenceFloor",
14995
+ message: opts.message,
14996
+ choices: [
14997
+ { name: "any \u2014 second-pass only on a low-confidence reviewer finding", value: "any" },
14998
+ { name: "medium \u2014 second-pass on any low-confidence finding", value: "medium" },
14999
+ { name: "high \u2014 second-pass on non-high confidence + ASK on every low-confidence finding", value: "high" }
15000
+ ],
15001
+ default: previous ?? resolvedDefault
15002
+ }
15003
+ ]);
15004
+ return isBack(answer.confidenceFloor) ? BACK : answer.confidenceFloor;
15005
+ }
15006
+ };
15007
+ }
14867
15008
 
14868
15009
  // src/cliTools/detect.ts
14869
15010
  import { spawn } from "child_process";
@@ -14875,7 +15016,7 @@ async function probeBin(name) {
14875
15016
  if (!isSafeProbeName(name)) return "";
14876
15017
  const isWindows = process.platform === "win32";
14877
15018
  const [cmd, args] = isWindows ? ["where", [name]] : ["/bin/sh", ["-c", `command -v -- "${name}"`]];
14878
- return new Promise((resolve9) => {
15019
+ return new Promise((resolve10) => {
14879
15020
  let settled = false;
14880
15021
  let stdout = "";
14881
15022
  const child = spawn(cmd, args, {
@@ -14889,7 +15030,7 @@ async function probeBin(name) {
14889
15030
  child.kill("SIGKILL");
14890
15031
  } catch {
14891
15032
  }
14892
- resolve9("");
15033
+ resolve10("");
14893
15034
  }, PROBE_TIMEOUT_MS);
14894
15035
  child.stdout?.on("data", (chunk) => {
14895
15036
  stdout += typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -14898,18 +15039,18 @@ async function probeBin(name) {
14898
15039
  if (settled) return;
14899
15040
  settled = true;
14900
15041
  clearTimeout(timer);
14901
- resolve9("");
15042
+ resolve10("");
14902
15043
  });
14903
15044
  child.on("close", (code) => {
14904
15045
  if (settled) return;
14905
15046
  settled = true;
14906
15047
  clearTimeout(timer);
14907
15048
  if (code !== 0) {
14908
- resolve9("");
15049
+ resolve10("");
14909
15050
  return;
14910
15051
  }
14911
15052
  const first = stdout.split(/\r?\n/).map((s) => s.trim()).find((s) => s.length > 0) ?? "";
14912
- resolve9(first);
15053
+ resolve10(first);
14913
15054
  });
14914
15055
  });
14915
15056
  }
@@ -14918,7 +15059,7 @@ async function runExtensionProbe(binary, args) {
14918
15059
  for (const arg of args) {
14919
15060
  if (!isSafeProbeName(arg)) return "";
14920
15061
  }
14921
- return new Promise((resolve9) => {
15062
+ return new Promise((resolve10) => {
14922
15063
  let settled = false;
14923
15064
  let stdout = "";
14924
15065
  const child = spawn(binary, [...args], {
@@ -14932,7 +15073,7 @@ async function runExtensionProbe(binary, args) {
14932
15073
  child.kill("SIGKILL");
14933
15074
  } catch {
14934
15075
  }
14935
- resolve9("");
15076
+ resolve10("");
14936
15077
  }, PROBE_TIMEOUT_MS);
14937
15078
  child.stdout?.on("data", (chunk) => {
14938
15079
  stdout += typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -14941,17 +15082,17 @@ async function runExtensionProbe(binary, args) {
14941
15082
  if (settled) return;
14942
15083
  settled = true;
14943
15084
  clearTimeout(timer);
14944
- resolve9("");
15085
+ resolve10("");
14945
15086
  });
14946
15087
  child.on("close", (code) => {
14947
15088
  if (settled) return;
14948
15089
  settled = true;
14949
15090
  clearTimeout(timer);
14950
15091
  if (code !== 0) {
14951
- resolve9("");
15092
+ resolve10("");
14952
15093
  return;
14953
15094
  }
14954
- resolve9(stdout);
15095
+ resolve10(stdout);
14955
15096
  });
14956
15097
  });
14957
15098
  }
@@ -15723,7 +15864,7 @@ init_safeWrite();
15723
15864
  init_types();
15724
15865
  import { readFile as readFile22 } from "fs/promises";
15725
15866
  import { join as join33 } from "path";
15726
- import { parse as parseYaml5 } from "yaml";
15867
+ import { parse as parseYaml6 } from "yaml";
15727
15868
 
15728
15869
  // src/importers/shared.ts
15729
15870
  var FRONTMATTER_REGEX2 = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/;
@@ -15744,7 +15885,7 @@ function parseAwesomeCursorrules(rawContent) {
15744
15885
  const { frontmatterStr, body } = splitFrontmatter2(rawContent);
15745
15886
  let description = "";
15746
15887
  if (frontmatterStr != null) {
15747
- const parsed = parseYaml5(frontmatterStr);
15888
+ const parsed = parseYaml6(frontmatterStr);
15748
15889
  if (parsed && typeof parsed.description === "string") description = parsed.description;
15749
15890
  }
15750
15891
  const canonical = {
@@ -15780,7 +15921,7 @@ async function parseAwesomeCursorrulesFile(rootDir) {
15780
15921
  init_types();
15781
15922
  import { readFile as readFile23, readdir as readdir13 } from "fs/promises";
15782
15923
  import { join as join34 } from "path";
15783
- import { parse as parseYaml6 } from "yaml";
15924
+ import { parse as parseYaml7 } from "yaml";
15784
15925
  var COPILOT_IMPORT_PREFIX = `${HATCH3R_PREFIX}copilot-import-`;
15785
15926
  var COPILOT_LEGACY_ID = `${COPILOT_IMPORT_PREFIX}repo-instructions`;
15786
15927
  function normaliseApplyTo(applyTo) {
@@ -15797,7 +15938,7 @@ function parseCopilotInstruction(filename, rawContent) {
15797
15938
  const { frontmatterStr, body } = splitFrontmatter2(rawContent);
15798
15939
  let fm = {};
15799
15940
  if (frontmatterStr != null) {
15800
- const parsed = parseYaml6(frontmatterStr);
15941
+ const parsed = parseYaml7(frontmatterStr);
15801
15942
  if (parsed && typeof parsed === "object") {
15802
15943
  const next = {};
15803
15944
  if (typeof parsed.name === "string") next.name = parsed.name;
@@ -15828,7 +15969,7 @@ function parseCopilotLegacyInstructions(rawContent) {
15828
15969
  const { frontmatterStr, body } = splitFrontmatter2(rawContent);
15829
15970
  let description = "";
15830
15971
  if (frontmatterStr != null) {
15831
- const parsed = parseYaml6(frontmatterStr);
15972
+ const parsed = parseYaml7(frontmatterStr);
15832
15973
  if (parsed && typeof parsed.description === "string") description = parsed.description;
15833
15974
  }
15834
15975
  const canonical = {
@@ -15896,7 +16037,7 @@ init_types();
15896
16037
  init_safeWrite();
15897
16038
  import { readFile as readFile24, readdir as readdir14, mkdir as mkdir10 } from "fs/promises";
15898
16039
  import { join as join35 } from "path";
15899
- import { parse as parseYaml7, stringify as yamlStringify2 } from "yaml";
16040
+ import { parse as parseYaml8, stringify as yamlStringify2 } from "yaml";
15900
16041
  var FRONTMATTER_REGEX3 = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/;
15901
16042
  function slugifyCursorRuleId(filename) {
15902
16043
  const base = filename.replace(/\.mdc$/i, "");
@@ -15921,7 +16062,7 @@ function parseCursorRule(filename, rawContent) {
15921
16062
  let body;
15922
16063
  if (match) {
15923
16064
  const [, fmStr, bodyStr = ""] = match;
15924
- const parsed = parseYaml7(fmStr ?? "");
16065
+ const parsed = parseYaml8(fmStr ?? "");
15925
16066
  if (parsed && typeof parsed === "object") {
15926
16067
  const fm = {};
15927
16068
  if (typeof parsed.description === "string") fm.description = parsed.description;
@@ -15983,7 +16124,7 @@ async function parseCursorRulesDir(cursorDir) {
15983
16124
  init_types();
15984
16125
  import { readFile as readFile25, readdir as readdir15 } from "fs/promises";
15985
16126
  import { join as join36 } from "path";
15986
- import { parse as parseYaml8 } from "yaml";
16127
+ import { parse as parseYaml9 } from "yaml";
15987
16128
  var WINDSURF_IMPORT_PREFIX = `${HATCH3R_PREFIX}windsurf-import-`;
15988
16129
  var WINDSURF_LEGACY_ID = `${WINDSURF_IMPORT_PREFIX}windsurfrules`;
15989
16130
  var VALID_TRIGGERS = /* @__PURE__ */ new Set([
@@ -16014,7 +16155,7 @@ function parseWindsurfRule(filename, rawContent) {
16014
16155
  const { frontmatterStr, body } = splitFrontmatter2(rawContent);
16015
16156
  let fm = {};
16016
16157
  if (frontmatterStr != null) {
16017
- const parsed = parseYaml8(frontmatterStr);
16158
+ const parsed = parseYaml9(frontmatterStr);
16018
16159
  if (parsed && typeof parsed === "object") {
16019
16160
  const next = {};
16020
16161
  if (typeof parsed.trigger === "string" && VALID_TRIGGERS.has(parsed.trigger)) {
@@ -18243,6 +18384,13 @@ async function initCommand(opts = {}) {
18243
18384
  seededMaturityDefault,
18244
18385
  "maturity"
18245
18386
  );
18387
+ info(chalk7.dim(`Detected default branch: ${inferredDefaultBranch}`));
18388
+ info(
18389
+ chalk7.dim(
18390
+ opts.maturity === void 0 ? `Inferred maturity: ${inferredMaturity} (team size: ${inferredTeamSize})` : `Maturity: ${inferredMaturity} (from --maturity); team size: ${inferredTeamSize}`
18391
+ )
18392
+ );
18393
+ const promptMaturity = opts.maturity === void 0;
18246
18394
  const explicitCliTools = opts.noCliTools ? [] : resolveCliToolsFlag(opts.cliTools, repoInfo, detectedPlatform);
18247
18395
  const steps = [
18248
18396
  platformStep({
@@ -18274,6 +18422,17 @@ async function initCommand(opts = {}) {
18274
18422
  )
18275
18423
  )
18276
18424
  }),
18425
+ // C (2.1.0, P1): maturity prompt, seeded at the git-inferred tier. Always
18426
+ // shown on the interactive path; conditionally spread (matching the
18427
+ // cliToolsStep gate below) only so it is ABSENT from the array — not just
18428
+ // skipped — when `--maturity` resolved the tier from the flag. Decision 25
18429
+ // raised the interactive ceiling 5→6 to admit it; see `promptMaturity`.
18430
+ ...promptMaturity ? [
18431
+ maturityStep({
18432
+ message: "Project maturity (investment depth):",
18433
+ defaultMaturity: inferredMaturity
18434
+ })
18435
+ ] : [],
18277
18436
  customItemsStep({
18278
18437
  index: filterIndex,
18279
18438
  // D10-13: floor + protected rows are locked-on by the picker itself;
@@ -18307,7 +18466,7 @@ async function initCommand(opts = {}) {
18307
18466
  const defaultBranch = inferredDefaultBranch;
18308
18467
  const projectType = inferredProjectType;
18309
18468
  const teamSize = inferredTeamSize;
18310
- const maturity = inferredMaturity;
18469
+ const maturity = stepState.maturity ?? inferredMaturity;
18311
18470
  const selectedPreset = getPreset(stepState.preset);
18312
18471
  const customSelections = stepState.customItems;
18313
18472
  const tools = stepState.tools;
@@ -19860,7 +20019,7 @@ function defaultShouldRetry(err, _attempt) {
19860
20019
  const type = classifyFailure(err);
19861
20020
  return type === "transient";
19862
20021
  }
19863
- var realSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
20022
+ var realSleep = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
19864
20023
  function computeBackoffDelay(attempt, initialDelayMs, maxDelayMs, backoffFactor) {
19865
20024
  const exponent = Math.max(0, attempt - 1);
19866
20025
  const raw = initialDelayMs * Math.pow(backoffFactor, exponent);
@@ -20626,13 +20785,13 @@ async function persistLearning(targetPath, body, options = {}) {
20626
20785
  init_safeWrite();
20627
20786
  import { mkdir as mkdir15, readdir as readdir21, readFile as readFile32, stat as stat13, unlink as unlink6 } from "fs/promises";
20628
20787
  import { join as join47 } from "path";
20629
- import { parse as parseYaml10, stringify as stringifyYaml } from "yaml";
20788
+ import { parse as parseYaml11, stringify as stringifyYaml } from "yaml";
20630
20789
 
20631
20790
  // src/content/handoffs/validation.ts
20632
20791
  import { createHash as createHash8, randomBytes as randomBytes5 } from "crypto";
20633
20792
  import { readdir as readdir20, readFile as readFile31, stat as stat12 } from "fs/promises";
20634
20793
  import { join as join46 } from "path";
20635
- import { parse as parseYaml9 } from "yaml";
20794
+ import { parse as parseYaml10 } from "yaml";
20636
20795
  init_customization();
20637
20796
 
20638
20797
  // src/content/handoffs/schema.ts
@@ -20847,7 +21006,7 @@ async function loadHandoffFile(filePath) {
20847
21006
  let parsed;
20848
21007
  let parseError = null;
20849
21008
  try {
20850
- parsed = parseYaml9(yamlBlock);
21009
+ parsed = parseYaml10(yamlBlock);
20851
21010
  } catch (err) {
20852
21011
  parseError = `Handoff "${filePath}" has invalid YAML frontmatter: ${err.message}`;
20853
21012
  parsed = null;
@@ -20960,7 +21119,7 @@ function parseHandoffText(raw, filePath) {
20960
21119
  const body = afterFirst.slice(bodyStart);
20961
21120
  let parsed;
20962
21121
  try {
20963
- parsed = parseYaml10(yamlBlock);
21122
+ parsed = parseYaml11(yamlBlock);
20964
21123
  } catch (err) {
20965
21124
  console.error(
20966
21125
  `hatch3r: failed to parse handoff frontmatter at ${filePath}: ${err instanceof Error ? err.message : String(err)}`
@@ -22025,6 +22184,13 @@ function computeDiff(oldManifest, newTools, newFeatures, newMcp, newPlatform, ne
22025
22184
  function isDiffEmpty(diff) {
22026
22185
  return diff.addedTools.length === 0 && diff.removedTools.length === 0 && diff.addedMcp.length === 0 && diff.removedMcp.length === 0 && diff.enabledFeatures.length === 0 && diff.disabledFeatures.length === 0 && !diff.platformChanged && !diff.repoChanged && diff.addedContent.length === 0 && diff.removedContent.length === 0 && diff.addedCliTools.length === 0 && diff.removedCliTools.length === 0;
22027
22186
  }
22187
+ function rebuildFeaturesFromSelection(prev, selected, pickerVisible) {
22188
+ const features = { ...DEFAULT_FEATURES, ...prev };
22189
+ for (const k of pickerVisible) {
22190
+ features[k] = selected.includes(k);
22191
+ }
22192
+ return features;
22193
+ }
22028
22194
  function buildDiffSummaryLines(diff, platform, namespace, project, defaultBranch, currentBranch) {
22029
22195
  const lines = [];
22030
22196
  if (diff.addedTools.length > 0) {
@@ -22484,7 +22650,9 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22484
22650
  {
22485
22651
  id: "features",
22486
22652
  async run(_state, previous) {
22487
- const currentFeatureKeys = previous ?? Object.keys(DEFAULT_FEATURES).filter((k) => manifest.features[k]);
22653
+ const currentFeatureKeys = previous ?? Object.keys(DEFAULT_FEATURES).filter(
22654
+ (k) => manifest.features[k] ?? DEFAULT_FEATURES[k]
22655
+ );
22488
22656
  const featureAnswers = await inquirer11.prompt([
22489
22657
  {
22490
22658
  type: "checkbox",
@@ -22552,6 +22720,27 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22552
22720
  },
22553
22721
  extraSkip: () => !manifest.content,
22554
22722
  wslTheme
22723
+ }),
22724
+ // C (2.1.0, P1): interactive maturity surface — parity with the scalar
22725
+ // `config maturity=<tier>` form. Default = the persisted tier
22726
+ // (readMaturityTier resolves absence to "solo").
22727
+ maturityStep({
22728
+ message: "Maturity tier (investment depth):",
22729
+ defaultMaturity: readMaturityTier(manifest)
22730
+ }),
22731
+ // E (2.1.0, P1): interactive confidence_floor surface — parity with the
22732
+ // scalar `config confidence_floor=<v>` form. The default is resolved AT
22733
+ // PROMPT TIME from the in-run maturity (`state.maturity`, set by the
22734
+ // maturity step sequenced just above) rather than the pre-mutation
22735
+ // manifest, so a solo→scaleup bump made earlier in this same run seeds the
22736
+ // tier-correct floor ("high") instead of the stale solo default ("any").
22737
+ // readConfidenceFloor keeps an explicit persisted floor winning; only the
22738
+ // tier-derived fallback follows the new maturity. Without this, accepting
22739
+ // the seeded default after a maturity bump would persist a floor that
22740
+ // contradicts the chosen tier (Cursor Bugbot — floor pinned on tier bump).
22741
+ confidenceFloorStep({
22742
+ message: "Agent assertiveness floor (review/ASK gate):",
22743
+ defaultFloor: (state) => readConfidenceFloor({ ...manifest, maturity: state.maturity ?? manifest.maturity })
22555
22744
  })
22556
22745
  ];
22557
22746
  const stepState = await runStepMachine(steps);
@@ -22559,6 +22748,10 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22559
22748
  const { owner, repo, namespace, project } = stepState.identity;
22560
22749
  const defaultBranch = stepState.defaultBranch;
22561
22750
  const tools = stepState.tools;
22751
+ const selectedMaturity = stepState.maturity;
22752
+ const selectedConfidenceFloor = stepState.confidenceFloor;
22753
+ const maturityChanged = selectedMaturity !== void 0 && selectedMaturity !== readMaturityTier(manifest);
22754
+ const confidenceFloorChanged = selectedConfidenceFloor !== void 0 && selectedConfidenceFloor !== readConfidenceFloor(manifest);
22562
22755
  if (tools.length === 0) {
22563
22756
  error("At least one tool must be selected.");
22564
22757
  throw new HatchError(
@@ -22584,11 +22777,11 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22584
22777
  enabled: selectedCliTools.length > 0,
22585
22778
  selected: selectedCliTools
22586
22779
  };
22587
- const selectedFeatures = stepState.features;
22588
- const features = { ...DEFAULT_FEATURES };
22589
- for (const k of Object.keys(features)) {
22590
- features[k] = selectedFeatures.includes(k);
22591
- }
22780
+ const features = rebuildFeaturesFromSelection(
22781
+ manifest.features,
22782
+ stepState.features,
22783
+ FEATURE_CHOICES.map((c) => c.value)
22784
+ );
22592
22785
  const hasExistingMcp = manifest.mcp.servers.length > 0;
22593
22786
  let mcpServers;
22594
22787
  if (features.mcp) {
@@ -22700,7 +22893,7 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22700
22893
  contentMetadataChanged = previousContent.preset !== newSelection.preset || previousContent.projectType !== newSelection.projectType || previousContent.teamSize !== newSelection.teamSize;
22701
22894
  }
22702
22895
  const diff = computeDiff(manifest, tools, features, mcpServers, platform, owner, repo, namespace, project, selectedCliTools, contentChanges);
22703
- if (isDiffEmpty(diff) && defaultBranch === currentBranch && !contentMetadataChanged) {
22896
+ if (isDiffEmpty(diff) && defaultBranch === currentBranch && !contentMetadataChanged && !maturityChanged && !confidenceFloorChanged) {
22704
22897
  if (!isQuiet()) console.log();
22705
22898
  info("No changes detected.");
22706
22899
  if (!isQuiet()) console.log();
@@ -22708,6 +22901,12 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22708
22901
  }
22709
22902
  if (cliOpts?.dryRun === true) {
22710
22903
  const dryLines = buildDiffSummaryLines(diff, platform, namespace, project, defaultBranch, currentBranch);
22904
+ if (maturityChanged && selectedMaturity !== void 0) {
22905
+ dryLines.push(`${chalk11.cyan("~")} Maturity: ${selectedMaturity}`);
22906
+ }
22907
+ if (confidenceFloorChanged && selectedConfidenceFloor !== void 0) {
22908
+ dryLines.push(`${chalk11.cyan("~")} Confidence floor: ${selectedConfidenceFloor}`);
22909
+ }
22711
22910
  dryLines.push("");
22712
22911
  dryLines.push(
22713
22912
  chalk11.dim(
@@ -22802,6 +23001,8 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22802
23001
  manifest.features = features;
22803
23002
  manifest.mcp = { servers: mcpServers };
22804
23003
  manifest.cliTools = cliToolsConfig;
23004
+ if (selectedMaturity !== void 0) manifest.maturity = selectedMaturity;
23005
+ if (selectedConfidenceFloor !== void 0) manifest.confidenceFloor = selectedConfidenceFloor;
22805
23006
  if (manifest.board) {
22806
23007
  manifest.board.owner = owner;
22807
23008
  manifest.board.repo = repo;
@@ -22870,6 +23071,12 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
22870
23071
  }
22871
23072
  if (!isQuiet()) console.log();
22872
23073
  const summaryLines = buildDiffSummaryLines(diff, platform, namespace, project, defaultBranch, currentBranch);
23074
+ if (maturityChanged && selectedMaturity !== void 0) {
23075
+ summaryLines.push(`${chalk11.cyan("~")} Maturity: ${selectedMaturity}`);
23076
+ }
23077
+ if (confidenceFloorChanged && selectedConfidenceFloor !== void 0) {
23078
+ summaryLines.push(`${chalk11.cyan("~")} Confidence floor: ${selectedConfidenceFloor}`);
23079
+ }
22873
23080
  summaryLines.push("");
22874
23081
  summaryLines.push(label("Files", `${updateResult.copiedFiles} canonical files updated`));
22875
23082
  summaryLines.push(label("Tools", `${updateResult.syncedTools} tool(s) synced`));
@@ -23170,12 +23377,161 @@ async function configCommandImpl(rootDir, format, cliOpts, arg1, arg2) {
23170
23377
  }
23171
23378
  }
23172
23379
 
23173
- // src/cli/commands/sync.ts
23174
- import { readFile as readFile37, stat as stat16, readdir as readdir26, mkdir as mkdir18 } from "fs/promises";
23175
- import { join as join52 } from "path";
23380
+ // src/cli/commands/setup.ts
23381
+ init_types();
23176
23382
  import { execFileSync as execFileSync9 } from "child_process";
23383
+ import { access as access16, mkdir as mkdir17, readdir as readdir24 } from "fs/promises";
23384
+ import { basename as basename4, join as join50, resolve as resolve8 } from "path";
23177
23385
  import chalk12 from "chalk";
23178
- import { parse as parseYaml11 } from "yaml";
23386
+ import inquirer12 from "inquirer";
23387
+ init_ui();
23388
+ var EMPTINESS_IGNORED_ENTRIES = /* @__PURE__ */ new Set([".git"]);
23389
+ async function pathExists3(p) {
23390
+ try {
23391
+ await access16(p);
23392
+ return true;
23393
+ } catch (err) {
23394
+ verbose(`setup: path probe access(${p}) \u2192 ${err.code ?? "absent"}`);
23395
+ return false;
23396
+ }
23397
+ }
23398
+ async function isDirEffectivelyEmpty(dir) {
23399
+ try {
23400
+ const entries = await readdir24(dir);
23401
+ return entries.every((e) => EMPTINESS_IGNORED_ENTRIES.has(e));
23402
+ } catch (err) {
23403
+ if (err.code === "ENOENT") return true;
23404
+ throw err;
23405
+ }
23406
+ }
23407
+ function runGit(args, cwd) {
23408
+ execFileSync9("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
23409
+ }
23410
+ function isGhReady() {
23411
+ try {
23412
+ execFileSync9("gh", ["auth", "status"], { stdio: ["ignore", "pipe", "pipe"] });
23413
+ return true;
23414
+ } catch (err) {
23415
+ verbose(`setup: gh availability probe failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
23416
+ return false;
23417
+ }
23418
+ }
23419
+ async function resolveTargetName(dir, cwd, headless) {
23420
+ if (dir && dir.trim().length > 0) return dir.trim();
23421
+ if (await isDirEffectivelyEmpty(cwd)) return ".";
23422
+ if (headless) return ".";
23423
+ const { dirName } = await inquirer12.prompt([
23424
+ {
23425
+ type: "input",
23426
+ name: "dirName",
23427
+ message: "Directory name for the new project:",
23428
+ validate: (v) => v && v.trim().length > 0 ? true : "Enter a non-empty directory name"
23429
+ }
23430
+ ]);
23431
+ return dirName.trim();
23432
+ }
23433
+ async function setupCommand(dir, opts = {}) {
23434
+ const startMs = Date.now();
23435
+ const headless = opts.yes === true;
23436
+ const format = beginCommand(opts, { banner: "none", interactive: !headless });
23437
+ const cwd = process.cwd();
23438
+ const targetName = await resolveTargetName(dir, cwd, headless);
23439
+ const targetDir = resolve8(cwd, targetName);
23440
+ const alreadyExists = await pathExists3(targetDir);
23441
+ if (alreadyExists && !await isDirEffectivelyEmpty(targetDir)) {
23442
+ throw new HatchError(
23443
+ `Target directory ${chalk12.bold(targetName)} already exists and is not empty.`,
23444
+ void 0,
23445
+ "FS_ERROR",
23446
+ `Pick a new or empty directory \u2014 e.g. \`hatch3r setup <new-dir>\` \u2014 or clear the existing contents first.`
23447
+ );
23448
+ }
23449
+ const gitDirExists = await pathExists3(join50(targetDir, ".git"));
23450
+ const willCreateDir = !alreadyExists;
23451
+ const willGitInit = !gitDirExists;
23452
+ const ghReady = opts.remote === true ? isGhReady() : false;
23453
+ const repoName = basename4(targetDir);
23454
+ if (opts.dryRun) {
23455
+ const lines = [];
23456
+ lines.push(
23457
+ willCreateDir ? `${chalk12.green("+")} create directory ${chalk12.bold(targetName)}` : `${chalk12.dim("=")} use existing empty directory ${chalk12.bold(targetName)}`
23458
+ );
23459
+ lines.push(
23460
+ willGitInit ? `${chalk12.green("+")} run \`git init\` in ${chalk12.bold(targetName)}` : `${chalk12.dim("=")} git repository already present \u2014 skip \`git init\``
23461
+ );
23462
+ if (opts.remote === true) {
23463
+ lines.push(
23464
+ ghReady ? `${chalk12.green("+")} create GitHub remote \`${repoName}\` via \`gh repo create\`` : `${chalk12.yellow("!")} \`--remote\` set but \`gh\` is unavailable/unauthenticated \u2014 skip remote`
23465
+ );
23466
+ }
23467
+ lines.push(`${chalk12.cyan(">")} run \`hatch3r init\` in ${chalk12.bold(targetName)}`);
23468
+ finishCommand(format, {
23469
+ command: "setup",
23470
+ title: "Setup (dry run)",
23471
+ style: "info",
23472
+ lines,
23473
+ json: {
23474
+ dryRun: true,
23475
+ targetDir,
23476
+ createDir: willCreateDir,
23477
+ gitInit: willGitInit,
23478
+ remote: opts.remote === true ? { requested: true, ghReady } : { requested: false },
23479
+ wouldRunInit: true
23480
+ },
23481
+ nextSteps: ["Re-run without --dry-run to scaffold and start init."],
23482
+ startMs
23483
+ });
23484
+ return;
23485
+ }
23486
+ await mkdir17(targetDir, { recursive: true });
23487
+ if (willCreateDir) {
23488
+ info(`Created project directory ${chalk12.bold(targetName)}`);
23489
+ } else {
23490
+ info(`Using directory ${chalk12.bold(targetName)}`);
23491
+ }
23492
+ if (willGitInit) {
23493
+ runGit(["init"], targetDir);
23494
+ info("Initialized empty Git repository");
23495
+ } else {
23496
+ info("Git repository already present \u2014 skipping `git init`");
23497
+ }
23498
+ if (opts.remote === true) {
23499
+ if (ghReady) {
23500
+ try {
23501
+ runGit(["status"], targetDir);
23502
+ execFileSync9("gh", ["repo", "create", repoName, "--source", targetDir, "--private"], {
23503
+ stdio: ["ignore", "pipe", "pipe"]
23504
+ });
23505
+ info(`Created GitHub remote ${chalk12.bold(repoName)} (private)`);
23506
+ } catch (err) {
23507
+ warn(
23508
+ `Could not create the GitHub remote: ${err instanceof Error ? err.message : String(err)}. Continuing with the local project; run \`gh repo create\` manually later.`
23509
+ );
23510
+ }
23511
+ } else {
23512
+ warn(
23513
+ `--remote was requested but the GitHub CLI (gh) is not installed or not authenticated. Skipping remote creation. Install gh and run \`gh auth login\`, then \`gh repo create\` from the new directory. Continuing with the local scaffold.`
23514
+ );
23515
+ }
23516
+ }
23517
+ process.chdir(targetDir);
23518
+ await initCommand({
23519
+ tools: opts.tools,
23520
+ preset: opts.preset,
23521
+ maturity: opts.maturity,
23522
+ yes: opts.yes,
23523
+ quiet: opts.quiet,
23524
+ format: opts.format,
23525
+ verbose: opts.verbose
23526
+ });
23527
+ }
23528
+
23529
+ // src/cli/commands/sync.ts
23530
+ import { readFile as readFile37, stat as stat16, readdir as readdir27, mkdir as mkdir19 } from "fs/promises";
23531
+ import { join as join53 } from "path";
23532
+ import { execFileSync as execFileSync10 } from "child_process";
23533
+ import chalk13 from "chalk";
23534
+ import { parse as parseYaml12 } from "yaml";
23179
23535
  init_safeWrite();
23180
23536
  init_types();
23181
23537
  init_version();
@@ -23184,8 +23540,8 @@ init_failureLog();
23184
23540
  // src/content/userContent.ts
23185
23541
  init_safeWrite();
23186
23542
  init_customization();
23187
- import { readdir as readdir24, mkdir as mkdir17, stat as stat15, readFile as readFile35 } from "fs/promises";
23188
- import { dirname as dirname23, join as join50 } from "path";
23543
+ import { readdir as readdir25, mkdir as mkdir18, stat as stat15, readFile as readFile35 } from "fs/promises";
23544
+ import { dirname as dirname23, join as join51 } from "path";
23189
23545
  import { fileURLToPath as fileURLToPath7 } from "url";
23190
23546
  import { stringify as yamlStringify4 } from "yaml";
23191
23547
  init_promptGuard();
@@ -23219,14 +23575,14 @@ async function discoverUserContent(rootDir) {
23219
23575
  if (type === "skill") {
23220
23576
  let dirents;
23221
23577
  try {
23222
- dirents = await readdir24(dir, { withFileTypes: true });
23578
+ dirents = await readdir25(dir, { withFileTypes: true });
23223
23579
  } catch (err) {
23224
23580
  if (err.code === "ENOENT") continue;
23225
23581
  throw err;
23226
23582
  }
23227
23583
  for (const d of dirents) {
23228
23584
  if (!d.isDirectory()) continue;
23229
- const skillFile = join50(dir, d.name, "SKILL.md");
23585
+ const skillFile = join51(dir, d.name, "SKILL.md");
23230
23586
  try {
23231
23587
  await stat15(skillFile);
23232
23588
  out.push({ type, name: d.name, path: skillFile });
@@ -23241,7 +23597,7 @@ async function discoverUserContent(rootDir) {
23241
23597
  } else {
23242
23598
  let entries;
23243
23599
  try {
23244
- entries = await readdir24(dir);
23600
+ entries = await readdir25(dir);
23245
23601
  } catch (err) {
23246
23602
  if (err.code === "ENOENT") continue;
23247
23603
  throw err;
@@ -23249,7 +23605,7 @@ async function discoverUserContent(rootDir) {
23249
23605
  for (const f of entries) {
23250
23606
  if (!f.endsWith(".md")) continue;
23251
23607
  const name = f.replace(/\.md$/, "");
23252
- out.push({ type, name, path: join50(dir, f) });
23608
+ out.push({ type, name, path: join51(dir, f) });
23253
23609
  }
23254
23610
  }
23255
23611
  }
@@ -23335,22 +23691,22 @@ function userTypeDir(userRoot, type) {
23335
23691
  command: "commands",
23336
23692
  hook: "hooks"
23337
23693
  };
23338
- return join50(userRoot, plural[type]);
23694
+ return join51(userRoot, plural[type]);
23339
23695
  }
23340
23696
 
23341
23697
  // src/content/learningsLoader.ts
23342
- import { readFile as readFile36, readdir as readdir25 } from "fs/promises";
23698
+ import { readFile as readFile36, readdir as readdir26 } from "fs/promises";
23343
23699
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync4 } from "fs";
23344
- import { dirname as dirname24, join as join51 } from "path";
23700
+ import { dirname as dirname24, join as join52 } from "path";
23345
23701
  init_failureLog();
23346
23702
  init_types();
23347
23703
  async function loadValidatedLearnings(rootDir, options = {}) {
23348
23704
  const onWarn = options.onWarn ?? (() => void 0);
23349
23705
  const source = options.source ?? "learnings-loader";
23350
- const learningsDir = join51(rootDir, HATCH3R_DIR, "learnings");
23706
+ const learningsDir = join52(rootDir, HATCH3R_DIR, "learnings");
23351
23707
  let entries;
23352
23708
  try {
23353
- entries = await readdir25(learningsDir);
23709
+ entries = await readdir26(learningsDir);
23354
23710
  } catch (err) {
23355
23711
  if (err.code === "ENOENT") {
23356
23712
  return { loaded: [], skipped: [] };
@@ -23373,7 +23729,7 @@ async function loadValidatedLearnings(rootDir, options = {}) {
23373
23729
  announceSkip(rootDir, source, fileName, reasons, onWarn);
23374
23730
  continue;
23375
23731
  }
23376
- const absolutePath = join51(learningsDir, fileName);
23732
+ const absolutePath = join52(learningsDir, fileName);
23377
23733
  let content;
23378
23734
  try {
23379
23735
  content = await readFile36(absolutePath, "utf-8");
@@ -23432,7 +23788,7 @@ function appendFailureLog(rootDir, source, err) {
23432
23788
  try {
23433
23789
  const entry = createFailureLogEntry(source, err);
23434
23790
  const line = formatLogEntry(entry) + "\n";
23435
- const failurePath = join51(rootDir, HATCH3R_DIR, FAILURE_LOG_FILE);
23791
+ const failurePath = join52(rootDir, HATCH3R_DIR, FAILURE_LOG_FILE);
23436
23792
  mkdirSync4(dirname24(failurePath), { recursive: true });
23437
23793
  appendFileSync4(failurePath, line);
23438
23794
  } catch {
@@ -23557,7 +23913,7 @@ function selectionSetFromManifest(content) {
23557
23913
 
23558
23914
  // src/cli/commands/sync.ts
23559
23915
  async function checkSpecFreshness(rootDir) {
23560
- const specsDir = join52(rootDir, "docs", "specs");
23916
+ const specsDir = join53(rootDir, "docs", "specs");
23561
23917
  try {
23562
23918
  await stat16(specsDir);
23563
23919
  } catch (err) {
@@ -23567,11 +23923,11 @@ async function checkSpecFreshness(rootDir) {
23567
23923
  }
23568
23924
  let oldestSpecMtime = Date.now();
23569
23925
  try {
23570
- const entries = await readdir26(specsDir, { withFileTypes: true, recursive: true });
23926
+ const entries = await readdir27(specsDir, { withFileTypes: true, recursive: true });
23571
23927
  for (const entry of entries) {
23572
23928
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
23573
23929
  const parentPath = entry.parentPath ?? entry.path ?? specsDir;
23574
- const fileStat = await stat16(join52(parentPath, entry.name));
23930
+ const fileStat = await stat16(join53(parentPath, entry.name));
23575
23931
  if (fileStat.mtimeMs < oldestSpecMtime) {
23576
23932
  oldestSpecMtime = fileStat.mtimeMs;
23577
23933
  }
@@ -23582,7 +23938,7 @@ async function checkSpecFreshness(rootDir) {
23582
23938
  return;
23583
23939
  }
23584
23940
  try {
23585
- const commitDate = execFileSync9("git", ["log", "-1", "--format=%ct"], { stdio: "pipe", timeout: 5e3 }).toString().trim();
23941
+ const commitDate = execFileSync10("git", ["log", "-1", "--format=%ct"], { stdio: "pipe", timeout: 5e3 }).toString().trim();
23586
23942
  const latestCommitMs = parseInt(commitDate, 10) * 1e3;
23587
23943
  if (latestCommitMs > oldestSpecMtime) {
23588
23944
  const daysSinceSpecUpdate = Math.floor((Date.now() - oldestSpecMtime) / (1e3 * 60 * 60 * 24));
@@ -23647,7 +24003,7 @@ async function syncCommand(opts = {}) {
23647
24003
  }
23648
24004
  }
23649
24005
  const syncStartMs = Date.now();
23650
- const syncWorkspace = join52(rootDir, ".sync-workspace");
24006
+ const syncWorkspace = join53(rootDir, ".sync-workspace");
23651
24007
  const checkpointMeta = () => ({
23652
24008
  baselineSha: HATCH3R_VERSION,
23653
24009
  lastPassedGateN: 0,
@@ -23668,7 +24024,7 @@ async function syncCommand(opts = {}) {
23668
24024
  const checkpoint = await readCheckpoint(syncWorkspace);
23669
24025
  if (checkpoint === null) {
23670
24026
  warn(
23671
- `\`hatch3r sync --resume\` requested but no checkpoint found at ${join52(syncWorkspace, "checkpoint.json")}. Continuing as a fresh sync.`
24027
+ `\`hatch3r sync --resume\` requested but no checkpoint found at ${join53(syncWorkspace, "checkpoint.json")}. Continuing as a fresh sync.`
23672
24028
  );
23673
24029
  } else if (checkpoint.meta.baselineSha === HATCH3R_VERSION && checkpoint.status === "passed") {
23674
24030
  info(
@@ -23688,14 +24044,14 @@ async function syncCommand(opts = {}) {
23688
24044
  const wsContext = await detectWorkspaceContext(rootDir);
23689
24045
  if (wsContext.type === "workspace-member") {
23690
24046
  warn(
23691
- `This repository appears to be managed by a workspace at ${wsContext.workspaceRoot ?? ".."}. Run ${chalk12.cyan("hatch3r sync")} from the workspace root to sync all repos.`
24047
+ `This repository appears to be managed by a workspace at ${wsContext.workspaceRoot ?? ".."}. Run ${chalk13.cyan("hatch3r sync")} from the workspace root to sync all repos.`
23692
24048
  );
23693
24049
  }
23694
24050
  if (wsContext.type === "workspace-root" || wsContext.type === "workspace-member") {
23695
24051
  enableDefaultCrossProcessLocking();
23696
24052
  }
23697
24053
  await migrateAgentsToHatch3r(rootDir);
23698
- const hatch3rDir = join52(rootDir, HATCH3R_DIR);
24054
+ const hatch3rDir = join53(rootDir, HATCH3R_DIR);
23699
24055
  const manifest = await readManifest(rootDir);
23700
24056
  assertManifest(manifest);
23701
24057
  const m = manifest;
@@ -23740,7 +24096,7 @@ async function syncCommand(opts = {}) {
23740
24096
  verbose(`User-content pre-flight scan skipped: ${err instanceof Error ? err.message : String(err)}`);
23741
24097
  }
23742
24098
  try {
23743
- const learnings = await validateLearningsDirectory(join52(rootDir, HATCH3R_DIR, "learnings"));
24099
+ const learnings = await validateLearningsDirectory(join53(rootDir, HATCH3R_DIR, "learnings"));
23744
24100
  const benignWarnings = learnings.warnings.filter((w) => !learnings.injectionHits.includes(w));
23745
24101
  if (benignWarnings.length > 0) {
23746
24102
  warn(`Learnings content scan: ${benignWarnings.length} advisory(ies):`);
@@ -23769,7 +24125,7 @@ async function syncCommand(opts = {}) {
23769
24125
  warn("Continuing with --force: invalid/poisoned learnings will be materialized into tool context.");
23770
24126
  console.log();
23771
24127
  }
23772
- const pruned = await pruneHandoffs(join52(rootDir, HATCH3R_DIR));
24128
+ const pruned = await pruneHandoffs(join53(rootDir, HATCH3R_DIR));
23773
24129
  if (pruned.archived.length > 0) {
23774
24130
  warn(
23775
24131
  `Handoffs: quarantined ${pruned.archived.length} past-expiry handoff(s) to ${HATCH3R_DIR}/handoffs/archived/ (off the resume read path): ${pruned.archived.join(", ")}`
@@ -23777,8 +24133,8 @@ async function syncCommand(opts = {}) {
23777
24133
  }
23778
24134
  for (const w of pruned.warnings) warn(` ${w}`);
23779
24135
  const handoffs = await validateHandoffsDirectory(
23780
- join52(rootDir, HATCH3R_DIR, "handoffs", "active"),
23781
- { archivedDir: join52(rootDir, HATCH3R_DIR, "handoffs", "archived") }
24136
+ join53(rootDir, HATCH3R_DIR, "handoffs", "active"),
24137
+ { archivedDir: join53(rootDir, HATCH3R_DIR, "handoffs", "archived") }
23782
24138
  );
23783
24139
  if (handoffs.warnings.length > 0) {
23784
24140
  warn(`Handoffs content scan: ${handoffs.warnings.length} advisory(ies):`);
@@ -23832,16 +24188,16 @@ async function syncCommand(opts = {}) {
23832
24188
  const diffBefore = /* @__PURE__ */ new Map();
23833
24189
  const diffAfter = /* @__PURE__ */ new Map();
23834
24190
  const canonicalContentRoot = resolveBundledContentRoot();
23835
- const syncSnapshotPaths = [join52(rootDir, HATCH3R_DIR, "hatch.json")];
24191
+ const syncSnapshotPaths = [join53(rootDir, HATCH3R_DIR, "hatch.json")];
23836
24192
  if (m.worktree?.enabled) {
23837
- syncSnapshotPaths.push(join52(rootDir, WORKTREE_INCLUDE_FILE));
24193
+ syncSnapshotPaths.push(join53(rootDir, WORKTREE_INCLUDE_FILE));
23838
24194
  }
23839
24195
  for (const rel of m.managedFiles) {
23840
- syncSnapshotPaths.push(join52(rootDir, rel));
24196
+ syncSnapshotPaths.push(join53(rootDir, rel));
23841
24197
  }
23842
24198
  if (m.managedFilesByAdapter) {
23843
24199
  for (const paths of Object.values(m.managedFilesByAdapter)) {
23844
- for (const rel of paths) syncSnapshotPaths.push(join52(rootDir, rel));
24200
+ for (const rel of paths) syncSnapshotPaths.push(join53(rootDir, rel));
23845
24201
  }
23846
24202
  }
23847
24203
  const syncSnapshot = await withSnapshot(
@@ -23862,7 +24218,7 @@ async function syncCommand(opts = {}) {
23862
24218
  const newManagedByAdapter = {};
23863
24219
  const orphanEntries = [];
23864
24220
  let budgetGateFailed = false;
23865
- const breakerStatePath = join52(hatch3rDir, BREAKER_STATE_FILE);
24221
+ const breakerStatePath = join53(hatch3rDir, BREAKER_STATE_FILE);
23866
24222
  let breakers = /* @__PURE__ */ new Map();
23867
24223
  try {
23868
24224
  const breakerLog = await readFile37(breakerStatePath, "utf-8");
@@ -23990,8 +24346,8 @@ async function syncCommand(opts = {}) {
23990
24346
  const previewActive = !!opts.previewTool && opts.previewTool === tool;
23991
24347
  for (const out of outputs) {
23992
24348
  const isManagedMerge = Boolean(out.managedContent);
23993
- const existing = await readFileOrNull2(join52(rootDir, out.path));
23994
- const predicted = predictMergeAction(existing, out.content, join52(rootDir, out.path), {
24349
+ const existing = await readFileOrNull2(join53(rootDir, out.path));
24350
+ const predicted = predictMergeAction(existing, out.content, join53(rootDir, out.path), {
23995
24351
  managedContent: out.managedContent,
23996
24352
  appendIfNoBlock: out.managedContent ? true : void 0,
23997
24353
  force: opts.force
@@ -24003,18 +24359,18 @@ async function syncCommand(opts = {}) {
24003
24359
  }
24004
24360
  if (previewActive) {
24005
24361
  const banner = `
24006
- ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u2500".repeat(40))}`;
24362
+ ${chalk13.dim("\u2500\u2500\u2500")} ${chalk13.bold(out.path)} ${chalk13.dim("\u2500".repeat(40))}`;
24007
24363
  console.error(banner);
24008
24364
  console.error(out.content);
24009
- console.error(chalk12.dim("\u2500".repeat(60)));
24365
+ console.error(chalk13.dim("\u2500".repeat(60)));
24010
24366
  }
24011
24367
  }
24012
24368
  } else {
24013
24369
  for (const out of outputs) {
24014
24370
  if (opts.diff) {
24015
- diffBefore.set(out.path, await readFileOrNull2(join52(rootDir, out.path)));
24371
+ diffBefore.set(out.path, await readFileOrNull2(join53(rootDir, out.path)));
24016
24372
  }
24017
- const fullPath = join52(rootDir, out.path);
24373
+ const fullPath = join53(rootDir, out.path);
24018
24374
  const isManagedMerge = Boolean(out.managedContent);
24019
24375
  if (out.managedContent) {
24020
24376
  const result = await safeWriteFile(fullPath, out.content, {
@@ -24047,7 +24403,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24047
24403
  }
24048
24404
  addManagedFile(m, out.path);
24049
24405
  if (opts.diff) {
24050
- diffAfter.set(out.path, await readFileOrNull2(join52(rootDir, out.path)));
24406
+ diffAfter.set(out.path, await readFileOrNull2(join53(rootDir, out.path)));
24051
24407
  }
24052
24408
  }
24053
24409
  }
@@ -24058,8 +24414,8 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24058
24414
  for (const p of perPackage) {
24059
24415
  if (opts.dryRun) {
24060
24416
  const isManagedMerge = Boolean(p.output.managedContent);
24061
- const existing = await readFileOrNull2(join52(rootDir, p.output.path));
24062
- const predicted = predictMergeAction(existing, p.output.content, join52(rootDir, p.output.path), {
24417
+ const existing = await readFileOrNull2(join53(rootDir, p.output.path));
24418
+ const predicted = predictMergeAction(existing, p.output.content, join53(rootDir, p.output.path), {
24063
24419
  managedContent: p.output.managedContent,
24064
24420
  appendIfNoBlock: p.output.managedContent ? true : void 0,
24065
24421
  force: opts.force
@@ -24071,7 +24427,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24071
24427
  }
24072
24428
  continue;
24073
24429
  }
24074
- const fullPath = join52(rootDir, p.output.path);
24430
+ const fullPath = join53(rootDir, p.output.path);
24075
24431
  if (opts.diff) {
24076
24432
  diffBefore.set(p.output.path, await readFileOrNull2(fullPath));
24077
24433
  }
@@ -24171,7 +24527,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24171
24527
  await recordPhase(1, "passed");
24172
24528
  if (breakers.size > 0) {
24173
24529
  try {
24174
- await mkdir18(hatch3rDir, { recursive: true });
24530
+ await mkdir19(hatch3rDir, { recursive: true });
24175
24531
  await safeWriteFile(breakerStatePath, serializeBreakerMap(breakers));
24176
24532
  verbose(`Persisted ${breakers.size} circuit breaker(s) to ${BREAKER_STATE_FILE}`);
24177
24533
  } catch (err) {
@@ -24214,7 +24570,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24214
24570
  const wtContent = await generateWorktreeInclude(m, rootDir);
24215
24571
  const wtManaged = extractManagedContent(wtContent);
24216
24572
  const wtResult = await safeWriteFile(
24217
- join52(rootDir, WORKTREE_INCLUDE_FILE),
24573
+ join53(rootDir, WORKTREE_INCLUDE_FILE),
24218
24574
  wtContent,
24219
24575
  { managedContent: wtManaged, appendIfNoBlock: true, force: opts.force }
24220
24576
  );
@@ -24281,7 +24637,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24281
24637
  const CUSTOMIZE_DIRS = ["agents", "commands", "skills", "rules"];
24282
24638
  for (const dir of CUSTOMIZE_DIRS) {
24283
24639
  try {
24284
- const files = await readdir26(join52(rootDir, ".hatch3r", dir));
24640
+ const files = await readdir27(join53(rootDir, ".hatch3r", dir));
24285
24641
  for (const f of files.filter((f2) => f2.endsWith(".customize.yaml") || f2.endsWith(".customize.md"))) {
24286
24642
  const itemId = f.replace(/\.customize\.(yaml|md)$/, "");
24287
24643
  const prefixed = `hatch3r-${itemId}`;
@@ -24289,14 +24645,14 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24289
24645
  warn(`Orphaned customization: .hatch3r/${dir}/${f} \u2014 content no longer in manifest. Consider removing it.`);
24290
24646
  }
24291
24647
  if (f.endsWith(".customize.yaml")) {
24292
- const yamlPath = join52(rootDir, ".hatch3r", dir, f);
24648
+ const yamlPath = join53(rootDir, ".hatch3r", dir, f);
24293
24649
  try {
24294
24650
  const raw = await readFile37(yamlPath, "utf-8");
24295
24651
  if (Buffer.byteLength(raw, "utf-8") > 10240) {
24296
24652
  warn(`.hatch3r/${dir}/${f} exceeds 10KB and will be skipped during adapter generation. Trim or split the override.`);
24297
24653
  } else {
24298
24654
  try {
24299
- parseYaml11(raw);
24655
+ parseYaml12(raw);
24300
24656
  } catch (parseErr) {
24301
24657
  const msg = parseErr instanceof Error ? parseErr.message : String(parseErr);
24302
24658
  warn(`Invalid YAML in .hatch3r/${dir}/${f} \u2014 ${msg}. Run \`npx hatch3r validate\` for the full error context.`);
@@ -24319,7 +24675,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24319
24675
  }
24320
24676
  console.log();
24321
24677
  const icons = {
24322
- created: chalk12.green("+"),
24678
+ created: chalk13.green("+"),
24323
24679
  // F10.4-1: split the prior `updated` icon into `merged` (managed-block
24324
24680
  // merge — user content preserved) and `regenerated` (full-file
24325
24681
  // overwrite) so the customization-preservation messaging in the
@@ -24327,24 +24683,24 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24327
24683
  // fallback for any call-path that has not yet adopted the
24328
24684
  // `renderAction()` heuristic at the call-site (none in this file
24329
24685
  // after F10.4-1; reserved for forward-compatibility).
24330
- updated: chalk12.yellow("~"),
24331
- merged: chalk12.cyan("*"),
24332
- regenerated: chalk12.yellow("~"),
24686
+ updated: chalk13.yellow("~"),
24687
+ merged: chalk13.cyan("*"),
24688
+ regenerated: chalk13.yellow("~"),
24333
24689
  // G5: "unchanged" is a no-op action returned by safeWriteFile when the
24334
24690
  // computed bytes match the file on disk. We render it the same as
24335
24691
  // "skipped" (dim "=") so the human summary remains readable while still
24336
24692
  // signalling that nothing changed.
24337
- unchanged: chalk12.dim("="),
24338
- skipped: chalk12.dim("="),
24339
- "dry-run": chalk12.cyan("?")
24693
+ unchanged: chalk13.dim("="),
24694
+ skipped: chalk13.dim("="),
24695
+ "dry-run": chalk13.cyan("?")
24340
24696
  };
24341
24697
  const compactedResults = compactPhaseOutput(results);
24342
24698
  const summaryLines = compactedResults.map((r) => {
24343
24699
  if (typeof r === "string") {
24344
- return chalk12.dim(r);
24700
+ return chalk13.dim(r);
24345
24701
  }
24346
- const icon = icons[r.action] ?? chalk12.dim(" ");
24347
- return `${icon} ${r.path} ${chalk12.dim(`(${r.action})`)}`;
24702
+ const icon = icons[r.action] ?? chalk13.dim(" ");
24703
+ return `${icon} ${r.path} ${chalk13.dim(`(${r.action})`)}`;
24348
24704
  });
24349
24705
  const actionCounts = {};
24350
24706
  for (const r of results) {
@@ -24353,15 +24709,15 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24353
24709
  }
24354
24710
  }
24355
24711
  const tallyParts = [];
24356
- if (actionCounts.created) tallyParts.push(chalk12.green(`${actionCounts.created} created`));
24357
- if (actionCounts.merged) tallyParts.push(chalk12.cyan(`${actionCounts.merged} merged (your edits preserved)`));
24358
- if (actionCounts.regenerated) tallyParts.push(chalk12.yellow(`${actionCounts.regenerated} regenerated (full overwrite)`));
24359
- if (actionCounts.updated) tallyParts.push(chalk12.yellow(`${actionCounts.updated} updated`));
24360
- if (actionCounts.unchanged) tallyParts.push(chalk12.dim(`${actionCounts.unchanged} unchanged`));
24361
- if (actionCounts.skipped) tallyParts.push(chalk12.dim(`${actionCounts.skipped} skipped`));
24362
- if (actionCounts["dry-run"]) tallyParts.push(chalk12.cyan(`${actionCounts["dry-run"]} dry-run`));
24712
+ if (actionCounts.created) tallyParts.push(chalk13.green(`${actionCounts.created} created`));
24713
+ if (actionCounts.merged) tallyParts.push(chalk13.cyan(`${actionCounts.merged} merged (your edits preserved)`));
24714
+ if (actionCounts.regenerated) tallyParts.push(chalk13.yellow(`${actionCounts.regenerated} regenerated (full overwrite)`));
24715
+ if (actionCounts.updated) tallyParts.push(chalk13.yellow(`${actionCounts.updated} updated`));
24716
+ if (actionCounts.unchanged) tallyParts.push(chalk13.dim(`${actionCounts.unchanged} unchanged`));
24717
+ if (actionCounts.skipped) tallyParts.push(chalk13.dim(`${actionCounts.skipped} skipped`));
24718
+ if (actionCounts["dry-run"]) tallyParts.push(chalk13.cyan(`${actionCounts["dry-run"]} dry-run`));
24363
24719
  if (tallyParts.length > 0) {
24364
- summaryLines.unshift(tallyParts.join(chalk12.dim(" \xB7 ")));
24720
+ summaryLines.unshift(tallyParts.join(chalk13.dim(" \xB7 ")));
24365
24721
  summaryLines.splice(1, 0, "");
24366
24722
  }
24367
24723
  if (opts.diff && diffBefore.size > 0) {
@@ -24370,11 +24726,11 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24370
24726
  const before = diffBefore.get(filePath) ?? null;
24371
24727
  const after = diffAfter.get(filePath) ?? null;
24372
24728
  if (before === null && after !== null) {
24373
- diffLines.push(`${chalk12.green("+ added")} ${filePath}`);
24729
+ diffLines.push(`${chalk13.green("+ added")} ${filePath}`);
24374
24730
  } else if (before !== null && after !== null && before !== after) {
24375
- diffLines.push(`${chalk12.yellow("~ modified")} ${filePath}`);
24731
+ diffLines.push(`${chalk13.yellow("~ modified")} ${filePath}`);
24376
24732
  } else if (before !== null && after !== null && before === after) {
24377
- diffLines.push(`${chalk12.dim("= unchanged")} ${filePath}`);
24733
+ diffLines.push(`${chalk13.dim("= unchanged")} ${filePath}`);
24378
24734
  }
24379
24735
  }
24380
24736
  if (diffLines.length > 0) {
@@ -24385,7 +24741,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24385
24741
  const boxTitle = opts.dryRun ? "Sync dry run complete" : adapterFailures.length > 0 ? "Sync complete (with warnings)" : "Sync complete";
24386
24742
  if (syncSessionId) {
24387
24743
  summaryLines.push("");
24388
- summaryLines.push(`${chalk12.dim("Snapshot:")} ${syncSessionId} ${chalk12.dim(`(revert: hatch3r rollback --session=${syncSessionId})`)}`);
24744
+ summaryLines.push(`${chalk13.dim("Snapshot:")} ${syncSessionId} ${chalk13.dim(`(revert: hatch3r rollback --session=${syncSessionId})`)}`);
24389
24745
  }
24390
24746
  finishCommand(format, {
24391
24747
  command: "sync",
@@ -24421,11 +24777,11 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24421
24777
  const head = activeIds.slice(0, 4).join(", ");
24422
24778
  const tail = activeIds.length > 4 ? ` \u2026 (+${activeIds.length - 4} more)` : "";
24423
24779
  info(
24424
- `Customizations applied: ${chalk12.bold(String(c.active))} active (${head}${tail})` + (c.skipped > 0 ? `, ${c.skipped} skipped` : "") + (c.failed > 0 ? `, ${chalk12.red(String(c.failed))} failed` : "") + (c.inert > 0 ? `, ${chalk12.yellow(String(c.inert))} inert` : "")
24780
+ `Customizations applied: ${chalk13.bold(String(c.active))} active (${head}${tail})` + (c.skipped > 0 ? `, ${c.skipped} skipped` : "") + (c.failed > 0 ? `, ${chalk13.red(String(c.failed))} failed` : "") + (c.inert > 0 ? `, ${chalk13.yellow(String(c.inert))} inert` : "")
24425
24781
  );
24426
24782
  } else if (c.skipped > 0 || c.failed > 0 || c.inert > 0) {
24427
24783
  info(
24428
- `Customizations: 0 active` + (c.skipped > 0 ? `, ${c.skipped} skipped` : "") + (c.failed > 0 ? `, ${chalk12.red(String(c.failed))} failed` : "") + (c.inert > 0 ? `, ${chalk12.yellow(String(c.inert))} inert` : "") + ` (run \`hatch3r explain --customizations\` for detail).`
24784
+ `Customizations: 0 active` + (c.skipped > 0 ? `, ${c.skipped} skipped` : "") + (c.failed > 0 ? `, ${chalk13.red(String(c.failed))} failed` : "") + (c.inert > 0 ? `, ${chalk13.yellow(String(c.inert))} inert` : "") + ` (run \`hatch3r explain --customizations\` for detail).`
24429
24785
  );
24430
24786
  }
24431
24787
  }
@@ -24481,7 +24837,7 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24481
24837
  const syncableCount = wsManifest.repos.filter((r) => r.sync).length;
24482
24838
  if (!syncReposRequested && !syncOnSync) {
24483
24839
  if (syncableCount > 0) {
24484
- info(`Workspace: ${syncableCount} repo(s) available for sync. Run ${chalk12.bold("hatch3r sync --repos")} to propagate.`);
24840
+ info(`Workspace: ${syncableCount} repo(s) available for sync. Run ${chalk13.bold("hatch3r sync --repos")} to propagate.`);
24485
24841
  }
24486
24842
  return;
24487
24843
  }
@@ -24513,13 +24869,13 @@ ${chalk12.dim("\u2500\u2500\u2500")} ${chalk12.bold(out.path)} ${chalk12.dim("\u
24513
24869
  }
24514
24870
 
24515
24871
  // src/cli/commands/validate.ts
24516
- import { readdir as readdir28, readFile as readFile39, access as access16, stat as stat17 } from "fs/promises";
24872
+ import { readdir as readdir29, readFile as readFile39, access as access17, stat as stat17 } from "fs/promises";
24517
24873
  import { existsSync as existsSync5 } from "fs";
24518
24874
  import { spawnSync as spawnSync3 } from "child_process";
24519
- import { dirname as dirname26, join as join54, posix as posix3 } from "path";
24875
+ import { dirname as dirname26, join as join55, posix as posix3 } from "path";
24520
24876
  import { fileURLToPath as fileURLToPath9 } from "url";
24521
- import chalk13 from "chalk";
24522
- import { parse as parseYaml12 } from "yaml";
24877
+ import chalk14 from "chalk";
24878
+ import { parse as parseYaml13 } from "yaml";
24523
24879
  init_types();
24524
24880
  init_version();
24525
24881
  init_customization();
@@ -24689,8 +25045,8 @@ function detectSecrets(envVars) {
24689
25045
 
24690
25046
  // src/pipeline/complianceVerification.ts
24691
25047
  init_agentToolAllowlist();
24692
- import { readFile as readFile38, readdir as readdir27 } from "fs/promises";
24693
- import { dirname as dirname25, join as join53 } from "path";
25048
+ import { readFile as readFile38, readdir as readdir28 } from "fs/promises";
25049
+ import { dirname as dirname25, join as join54 } from "path";
24694
25050
  import { fileURLToPath as fileURLToPath8 } from "url";
24695
25051
 
24696
25052
  // src/pipeline/reviewLoop.ts
@@ -24794,13 +25150,13 @@ var RESILIENCE_MODULES = [
24794
25150
  var __dirname5 = dirname25(fileURLToPath8(import.meta.url));
24795
25151
  async function resolveCommandsDir() {
24796
25152
  const candidates = [
24797
- join53(__dirname5, "..", "cli", "commands"),
24798
- join53(__dirname5, "..", "..", "src", "cli", "commands"),
24799
- join53(__dirname5, "..", "cli")
25153
+ join54(__dirname5, "..", "cli", "commands"),
25154
+ join54(__dirname5, "..", "..", "src", "cli", "commands"),
25155
+ join54(__dirname5, "..", "cli")
24800
25156
  ];
24801
25157
  for (const candidate of candidates) {
24802
25158
  try {
24803
- const entries = await readdir27(candidate);
25159
+ const entries = await readdir28(candidate);
24804
25160
  if (entries.length > 0) return candidate;
24805
25161
  } catch (err) {
24806
25162
  const message = err instanceof Error ? err.message : String(err);
@@ -24815,13 +25171,13 @@ async function detectResilienceInvocations() {
24815
25171
  if (!commandsDir) return invoked;
24816
25172
  let entries;
24817
25173
  try {
24818
- entries = await readdir27(commandsDir, { recursive: true });
25174
+ entries = await readdir28(commandsDir, { recursive: true });
24819
25175
  } catch (err) {
24820
25176
  const message = err instanceof Error ? err.message : String(err);
24821
25177
  verbose(`complianceVerification: detectResilienceInvocations readdir(${commandsDir}) \u2192 empty \u2014 ${message}`);
24822
25178
  return invoked;
24823
25179
  }
24824
- const files = entries.filter((e) => typeof e === "string" && (e.endsWith(".ts") || e.endsWith(".js"))).map((e) => join53(commandsDir, e));
25180
+ const files = entries.filter((e) => typeof e === "string" && (e.endsWith(".ts") || e.endsWith(".js"))).map((e) => join54(commandsDir, e));
24825
25181
  const patterns = {};
24826
25182
  for (const mod of RESILIENCE_MODULES) {
24827
25183
  patterns[mod] = new RegExp(`pipeline/${mod}(?:\\.js)?["']`);
@@ -24874,17 +25230,17 @@ async function verifyAdapterSignalPropagation() {
24874
25230
  warnings: [],
24875
25231
  async generate(_canonicalRoot, _manifest, _userRepoRoot, _generationMode, signal) {
24876
25232
  signalDefined = signal !== void 0;
24877
- await new Promise((resolve9) => {
25233
+ await new Promise((resolve10) => {
24878
25234
  if (signal?.aborted) {
24879
25235
  signalAborted = true;
24880
- resolve9();
25236
+ resolve10();
24881
25237
  return;
24882
25238
  }
24883
25239
  signal?.addEventListener(
24884
25240
  "abort",
24885
25241
  () => {
24886
25242
  signalAborted = true;
24887
- resolve9();
25243
+ resolve10();
24888
25244
  },
24889
25245
  { once: true }
24890
25246
  );
@@ -25260,7 +25616,7 @@ async function validateManifest2(rootDir, manifest, result) {
25260
25616
  if (!manifest.tools || manifest.tools.length === 0) result.warnings.push("hatch.json: no tools configured");
25261
25617
  for (const managedFile of manifest.managedFiles ?? []) {
25262
25618
  try {
25263
- await access16(join54(rootDir, managedFile));
25619
+ await access17(join55(rootDir, managedFile));
25264
25620
  } catch (err) {
25265
25621
  if (err.code !== "ENOENT") throw err;
25266
25622
  result.warnings.push(`Managed file missing from disk: ${managedFile}`);
@@ -25272,7 +25628,7 @@ async function validateDirectories(canonicalRoot, result) {
25272
25628
  const optionalDirs = ["commands", "prompts", "mcp", "policy", "github-agents"];
25273
25629
  for (const dir of requiredDirs) {
25274
25630
  try {
25275
- await access16(join54(canonicalRoot, dir));
25631
+ await access17(join55(canonicalRoot, dir));
25276
25632
  } catch (err) {
25277
25633
  if (err.code !== "ENOENT") throw err;
25278
25634
  result.errors.push(`Required canonical directory missing: ${dir}/ (under bundled content root)`);
@@ -25280,7 +25636,7 @@ async function validateDirectories(canonicalRoot, result) {
25280
25636
  }
25281
25637
  for (const dir of optionalDirs) {
25282
25638
  try {
25283
- await access16(join54(canonicalRoot, dir));
25639
+ await access17(join55(canonicalRoot, dir));
25284
25640
  } catch (err) {
25285
25641
  if (err.code !== "ENOENT") throw err;
25286
25642
  verboseWarn(result, `Optional canonical directory missing: ${dir}/ (under bundled content root)`);
@@ -25291,12 +25647,12 @@ async function validateFrontmatter(canonicalRoot, result) {
25291
25647
  const requiredDirs = ["agents", "skills", "rules"];
25292
25648
  const optionalDirs = ["commands", "prompts", "mcp", "policy", "github-agents"];
25293
25649
  for (const dir of [...requiredDirs, ...optionalDirs]) {
25294
- const dirPath = join54(canonicalRoot, dir);
25650
+ const dirPath = join55(canonicalRoot, dir);
25295
25651
  try {
25296
- const entries = await readdir28(dirPath, { withFileTypes: true });
25652
+ const entries = await readdir29(dirPath, { withFileTypes: true });
25297
25653
  for (const entry of entries) {
25298
25654
  if (entry.isFile() && entry.name.endsWith(".md")) {
25299
- const filePath = join54(dirPath, entry.name);
25655
+ const filePath = join55(dirPath, entry.name);
25300
25656
  const content = await readFile39(filePath, "utf-8");
25301
25657
  const label2 = `${dir}/${entry.name}`;
25302
25658
  if (!content.startsWith("---")) {
@@ -25307,7 +25663,7 @@ async function validateFrontmatter(canonicalRoot, result) {
25307
25663
  result.errors.push(`Invalid frontmatter (no closing ---): ${label2}`);
25308
25664
  } else {
25309
25665
  const frontmatter = content.slice(3, endIdx).trim();
25310
- const parsedFm = parseYaml12(frontmatter);
25666
+ const parsedFm = parseYaml13(frontmatter);
25311
25667
  const idField = dir === "github-agents" ? "name" : "id";
25312
25668
  if (!parsedFm || typeof parsedFm !== "object" || !parsedFm[idField]) {
25313
25669
  result.warnings.push(`Missing '${idField}' in frontmatter: ${label2}`);
@@ -25328,9 +25684,9 @@ async function validateFrontmatter(canonicalRoot, result) {
25328
25684
  }
25329
25685
  } else if (entry.isDirectory()) {
25330
25686
  if (dir !== "skills") continue;
25331
- const skillPath = join54(dirPath, entry.name, "SKILL.md");
25687
+ const skillPath = join55(dirPath, entry.name, "SKILL.md");
25332
25688
  try {
25333
- await access16(skillPath);
25689
+ await access17(skillPath);
25334
25690
  } catch (err) {
25335
25691
  if (err.code !== "ENOENT") throw err;
25336
25692
  result.warnings.push(`Skill directory missing SKILL.md: ${dir}/${entry.name}/`);
@@ -25544,29 +25900,29 @@ async function validateManagedFilePrefixes(manifest, result) {
25544
25900
  }
25545
25901
  async function validateHooks(canonicalRoot, manifest, result) {
25546
25902
  if (!manifest.features.hooks) return;
25547
- const hooksDir = join54(canonicalRoot, "hooks");
25903
+ const hooksDir = join55(canonicalRoot, "hooks");
25548
25904
  try {
25549
- const hookFiles = await readdir28(hooksDir);
25905
+ const hookFiles = await readdir29(hooksDir);
25550
25906
  const mdHooks = hookFiles.filter((f) => f.endsWith(".md"));
25551
25907
  if (mdHooks.length === 0) {
25552
25908
  result.warnings.push("Hooks feature enabled but no hook definitions found in bundled hooks/");
25553
25909
  }
25554
25910
  let agentFiles;
25555
25911
  try {
25556
- const agentEntries = await readdir28(join54(canonicalRoot, "agents"));
25912
+ const agentEntries = await readdir29(join55(canonicalRoot, "agents"));
25557
25913
  agentFiles = new Set(agentEntries.filter((f) => f.endsWith(".md")));
25558
25914
  } catch (err) {
25559
25915
  if (err.code !== "ENOENT") throw err;
25560
25916
  }
25561
25917
  for (const hookFile of mdHooks) {
25562
- const hookContent = await readFile39(join54(hooksDir, hookFile), "utf-8");
25918
+ const hookContent = await readFile39(join55(hooksDir, hookFile), "utf-8");
25563
25919
  if (!hookContent.startsWith("---")) {
25564
25920
  result.warnings.push(`Hook missing frontmatter: hooks/${hookFile}`);
25565
25921
  continue;
25566
25922
  }
25567
25923
  const endIdx = hookContent.indexOf("---", 3);
25568
25924
  if (endIdx === -1) continue;
25569
- const fm = parseYaml12(hookContent.slice(3, endIdx).trim());
25925
+ const fm = parseYaml13(hookContent.slice(3, endIdx).trim());
25570
25926
  if (fm?.event && typeof fm.event === "string") {
25571
25927
  if (!isValidHookEvent(fm.event)) {
25572
25928
  result.errors.push(`Hook "${hookFile}" has invalid event "${fm.event}". Valid events: ${[...VALID_HOOK_EVENTS].join(", ")}`);
@@ -25591,7 +25947,7 @@ async function validateHooks(canonicalRoot, manifest, result) {
25591
25947
  }
25592
25948
  async function validateMcp(canonicalRoot, manifest, result) {
25593
25949
  if (!manifest.features.mcp || manifest.mcp.servers.length === 0) return;
25594
- const mcpPath = join54(canonicalRoot, "mcp", "mcp.json");
25950
+ const mcpPath = join55(canonicalRoot, "mcp", "mcp.json");
25595
25951
  try {
25596
25952
  const mcpContent = await readFile39(mcpPath, "utf-8");
25597
25953
  const mcpParsed = JSON.parse(mcpContent);
@@ -25660,17 +26016,17 @@ async function validateCustomizeYaml(rootDir, result) {
25660
26016
  enabled: "boolean"
25661
26017
  };
25662
26018
  for (const { dir } of CUSTOMIZATION_TYPES2) {
25663
- const customDir = join54(rootDir, ".hatch3r", dir);
26019
+ const customDir = join55(rootDir, ".hatch3r", dir);
25664
26020
  let files;
25665
26021
  try {
25666
- files = await readdir28(customDir);
26022
+ files = await readdir29(customDir);
25667
26023
  } catch (err) {
25668
26024
  if (err.code === "ENOENT") continue;
25669
26025
  throw err;
25670
26026
  }
25671
26027
  const yamlFiles = files.filter((f) => f.endsWith(".customize.yaml"));
25672
26028
  for (const file of yamlFiles) {
25673
- const filePath = join54(customDir, file);
26029
+ const filePath = join55(customDir, file);
25674
26030
  const itemId = file.replace(".customize.yaml", "");
25675
26031
  let raw;
25676
26032
  try {
@@ -25686,7 +26042,7 @@ async function validateCustomizeYaml(rootDir, result) {
25686
26042
  }
25687
26043
  let parsed;
25688
26044
  try {
25689
- parsed = parseYaml12(raw);
26045
+ parsed = parseYaml13(raw);
25690
26046
  } catch {
25691
26047
  result.errors.push(
25692
26048
  `Invalid YAML syntax in .hatch3r/${dir}/${file}`
@@ -25737,10 +26093,10 @@ async function validateCustomizeYaml(rootDir, result) {
25737
26093
  }
25738
26094
  async function validateCustomizations(rootDir, agentsDir, manifest, result) {
25739
26095
  for (const { dir, canonical } of CUSTOMIZATION_TYPES2) {
25740
- const customDir = join54(rootDir, ".hatch3r", dir);
26096
+ const customDir = join55(rootDir, ".hatch3r", dir);
25741
26097
  const strategy = canonical === "skills" ? "subdir" : "glob";
25742
26098
  try {
25743
- const customFiles = await readdir28(customDir);
26099
+ const customFiles = await readdir29(customDir);
25744
26100
  for (const file of customFiles) {
25745
26101
  if (file.endsWith(".customize.yaml")) {
25746
26102
  const itemId = file.replace(".customize.yaml", "");
@@ -25758,9 +26114,9 @@ async function validateCustomizations(rootDir, agentsDir, manifest, result) {
25758
26114
  async function findContentFile(agentsDir, cfg, id) {
25759
26115
  const baseId = id.startsWith("cmd-") ? id.slice(4) : id;
25760
26116
  if (cfg.strategy === "subdir") {
25761
- const path = join54(agentsDir, cfg.dir, baseId, "SKILL.md");
26117
+ const path = join55(agentsDir, cfg.dir, baseId, "SKILL.md");
25762
26118
  try {
25763
- await access16(path);
26119
+ await access17(path);
25764
26120
  return path;
25765
26121
  } catch (err) {
25766
26122
  const message = err instanceof Error ? err.message : String(err);
@@ -25768,21 +26124,21 @@ async function findContentFile(agentsDir, cfg, id) {
25768
26124
  return null;
25769
26125
  }
25770
26126
  }
25771
- const root = join54(agentsDir, cfg.dir);
26127
+ const root = join55(agentsDir, cfg.dir);
25772
26128
  const stack = [root];
25773
26129
  const mdFiles = [];
25774
26130
  while (stack.length > 0) {
25775
26131
  const dir = stack.pop();
25776
26132
  let entries;
25777
26133
  try {
25778
- entries = await readdir28(dir, { withFileTypes: true });
26134
+ entries = await readdir29(dir, { withFileTypes: true });
25779
26135
  } catch (err) {
25780
26136
  const message = err instanceof Error ? err.message : String(err);
25781
26137
  verbose(`validate: findContentFile readdir(${dir}) skipped \u2014 ${message}`);
25782
26138
  continue;
25783
26139
  }
25784
26140
  for (const entry of entries) {
25785
- const full = join54(dir, entry.name);
26141
+ const full = join55(dir, entry.name);
25786
26142
  if (entry.isDirectory()) {
25787
26143
  stack.push(full);
25788
26144
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -25799,7 +26155,7 @@ async function findContentFile(agentsDir, cfg, id) {
25799
26155
  if (!raw.startsWith("---")) continue;
25800
26156
  const endIdx = raw.indexOf("---", 3);
25801
26157
  if (endIdx === -1) continue;
25802
- const fm = parseYaml12(raw.slice(3, endIdx).trim());
26158
+ const fm = parseYaml13(raw.slice(3, endIdx).trim());
25803
26159
  const fmId = fm && typeof fm === "object" && typeof fm.id === "string" ? fm.id : null;
25804
26160
  if (fmId && (fmId === id || fmId === baseId)) {
25805
26161
  return file;
@@ -25836,9 +26192,9 @@ async function validateContentConsistency(rootDir, agentsDir, manifest, result)
25836
26192
  for (const id of ids) allContentIds.add(id);
25837
26193
  }
25838
26194
  for (const { dir } of CUSTOMIZATION_TYPES2) {
25839
- const customDir = join54(rootDir, ".hatch3r", dir);
26195
+ const customDir = join55(rootDir, ".hatch3r", dir);
25840
26196
  try {
25841
- const files = await readdir28(customDir);
26197
+ const files = await readdir29(customDir);
25842
26198
  for (const f of files.filter((f2) => f2.endsWith(".customize.yaml") || f2.endsWith(".customize.md"))) {
25843
26199
  const itemId = f.replace(/\.customize\.(yaml|md)$/, "");
25844
26200
  if (!allContentIds.has(itemId) && !allContentIds.has(`${HATCH3R_PREFIX}${itemId}`) && !allContentIds.has(`cmd-${itemId}`) && !allContentIds.has(`cmd-${HATCH3R_PREFIX}${itemId}`)) {
@@ -25850,7 +26206,7 @@ async function validateContentConsistency(rootDir, agentsDir, manifest, result)
25850
26206
  }
25851
26207
  }
25852
26208
  }
25853
- const learningsDir = join54(rootDir, HATCH3R_DIR, "learnings");
26209
+ const learningsDir = join55(rootDir, HATCH3R_DIR, "learnings");
25854
26210
  const learningsResult = await validateLearningsDirectory(learningsDir);
25855
26211
  for (const e of learningsResult.errors) {
25856
26212
  result.errors.push(e);
@@ -25858,8 +26214,8 @@ async function validateContentConsistency(rootDir, agentsDir, manifest, result)
25858
26214
  for (const w of learningsResult.warnings) {
25859
26215
  result.warnings.push(w);
25860
26216
  }
25861
- const handoffsActiveDir = join54(rootDir, HATCH3R_DIR, "handoffs", "active");
25862
- const handoffsArchivedDir = join54(rootDir, HATCH3R_DIR, "handoffs", "archived");
26217
+ const handoffsActiveDir = join55(rootDir, HATCH3R_DIR, "handoffs", "active");
26218
+ const handoffsArchivedDir = join55(rootDir, HATCH3R_DIR, "handoffs", "archived");
25863
26219
  const handoffsResult = await validateHandoffsDirectory(handoffsActiveDir, {
25864
26220
  archivedDir: handoffsArchivedDir
25865
26221
  });
@@ -25907,7 +26263,7 @@ async function validateUserContent(rootDir, agentsDir, result, index) {
25907
26263
  if (userItems.length === 0) return;
25908
26264
  for (const item of userItems) {
25909
26265
  const fileLabel = `.hatch3r/overrides/${item.relativePath}`;
25910
- const absPath = item.type === "skill" ? join54(userRoot, item.relativePath, "SKILL.md") : join54(userRoot, item.relativePath);
26266
+ const absPath = item.type === "skill" ? join55(userRoot, item.relativePath, "SKILL.md") : join55(userRoot, item.relativePath);
25911
26267
  let raw;
25912
26268
  try {
25913
26269
  raw = await readFile39(absPath, "utf-8");
@@ -25934,7 +26290,7 @@ async function validateUserContent(rootDir, agentsDir, result, index) {
25934
26290
  const fmRaw = raw.slice(3, fmEnd).trim();
25935
26291
  let fm;
25936
26292
  try {
25937
- fm = parseYaml12(fmRaw);
26293
+ fm = parseYaml13(fmRaw);
25938
26294
  } catch (err) {
25939
26295
  result.errors.push(
25940
26296
  `${fileLabel}: YAML parse error in frontmatter \u2014 ${err instanceof Error ? err.message : String(err)}`
@@ -26270,7 +26626,7 @@ async function commandOrchestrates(commandPath, result) {
26270
26626
  if (endIdx === -1) return false;
26271
26627
  let fm;
26272
26628
  try {
26273
- fm = parseYaml12(raw.slice(3, endIdx).trim());
26629
+ fm = parseYaml13(raw.slice(3, endIdx).trim());
26274
26630
  } catch (err) {
26275
26631
  result.warnings.push(
26276
26632
  `Could not parse frontmatter of command ${commandPath} to verify Decision-13 orchestrator status \u2014 treating its id collision as a duplicate (${err instanceof Error ? err.message : String(err)})`
@@ -26302,10 +26658,10 @@ async function skillDocumentsDecision13Split(skillPath, result) {
26302
26658
  async function listMarkdownFiles(dirPath) {
26303
26659
  const found = [];
26304
26660
  try {
26305
- const entries = await readdir28(dirPath, { withFileTypes: true });
26661
+ const entries = await readdir29(dirPath, { withFileTypes: true });
26306
26662
  for (const entry of entries) {
26307
26663
  if (entry.name.startsWith(".")) continue;
26308
- const childPath = join54(dirPath, entry.name);
26664
+ const childPath = join55(dirPath, entry.name);
26309
26665
  if (entry.isFile() && entry.name.endsWith(".md")) {
26310
26666
  found.push(childPath);
26311
26667
  } else if (entry.isDirectory()) {
@@ -26363,7 +26719,7 @@ async function validateContentBody2(canonicalRoot, result, strictContent) {
26363
26719
  const scanDirs = ["agents", "commands", "rules", "skills", "hooks"];
26364
26720
  const sink = strictContent ? result.errors : result.warnings;
26365
26721
  for (const dir of scanDirs) {
26366
- const dirPath = join54(canonicalRoot, dir);
26722
+ const dirPath = join55(canonicalRoot, dir);
26367
26723
  const files = await listMarkdownFiles(dirPath);
26368
26724
  for (const filePath of files) {
26369
26725
  let raw;
@@ -26380,7 +26736,7 @@ async function validateContentBody2(canonicalRoot, result, strictContent) {
26380
26736
  const endIdx = raw.indexOf("---", 3);
26381
26737
  if (endIdx !== -1) {
26382
26738
  try {
26383
- parsedFm = parseYaml12(raw.slice(3, endIdx).trim());
26739
+ parsedFm = parseYaml13(raw.slice(3, endIdx).trim());
26384
26740
  } catch (err) {
26385
26741
  const message = err instanceof Error ? err.message : String(err);
26386
26742
  sink.push(
@@ -26443,10 +26799,10 @@ function checkAmbiguityGate(body) {
26443
26799
  return { hasMarker, referencesProtocol };
26444
26800
  }
26445
26801
  async function validateAgentToolPolicyCoverage(canonicalRoot, result, userRepoRoot) {
26446
- const agentsDir = join54(canonicalRoot, "agents");
26802
+ const agentsDir = join55(canonicalRoot, "agents");
26447
26803
  let entries;
26448
26804
  try {
26449
- entries = await readdir28(agentsDir, { withFileTypes: true });
26805
+ entries = await readdir29(agentsDir, { withFileTypes: true });
26450
26806
  } catch (err) {
26451
26807
  if (err.code === "ENOENT") {
26452
26808
  entries = [];
@@ -26457,7 +26813,7 @@ async function validateAgentToolPolicyCoverage(canonicalRoot, result, userRepoRo
26457
26813
  const filesystemIds = [];
26458
26814
  for (const entry of entries) {
26459
26815
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
26460
- const filePath = join54(agentsDir, entry.name);
26816
+ const filePath = join55(agentsDir, entry.name);
26461
26817
  let raw;
26462
26818
  try {
26463
26819
  raw = await readFile39(filePath, "utf-8");
@@ -26470,7 +26826,7 @@ async function validateAgentToolPolicyCoverage(canonicalRoot, result, userRepoRo
26470
26826
  if (endIdx === -1) continue;
26471
26827
  let fm;
26472
26828
  try {
26473
- fm = parseYaml12(raw.slice(3, endIdx).trim());
26829
+ fm = parseYaml13(raw.slice(3, endIdx).trim());
26474
26830
  } catch (err) {
26475
26831
  const message = err instanceof Error ? err.message : String(err);
26476
26832
  result.warnings.push(
@@ -26497,10 +26853,10 @@ async function validateAgentToolPolicyCoverage(canonicalRoot, result, userRepoRo
26497
26853
  }
26498
26854
  async function scanUserAgentPolicyCoverage(userRepoRoot, result) {
26499
26855
  if (!userRepoRoot) return;
26500
- const userAgentsDir = join54(resolveUserContentRoot(userRepoRoot), "agents");
26856
+ const userAgentsDir = join55(resolveUserContentRoot(userRepoRoot), "agents");
26501
26857
  let entries;
26502
26858
  try {
26503
- entries = await readdir28(userAgentsDir, { withFileTypes: true });
26859
+ entries = await readdir29(userAgentsDir, { withFileTypes: true });
26504
26860
  } catch (err) {
26505
26861
  if (err.code === "ENOENT") return;
26506
26862
  throw err;
@@ -26509,7 +26865,7 @@ async function scanUserAgentPolicyCoverage(userRepoRoot, result) {
26509
26865
  const known = new Set(ALL_TOOL_CATEGORIES2);
26510
26866
  for (const entry of entries) {
26511
26867
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
26512
- const filePath = join54(userAgentsDir, entry.name);
26868
+ const filePath = join55(userAgentsDir, entry.name);
26513
26869
  let raw;
26514
26870
  try {
26515
26871
  raw = await readFile39(filePath, "utf-8");
@@ -26522,7 +26878,7 @@ async function scanUserAgentPolicyCoverage(userRepoRoot, result) {
26522
26878
  if (endIdx === -1) continue;
26523
26879
  let fm;
26524
26880
  try {
26525
- fm = parseYaml12(raw.slice(3, endIdx).trim());
26881
+ fm = parseYaml13(raw.slice(3, endIdx).trim());
26526
26882
  } catch (err) {
26527
26883
  const message = err instanceof Error ? err.message : String(err);
26528
26884
  result.warnings.push(
@@ -26603,10 +26959,10 @@ function lineDisclaimsCapability(line) {
26603
26959
  return new RegExp(`\\bdo(?:es)?\\s+${notMarker}\\s+need\\b`, "i").test(line) || /\b(?:no longer use|never use|out of scope|not (?:in scope|required))\b/i.test(line) || /^\s*[-*]\s*\*\*Never:\*\*/.test(line);
26604
26960
  }
26605
26961
  async function validateAgentBodyCapabilityCoverage(canonicalRoot, result) {
26606
- const agentsDir = join54(canonicalRoot, "agents");
26962
+ const agentsDir = join55(canonicalRoot, "agents");
26607
26963
  const { getAgentToolPolicy: getAgentToolPolicy2 } = await Promise.resolve().then(() => (init_agentToolAllowlist(), agentToolAllowlist_exports));
26608
26964
  for (const agentId of D5_2_BODY_CAPABILITY_AGENTS) {
26609
- const filePath = join54(agentsDir, `${agentId}.md`);
26965
+ const filePath = join55(agentsDir, `${agentId}.md`);
26610
26966
  let raw;
26611
26967
  try {
26612
26968
  raw = await readFile39(filePath, "utf-8");
@@ -26644,17 +27000,17 @@ async function validateAgentBodyCapabilityCoverage(canonicalRoot, result) {
26644
27000
  }
26645
27001
  }
26646
27002
  async function validateSkillAllowedTools(canonicalRoot, result) {
26647
- const skillsDir = join54(canonicalRoot, "skills");
27003
+ const skillsDir = join55(canonicalRoot, "skills");
26648
27004
  let entries;
26649
27005
  try {
26650
- entries = await readdir28(skillsDir, { withFileTypes: true });
27006
+ entries = await readdir29(skillsDir, { withFileTypes: true });
26651
27007
  } catch (err) {
26652
27008
  if (err.code === "ENOENT") return;
26653
27009
  throw err;
26654
27010
  }
26655
27011
  for (const entry of entries) {
26656
27012
  if (!entry.isDirectory()) continue;
26657
- const skillPath = join54(skillsDir, entry.name, "SKILL.md");
27013
+ const skillPath = join55(skillsDir, entry.name, "SKILL.md");
26658
27014
  let raw;
26659
27015
  try {
26660
27016
  raw = await readFile39(skillPath, "utf-8");
@@ -26667,7 +27023,7 @@ async function validateSkillAllowedTools(canonicalRoot, result) {
26667
27023
  if (endIdx === -1) continue;
26668
27024
  let fm;
26669
27025
  try {
26670
- fm = parseYaml12(raw.slice(3, endIdx).trim());
27026
+ fm = parseYaml13(raw.slice(3, endIdx).trim());
26671
27027
  } catch (err) {
26672
27028
  const message = err instanceof Error ? err.message : String(err);
26673
27029
  result.warnings.push(
@@ -26693,16 +27049,16 @@ async function validateDocsCounts(rootDir) {
26693
27049
  let checked = 0;
26694
27050
  const actual = {};
26695
27051
  const dirs = [
26696
- ["adapters", join54(rootDir, "src/adapters"), (e) => e.endsWith(".ts") && !["base.ts", "index.ts", "canonical.ts", "customization.ts", "types.ts", "mcp-utils.ts", "contextBudget.ts"].includes(e)],
26697
- ["commands", join54(rootDir, "src/cli/commands"), (e) => e.endsWith(".ts")],
26698
- ["agents", join54(rootDir, "agents"), (e) => e.endsWith(".md")],
26699
- ["skills", join54(rootDir, "skills"), (_e) => true],
26700
- ["rules", join54(rootDir, "rules"), (e) => e.endsWith(".md")],
26701
- ["hooks", join54(rootDir, "hooks"), (e) => e.endsWith(".md")]
27052
+ ["adapters", join55(rootDir, "src/adapters"), (e) => e.endsWith(".ts") && !["base.ts", "index.ts", "canonical.ts", "customization.ts", "types.ts", "mcp-utils.ts", "contextBudget.ts"].includes(e)],
27053
+ ["commands", join55(rootDir, "src/cli/commands"), (e) => e.endsWith(".ts")],
27054
+ ["agents", join55(rootDir, "agents"), (e) => e.endsWith(".md")],
27055
+ ["skills", join55(rootDir, "skills"), (_e) => true],
27056
+ ["rules", join55(rootDir, "rules"), (e) => e.endsWith(".md")],
27057
+ ["hooks", join55(rootDir, "hooks"), (e) => e.endsWith(".md")]
26702
27058
  ];
26703
27059
  for (const [name, dir, filter] of dirs) {
26704
27060
  try {
26705
- const entries = await readdir28(dir, { withFileTypes: true });
27061
+ const entries = await readdir29(dir, { withFileTypes: true });
26706
27062
  if (name === "skills") {
26707
27063
  actual[name] = entries.filter((e) => e.isDirectory()).length;
26708
27064
  } else {
@@ -26712,7 +27068,7 @@ async function validateDocsCounts(rootDir) {
26712
27068
  actual[name] = 0;
26713
27069
  }
26714
27070
  }
26715
- const readmePath = join54(rootDir, "README.md");
27071
+ const readmePath = join55(rootDir, "README.md");
26716
27072
  try {
26717
27073
  const readme = await readFile39(readmePath, "utf-8");
26718
27074
  const countPatterns = [
@@ -26744,14 +27100,14 @@ async function scanManagedBlockTampering(rootDir) {
26744
27100
  async function collect(dir) {
26745
27101
  let entries;
26746
27102
  try {
26747
- entries = await readdir28(dir, { withFileTypes: true });
27103
+ entries = await readdir29(dir, { withFileTypes: true });
26748
27104
  } catch (err) {
26749
27105
  const message = err instanceof Error ? err.message : String(err);
26750
27106
  verbose(`validate: tamper-scan readdir(${dir}) skipped \u2014 ${message}`);
26751
27107
  return;
26752
27108
  }
26753
27109
  for (const entry of entries) {
26754
- const full = join54(dir, entry.name);
27110
+ const full = join55(dir, entry.name);
26755
27111
  if (entry.isDirectory()) {
26756
27112
  await collect(full);
26757
27113
  } else if (entry.isFile() && candidateExt.some((e) => entry.name.toLowerCase().endsWith(e))) {
@@ -26760,7 +27116,7 @@ async function scanManagedBlockTampering(rootDir) {
26760
27116
  }
26761
27117
  }
26762
27118
  for (const d of adapterDirs) {
26763
- await collect(join54(rootDir, d));
27119
+ await collect(join55(rootDir, d));
26764
27120
  }
26765
27121
  for (const file of files) {
26766
27122
  let content;
@@ -26859,29 +27215,29 @@ ${transcript}`);
26859
27215
  }
26860
27216
  function runRuleParityCheck(packageRoot, result) {
26861
27217
  runSubValidator(
26862
- join54(packageRoot, "scripts", "validate-rule-parity.ts"),
27218
+ join55(packageRoot, "scripts", "validate-rule-parity.ts"),
26863
27219
  "validate:rule-parity",
26864
27220
  result
26865
27221
  );
26866
27222
  runSubValidator(
26867
- join54(packageRoot, "scripts", "validate-rule-pillar-currency.ts"),
27223
+ join55(packageRoot, "scripts", "validate-rule-pillar-currency.ts"),
26868
27224
  "validate:rule-pillar-currency",
26869
27225
  result
26870
27226
  );
26871
27227
  }
26872
27228
  function runEfficiencyInvariantCheck(packageRoot, result) {
26873
27229
  runSubValidator(
26874
- join54(packageRoot, "scripts", "validate-efficiency-invariants.ts"),
27230
+ join55(packageRoot, "scripts", "validate-efficiency-invariants.ts"),
26875
27231
  "validate:efficiency-invariants",
26876
27232
  result
26877
27233
  );
26878
27234
  runSubValidator(
26879
- join54(packageRoot, "scripts", "validate-bridge-budget.ts"),
27235
+ join55(packageRoot, "scripts", "validate-bridge-budget.ts"),
26880
27236
  "validate:bridge-budget",
26881
27237
  result
26882
27238
  );
26883
27239
  runSubValidator(
26884
- join54(packageRoot, "scripts", "validate-fanout-emission.ts"),
27240
+ join55(packageRoot, "scripts", "validate-fanout-emission.ts"),
26885
27241
  "validate:fanout-emission",
26886
27242
  result
26887
27243
  );
@@ -27025,7 +27381,7 @@ async function validateCommand(opts) {
27025
27381
  if (types.size === 2 && types.has("command") && types.has("skill")) {
27026
27382
  const commandPath = collision.existingType === "command" ? collision.existingPath : collision.duplicatePath;
27027
27383
  const skillPath = collision.existingType === "skill" ? collision.existingPath : collision.duplicatePath;
27028
- const qualifies = await commandOrchestrates(join54(canonicalRoot, commandPath), result);
27384
+ const qualifies = await commandOrchestrates(join55(canonicalRoot, commandPath), result);
27029
27385
  if (!qualifies) {
27030
27386
  result.warnings.push(
27031
27387
  `Content ID collision: "${collision.id}" exists as both a command (${commandPath}) and a skill (${skillPath}), but the command is not orchestrator:true with a non-empty agentPipeline \u2014 per Decision 13 this is a duplicate: collapse to one artifact or promote the command to a real orchestrator (.claude/rules/content-authoring.md item 9)`
@@ -27033,7 +27389,7 @@ async function validateCommand(opts) {
27033
27389
  continue;
27034
27390
  }
27035
27391
  const documented = await skillDocumentsDecision13Split(
27036
- join54(canonicalRoot, skillPath),
27392
+ join55(canonicalRoot, skillPath),
27037
27393
  result
27038
27394
  );
27039
27395
  if (!documented) {
@@ -27086,7 +27442,7 @@ async function validateCommand(opts) {
27086
27442
  let hasCustomizations = false;
27087
27443
  for (const { dir } of CUSTOMIZATION_TYPES2) {
27088
27444
  try {
27089
- const files = await readdir28(join54(rootDir, ".hatch3r", dir));
27445
+ const files = await readdir29(join55(rootDir, ".hatch3r", dir));
27090
27446
  if (files.some((f) => f.endsWith(".customize.yaml") || f.endsWith(".customize.md"))) {
27091
27447
  hasCustomizations = true;
27092
27448
  break;
@@ -27116,7 +27472,7 @@ async function validateCommand(opts) {
27116
27472
  return;
27117
27473
  }
27118
27474
  if (result.errors.length === 0 && result.warnings.length === 0) {
27119
- printBox("Validation", [chalk13.green("All checks passed")], "success");
27475
+ printBox("Validation", [chalk14.green("All checks passed")], "success");
27120
27476
  printNextSteps([
27121
27477
  "Run `hatch3r sync` if you changed `.hatch3r/` overrides since the last generation."
27122
27478
  ]);
@@ -27147,8 +27503,8 @@ async function validateCommand(opts) {
27147
27503
  }
27148
27504
  if (result.errors.length > 0) {
27149
27505
  const summaryLines = [
27150
- `${chalk13.red("\u2716")} ${result.errors.length} error(s)`,
27151
- `${chalk13.yellow("\u26A0")} ${result.warnings.length} warning(s)`
27506
+ `${chalk14.red("\u2716")} ${result.errors.length} error(s)`,
27507
+ `${chalk14.yellow("\u26A0")} ${result.warnings.length} warning(s)`
27152
27508
  ];
27153
27509
  printBox("Validation failed", summaryLines, "error");
27154
27510
  printNextSteps(["Re-run with `--verbose` to see each failing check."]);
@@ -27160,8 +27516,8 @@ async function validateCommand(opts) {
27160
27516
  );
27161
27517
  } else {
27162
27518
  const summaryLines = [
27163
- `${chalk13.green("\u2714")} 0 errors`,
27164
- `${chalk13.yellow("\u26A0")} ${result.warnings.length} warning(s)`
27519
+ `${chalk14.green("\u2714")} 0 errors`,
27520
+ `${chalk14.yellow("\u26A0")} ${result.warnings.length} warning(s)`
27165
27521
  ];
27166
27522
  printBox("Validation passed", summaryLines, "success");
27167
27523
  printNextSteps([
@@ -27174,7 +27530,7 @@ async function validateCommand(opts) {
27174
27530
  }
27175
27531
  }
27176
27532
  async function validateEnvMcpSecrets(rootDir, result) {
27177
- const envMcpPath = join54(rootDir, ".env.mcp");
27533
+ const envMcpPath = join55(rootDir, ".env.mcp");
27178
27534
  if (!existsSync5(envMcpPath)) return;
27179
27535
  try {
27180
27536
  const raw = await readFile39(envMcpPath, "utf-8");
@@ -27196,7 +27552,7 @@ async function validateEnvMcpSecrets(rootDir, result) {
27196
27552
  async function validateCanonicalDescriptionQuality(rootDir, result) {
27197
27553
  const __filename = fileURLToPath9(import.meta.url);
27198
27554
  const packageRoot = findPackageRoot(dirname26(__filename));
27199
- const canonicalRoot = existsSync5(join54(rootDir, "agents")) && existsSync5(join54(rootDir, "skills")) && existsSync5(join54(rootDir, "rules")) && existsSync5(join54(rootDir, "commands")) ? rootDir : packageRoot;
27555
+ const canonicalRoot = existsSync5(join55(rootDir, "agents")) && existsSync5(join55(rootDir, "skills")) && existsSync5(join55(rootDir, "rules")) && existsSync5(join55(rootDir, "commands")) ? rootDir : packageRoot;
27200
27556
  try {
27201
27557
  const index = await buildContentIndex(canonicalRoot);
27202
27558
  if (index.items.length === 0) return;
@@ -27235,24 +27591,24 @@ async function validateSecurityCompliance(result) {
27235
27591
  }
27236
27592
  function printCustomizationHint() {
27237
27593
  console.log();
27238
- info(chalk13.bold("Customization mechanisms detected. Quick reference:"));
27239
- console.log(chalk13.dim(" 1. hatch3r- prefix: Files prefixed with hatch3r- are managed by hatch3r and"));
27240
- console.log(chalk13.dim(" overwritten on update. Do not edit these directly."));
27241
- console.log(chalk13.dim(" 2. Managed blocks: Sections between <!-- HATCH3R:BEGIN --> and"));
27242
- console.log(chalk13.dim(" <!-- HATCH3R:END --> are auto-updated. Add content outside these markers."));
27243
- console.log(chalk13.dim(" 3. .customize.yaml/.md: Place in .hatch3r/{type}/ to override model, scope,"));
27244
- console.log(chalk13.dim(" description, or disable items. Use .customize.md for content additions."));
27245
- console.log(chalk13.dim(" See: https://docs.hatch3r.com/docs/guides/customization"));
27594
+ info(chalk14.bold("Customization mechanisms detected. Quick reference:"));
27595
+ console.log(chalk14.dim(" 1. hatch3r- prefix: Files prefixed with hatch3r- are managed by hatch3r and"));
27596
+ console.log(chalk14.dim(" overwritten on update. Do not edit these directly."));
27597
+ console.log(chalk14.dim(" 2. Managed blocks: Sections between <!-- HATCH3R:BEGIN --> and"));
27598
+ console.log(chalk14.dim(" <!-- HATCH3R:END --> are auto-updated. Add content outside these markers."));
27599
+ console.log(chalk14.dim(" 3. .customize.yaml/.md: Place in .hatch3r/{type}/ to override model, scope,"));
27600
+ console.log(chalk14.dim(" description, or disable items. Use .customize.md for content additions."));
27601
+ console.log(chalk14.dim(" See: https://docs.hatch3r.com/docs/guides/customization"));
27246
27602
  }
27247
27603
 
27248
27604
  // src/cli/commands/verify.ts
27249
27605
  init_types();
27250
- import chalk15 from "chalk";
27606
+ import chalk16 from "chalk";
27251
27607
 
27252
27608
  // src/cli/commands/status.ts
27253
- import { access as access17, readFile as readFile40 } from "fs/promises";
27254
- import { join as join55 } from "path";
27255
- import chalk14 from "chalk";
27609
+ import { access as access18, readFile as readFile40 } from "fs/promises";
27610
+ import { join as join56 } from "path";
27611
+ import chalk15 from "chalk";
27256
27612
  init_types();
27257
27613
  init_managedBlocks();
27258
27614
  init_version();
@@ -27260,7 +27616,7 @@ init_ui();
27260
27616
  async function loadProvenanceBaseline(rootDir) {
27261
27617
  const baseline = /* @__PURE__ */ new Map();
27262
27618
  try {
27263
- const raw = await readFile40(join55(rootDir, HATCH3R_DIR, "provenance.json"), "utf-8");
27619
+ const raw = await readFile40(join56(rootDir, HATCH3R_DIR, "provenance.json"), "utf-8");
27264
27620
  const parsed = JSON.parse(raw);
27265
27621
  for (const entry of parsed.outputs ?? []) {
27266
27622
  if (typeof entry.path === "string" && typeof entry.contentHash === "string") {
@@ -27307,7 +27663,7 @@ async function computeAdapterDrift(rootDir, manifest) {
27307
27663
  verbose(`${tool}: ${outputs.length} output file(s) to check`);
27308
27664
  for (const out of outputs) {
27309
27665
  seenPaths.add(out.path);
27310
- const destPath = join55(rootDir, out.path);
27666
+ const destPath = join56(rootDir, out.path);
27311
27667
  try {
27312
27668
  const existing = await readFile40(destPath, "utf-8");
27313
27669
  const existingBlock = extractManagedBlock(existing, out.path);
@@ -27363,7 +27719,7 @@ async function computeAdapterDrift(rootDir, manifest) {
27363
27719
  if (seenPaths.has(tracked)) continue;
27364
27720
  if (NON_ADAPTER_MANAGED_FILES.has(tracked)) continue;
27365
27721
  try {
27366
- await access17(join55(rootDir, tracked));
27722
+ await access18(join56(rootDir, tracked));
27367
27723
  entries.push({ path: tracked, tool: "(unowned)", status: "unexpected" });
27368
27724
  counts.unexpected++;
27369
27725
  } catch (err) {
@@ -27376,14 +27732,14 @@ async function computeAdapterDrift(rootDir, manifest) {
27376
27732
  function driftKindTag(kind) {
27377
27733
  switch (kind) {
27378
27734
  case "user-modified":
27379
- return chalk14.yellow(" (your edit)");
27735
+ return chalk15.yellow(" (your edit)");
27380
27736
  case "canonical-outdated":
27381
- return chalk14.cyan(" (canonical changed)");
27737
+ return chalk15.cyan(" (canonical changed)");
27382
27738
  case "both":
27383
- return chalk14.red(" (your edit + canonical changed)");
27739
+ return chalk15.red(" (your edit + canonical changed)");
27384
27740
  case "unknown":
27385
27741
  default:
27386
- return chalk14.dim(" (no baseline)");
27742
+ return chalk15.dim(" (no baseline)");
27387
27743
  }
27388
27744
  }
27389
27745
  function renderDiffSummaryLines(report) {
@@ -27391,16 +27747,16 @@ function renderDiffSummaryLines(report) {
27391
27747
  for (const entry of report.entries) {
27392
27748
  switch (entry.status) {
27393
27749
  case "in-sync":
27394
- lines.push(`${chalk14.dim("= unchanged")} ${entry.path}`);
27750
+ lines.push(`${chalk15.dim("= unchanged")} ${entry.path}`);
27395
27751
  break;
27396
27752
  case "modified":
27397
- lines.push(`${chalk14.yellow("~ modified")} ${entry.path}`);
27753
+ lines.push(`${chalk15.yellow("~ modified")} ${entry.path}`);
27398
27754
  break;
27399
27755
  case "missing":
27400
- lines.push(`${chalk14.green("+ added")} ${entry.path}`);
27756
+ lines.push(`${chalk15.green("+ added")} ${entry.path}`);
27401
27757
  break;
27402
27758
  case "unexpected":
27403
- lines.push(`${chalk14.red("! orphan")} ${entry.path}`);
27759
+ lines.push(`${chalk15.red("! orphan")} ${entry.path}`);
27404
27760
  break;
27405
27761
  }
27406
27762
  }
@@ -27415,20 +27771,20 @@ function renderDriftLines(report) {
27415
27771
  }
27416
27772
  const lines = [];
27417
27773
  for (const [tool, items] of byTool) {
27418
- lines.push(chalk14.bold(`${tool}:`));
27774
+ lines.push(chalk15.bold(`${tool}:`));
27419
27775
  for (const entry of items) {
27420
27776
  switch (entry.status) {
27421
27777
  case "in-sync":
27422
- lines.push(` ${chalk14.green("=")} ${entry.path}`);
27778
+ lines.push(` ${chalk15.green("=")} ${entry.path}`);
27423
27779
  break;
27424
27780
  case "modified":
27425
- lines.push(` ${chalk14.yellow("~")} ${entry.path} ${chalk14.dim("(drifted)")}${driftKindTag(entry.driftKind)}`);
27781
+ lines.push(` ${chalk15.yellow("~")} ${entry.path} ${chalk15.dim("(drifted)")}${driftKindTag(entry.driftKind)}`);
27426
27782
  break;
27427
27783
  case "missing":
27428
- lines.push(` ${chalk14.red("+")} ${entry.path} ${chalk14.dim("(missing)")}`);
27784
+ lines.push(` ${chalk15.red("+")} ${entry.path} ${chalk15.dim("(missing)")}`);
27429
27785
  break;
27430
27786
  case "unexpected":
27431
- lines.push(` ${chalk14.red("!")} ${entry.path} ${chalk14.dim("(unexpected: not produced by any current adapter)")}`);
27787
+ lines.push(` ${chalk15.red("!")} ${entry.path} ${chalk15.dim("(unexpected: not produced by any current adapter)")}`);
27432
27788
  break;
27433
27789
  }
27434
27790
  }
@@ -27482,21 +27838,21 @@ async function statusCommand(opts) {
27482
27838
  }
27483
27839
  if (!isQuiet()) console.log();
27484
27840
  const summaryLines = [
27485
- `${chalk14.green("=")} In sync: ${report.counts.synced}`
27841
+ `${chalk15.green("=")} In sync: ${report.counts.synced}`
27486
27842
  ];
27487
27843
  if (report.counts.modified > 0) {
27488
- summaryLines.push(`${chalk14.yellow("~")} Drifted: ${report.counts.modified}`);
27844
+ summaryLines.push(`${chalk15.yellow("~")} Drifted: ${report.counts.modified}`);
27489
27845
  const dk = report.driftKindCounts;
27490
- if (dk.userModified > 0) summaryLines.push(` ${chalk14.yellow("\u2022")} your edits: ${dk.userModified}`);
27491
- if (dk.canonicalOutdated > 0) summaryLines.push(` ${chalk14.cyan("\u2022")} canonical changed: ${dk.canonicalOutdated}`);
27492
- if (dk.both > 0) summaryLines.push(` ${chalk14.red("\u2022")} edits + canonical: ${dk.both}`);
27493
- if (dk.unknown > 0) summaryLines.push(` ${chalk14.dim("\u2022")} no baseline: ${dk.unknown}`);
27846
+ if (dk.userModified > 0) summaryLines.push(` ${chalk15.yellow("\u2022")} your edits: ${dk.userModified}`);
27847
+ if (dk.canonicalOutdated > 0) summaryLines.push(` ${chalk15.cyan("\u2022")} canonical changed: ${dk.canonicalOutdated}`);
27848
+ if (dk.both > 0) summaryLines.push(` ${chalk15.red("\u2022")} edits + canonical: ${dk.both}`);
27849
+ if (dk.unknown > 0) summaryLines.push(` ${chalk15.dim("\u2022")} no baseline: ${dk.unknown}`);
27494
27850
  }
27495
27851
  if (report.counts.missing > 0) {
27496
- summaryLines.push(`${chalk14.red("+")} Missing: ${report.counts.missing}`);
27852
+ summaryLines.push(`${chalk15.red("+")} Missing: ${report.counts.missing}`);
27497
27853
  }
27498
27854
  if (report.counts.unexpected > 0) {
27499
- summaryLines.push(`${chalk14.red("!")} Unexpected: ${report.counts.unexpected}`);
27855
+ summaryLines.push(`${chalk15.red("!")} Unexpected: ${report.counts.unexpected}`);
27500
27856
  }
27501
27857
  const hasDrift = report.counts.modified > 0 || report.counts.missing > 0 || report.counts.unexpected > 0;
27502
27858
  const style = hasDrift ? "info" : "success";
@@ -27517,30 +27873,30 @@ async function statusCommand(opts) {
27517
27873
  const dk = report.driftKindCounts;
27518
27874
  if (dk.canonicalOutdated > 0 && dk.userModified === 0 && dk.both === 0 && dk.unknown === 0) {
27519
27875
  info(
27520
- `Run ${chalk14.bold("hatch3r sync")} to update ${dk.canonicalOutdated} file(s) whose ${chalk14.cyan("canonical content changed")}. No local edits were detected, so regenerating is safe.`
27876
+ `Run ${chalk15.bold("hatch3r sync")} to update ${dk.canonicalOutdated} file(s) whose ${chalk15.cyan("canonical content changed")}. No local edits were detected, so regenerating is safe.`
27521
27877
  );
27522
27878
  } else if ((dk.userModified > 0 || dk.both > 0) && dk.canonicalOutdated === 0 && dk.unknown === 0) {
27523
27879
  info(
27524
- `${chalk14.bold("hatch3r sync overwrites the managed block.")} ${dk.userModified + dk.both} drifted file(s) carry ${chalk14.yellow("your edits")}${dk.both > 0 ? " (some also have newer canonical content)" : ""} \u2014 back them up before running sync if you want to keep them.`
27880
+ `${chalk15.bold("hatch3r sync overwrites the managed block.")} ${dk.userModified + dk.both} drifted file(s) carry ${chalk15.yellow("your edits")}${dk.both > 0 ? " (some also have newer canonical content)" : ""} \u2014 back them up before running sync if you want to keep them.`
27525
27881
  );
27526
27882
  } else {
27527
27883
  info(
27528
- `Drifted files are tagged above: ${chalk14.cyan("(canonical changed)")} is safe to ${chalk14.bold("hatch3r sync")}; ${chalk14.yellow("(your edit)")} / ${chalk14.red("(your edit + canonical changed)")} would be overwritten \u2014 back up first. ${chalk14.dim("(no baseline)")} means sync has not run since this file was tracked; run sync once to record a baseline for future attribution.`
27884
+ `Drifted files are tagged above: ${chalk15.cyan("(canonical changed)")} is safe to ${chalk15.bold("hatch3r sync")}; ${chalk15.yellow("(your edit)")} / ${chalk15.red("(your edit + canonical changed)")} would be overwritten \u2014 back up first. ${chalk15.dim("(no baseline)")} means sync has not run since this file was tracked; run sync once to record a baseline for future attribution.`
27529
27885
  );
27530
27886
  }
27531
27887
  console.log();
27532
27888
  }
27533
27889
  if (report.counts.missing > 0) {
27534
- info(`Run ${chalk14.bold("hatch3r sync")} to regenerate missing files.`);
27890
+ info(`Run ${chalk15.bold("hatch3r sync")} to regenerate missing files.`);
27535
27891
  console.log();
27536
27892
  }
27537
27893
  if (report.counts.unexpected > 0) {
27538
- info(`Unexpected files are tracked in the manifest but no longer produced. Run ${chalk14.bold("hatch3r clean")} to remove them, or remove them manually.`);
27894
+ info(`Unexpected files are tracked in the manifest but no longer produced. Run ${chalk15.bold("hatch3r clean")} to remove them, or remove them manually.`);
27539
27895
  console.log();
27540
27896
  }
27541
27897
  if (hasDrift) {
27542
27898
  console.log(
27543
- chalk14.dim(
27899
+ chalk15.dim(
27544
27900
  " Note: drift detection covers hatch3r-managed blocks only (HATCH3R:BEGIN/END). Content outside the markers is yours \u2014 use `git diff` to inspect it."
27545
27901
  )
27546
27902
  );
@@ -27557,13 +27913,13 @@ async function statusCommand(opts) {
27557
27913
  cliLines.push("");
27558
27914
  for (const r of missing) {
27559
27915
  if (r.extensionMissing) {
27560
- cliLines.push(` ${chalk14.yellow("\u2717")} ${r.id} \u2014 extension missing: ${r.extensionMissing}`);
27916
+ cliLines.push(` ${chalk15.yellow("\u2717")} ${r.id} \u2014 extension missing: ${r.extensionMissing}`);
27561
27917
  } else {
27562
- cliLines.push(` ${chalk14.yellow("\u2717")} ${r.id} not on PATH`);
27918
+ cliLines.push(` ${chalk15.yellow("\u2717")} ${r.id} not on PATH`);
27563
27919
  }
27564
27920
  }
27565
27921
  cliLines.push("");
27566
- cliLines.push(chalk14.dim(`Run \`npx hatch3r cli-tools install\` to see install commands.`));
27922
+ cliLines.push(chalk15.dim(`Run \`npx hatch3r cli-tools install\` to see install commands.`));
27567
27923
  }
27568
27924
  printBox("CLI tools", cliLines, missing.length === 0 ? "success" : "info");
27569
27925
  }
@@ -27618,7 +27974,7 @@ async function statusCommand(opts) {
27618
27974
  spaceLines.push(` ${a.axis.padEnd(14)}${a.count} metric(s), mean ${a.mean.toFixed(2)}`);
27619
27975
  }
27620
27976
  spaceLines.push(
27621
- chalk14.dim(` ${spaceTelemetry.recordCount} record(s) over the last ${SPACE_TELEMETRY_WINDOW_DAYS} day(s)`)
27977
+ chalk15.dim(` ${spaceTelemetry.recordCount} record(s) over the last ${SPACE_TELEMETRY_WINDOW_DAYS} day(s)`)
27622
27978
  );
27623
27979
  printBox("Developer productivity (SPACE)", spaceLines, "info");
27624
27980
  }
@@ -27629,16 +27985,16 @@ async function statusCommand(opts) {
27629
27985
  );
27630
27986
  if (customizationSummary.entries.length > 0) {
27631
27987
  const c = customizationSummary.counts;
27632
- const oneLine = `${chalk14.bold(String(c.active))} active` + (c.skipped > 0 ? `, ${chalk14.yellow(String(c.skipped))} skipped` : "") + (c.failed > 0 ? `, ${chalk14.red(String(c.failed))} failed` : "") + // D10-29: inert overrides (deselected artifact, no adapter emits them)
27988
+ const oneLine = `${chalk15.bold(String(c.active))} active` + (c.skipped > 0 ? `, ${chalk15.yellow(String(c.skipped))} skipped` : "") + (c.failed > 0 ? `, ${chalk15.red(String(c.failed))} failed` : "") + // D10-29: inert overrides (deselected artifact, no adapter emits them)
27633
27989
  // surface in the one-liner so an override that silently does nothing is
27634
27990
  // not hidden behind the active count.
27635
- (c.inert > 0 ? `, ${chalk14.yellow(String(c.inert))} inert` : "");
27991
+ (c.inert > 0 ? `, ${chalk15.yellow(String(c.inert))} inert` : "");
27636
27992
  const customLines = [oneLine];
27637
27993
  if (opts?.verbose) {
27638
27994
  customLines.push("");
27639
27995
  for (const entry of customizationSummary.entries) {
27640
- const icon = entry.outcome === "failed" ? chalk14.red("\u2717") : entry.outcome === "skipped" ? chalk14.yellow("\u25CB") : entry.outcome === "inert" ? chalk14.yellow("\u2298") : entry.outcome === "active" ? chalk14.green("\u2713") : chalk14.dim("\xB7");
27641
- const reason = entry.reason ? chalk14.dim(` \u2014 ${entry.reason}`) : "";
27996
+ const icon = entry.outcome === "failed" ? chalk15.red("\u2717") : entry.outcome === "skipped" ? chalk15.yellow("\u25CB") : entry.outcome === "inert" ? chalk15.yellow("\u2298") : entry.outcome === "active" ? chalk15.green("\u2713") : chalk15.dim("\xB7");
27997
+ const reason = entry.reason ? chalk15.dim(` \u2014 ${entry.reason}`) : "";
27642
27998
  customLines.push(` ${icon} ${entry.type}/${entry.id}${reason}`);
27643
27999
  }
27644
28000
  } else {
@@ -27651,18 +28007,18 @@ async function statusCommand(opts) {
27651
28007
  if (visible.length > 0) {
27652
28008
  customLines.push("");
27653
28009
  for (const entry of visible) {
27654
- const icon = entry.outcome === "failed" ? chalk14.red("\u2717") : entry.outcome === "skipped" ? chalk14.yellow("\u25CB") : entry.outcome === "inert" ? chalk14.yellow("\u2298") : chalk14.green("\u2713");
27655
- const reason = entry.reason ? chalk14.dim(` \u2014 ${entry.reason}`) : "";
28010
+ const icon = entry.outcome === "failed" ? chalk15.red("\u2717") : entry.outcome === "skipped" ? chalk15.yellow("\u25CB") : entry.outcome === "inert" ? chalk15.yellow("\u2298") : chalk15.green("\u2713");
28011
+ const reason = entry.reason ? chalk15.dim(` \u2014 ${entry.reason}`) : "";
27656
28012
  customLines.push(` ${icon} ${entry.type}/${entry.id}${reason}`);
27657
28013
  }
27658
28014
  if (hiddenActive > 0) {
27659
28015
  customLines.push(
27660
- chalk14.dim(` \u2026 +${hiddenActive} more active (run with --verbose for the full list)`)
28016
+ chalk15.dim(` \u2026 +${hiddenActive} more active (run with --verbose for the full list)`)
27661
28017
  );
27662
28018
  }
27663
28019
  }
27664
28020
  if (c.failed > 0 || c.skipped > 0 || c.inert > 0) {
27665
- customLines.push(chalk14.dim(` Run \`hatch3r explain --customizations\` for the per-artifact table.`));
28021
+ customLines.push(chalk15.dim(` Run \`hatch3r explain --customizations\` for the per-artifact table.`));
27666
28022
  }
27667
28023
  }
27668
28024
  printBox(
@@ -27678,28 +28034,28 @@ async function statusCommand(opts) {
27678
28034
  if (wsManifest && wsManifest.repos.length > 0) {
27679
28035
  const wsLines = [];
27680
28036
  for (const repo of wsManifest.repos) {
27681
- const icon = repo.sync ? chalk14.green("\u2713") : chalk14.dim("\u25CB");
28037
+ const icon = repo.sync ? chalk15.green("\u2713") : chalk15.dim("\u25CB");
27682
28038
  let detail;
27683
28039
  if (!repo.sync) {
27684
- detail = chalk14.dim("sync disabled");
28040
+ detail = chalk15.dim("sync disabled");
27685
28041
  } else if (repo.lastSync) {
27686
28042
  const elapsed = Math.max(0, Date.now() - new Date(repo.lastSync).getTime());
27687
28043
  const hours = Math.floor(elapsed / (1e3 * 60 * 60));
27688
28044
  const timeAgo = hours < 1 ? "just now" : hours < 24 ? `${hours}h ago` : `${Math.floor(hours / 24)}d ago`;
27689
28045
  detail = `synced ${timeAgo}`;
27690
28046
  } else {
27691
- detail = chalk14.yellow("never synced");
28047
+ detail = chalk15.yellow("never synced");
27692
28048
  }
27693
- const identity = repo.owner && repo.repo ? chalk14.dim(`${repo.owner}/${repo.repo}`) : "";
27694
- const branch = repo.defaultBranch ? chalk14.dim(`[${repo.defaultBranch}]`) : "";
28049
+ const identity = repo.owner && repo.repo ? chalk15.dim(`${repo.owner}/${repo.repo}`) : "";
28050
+ const branch = repo.defaultBranch ? chalk15.dim(`[${repo.defaultBranch}]`) : "";
27695
28051
  const identityPart = identity || branch ? ` ${identity} ${branch}` : "";
27696
- wsLines.push(`${icon} ${repo.name ?? repo.path}${identityPart} ${chalk14.dim(`(${detail})`)}`);
28052
+ wsLines.push(`${icon} ${repo.name ?? repo.path}${identityPart} ${chalk15.dim(`(${detail})`)}`);
27697
28053
  }
27698
28054
  printBox(`Workspace: ${wsManifest.name} (${wsManifest.repos.length} repos)`, wsLines, "info");
27699
28055
  }
27700
28056
  if (manifest.workspace) {
27701
28057
  const wsInfo = [
27702
- `Managed by workspace at ${chalk14.bold(manifest.workspace.rootPath)}`,
28058
+ `Managed by workspace at ${chalk15.bold(manifest.workspace.rootPath)}`,
27703
28059
  `Last synced: ${manifest.workspace.lastSync ? new Date(manifest.workspace.lastSync).toLocaleString() : "never"}`
27704
28060
  ];
27705
28061
  printBox("Workspace member", wsInfo, "info");
@@ -27713,16 +28069,16 @@ var DEFAULT_MAX_FIX_ATTEMPTS = 2;
27713
28069
  var MAX_FIX_ATTEMPTS_CEILING = 5;
27714
28070
  function buildSummaryLines(report) {
27715
28071
  const summaryLines = [
27716
- `${chalk15.green("\u2714")} In sync: ${report.counts.synced}`
28072
+ `${chalk16.green("\u2714")} In sync: ${report.counts.synced}`
27717
28073
  ];
27718
28074
  if (report.counts.modified > 0) {
27719
- summaryLines.push(`${chalk15.yellow("~")} Drifted: ${report.counts.modified}`);
28075
+ summaryLines.push(`${chalk16.yellow("~")} Drifted: ${report.counts.modified}`);
27720
28076
  }
27721
28077
  if (report.counts.missing > 0) {
27722
- summaryLines.push(`${chalk15.red("+")} Missing: ${report.counts.missing}`);
28078
+ summaryLines.push(`${chalk16.red("+")} Missing: ${report.counts.missing}`);
27723
28079
  }
27724
28080
  if (report.counts.unexpected > 0) {
27725
- summaryLines.push(`${chalk15.red("!")} Unexpected: ${report.counts.unexpected}`);
28081
+ summaryLines.push(`${chalk16.red("!")} Unexpected: ${report.counts.unexpected}`);
27726
28082
  }
27727
28083
  return summaryLines;
27728
28084
  }
@@ -27756,7 +28112,7 @@ function maybePrintVerboseDrift(options, report) {
27756
28112
  }
27757
28113
  function printTamperWarnings(tamperWarnings) {
27758
28114
  if (tamperWarnings.length === 0) return;
27759
- const lines = tamperWarnings.map((w) => `${chalk15.yellow("!")} ${w}`);
28115
+ const lines = tamperWarnings.map((w) => `${chalk16.yellow("!")} ${w}`);
27760
28116
  printBox(
27761
28117
  `Managed-block structural warnings (${tamperWarnings.length})`,
27762
28118
  lines,
@@ -27844,7 +28200,7 @@ async function verifyCommand(options = {}) {
27844
28200
  maybePrintDiffSummary(options, report);
27845
28201
  printTamperWarnings(tamperWarnings);
27846
28202
  if (options.fix) {
27847
- info(`--fix could not clear all drift \u2014 run ${chalk15.bold("hatch3r sync")} or inspect the failing tool(s).`);
28203
+ info(`--fix could not clear all drift \u2014 run ${chalk16.bold("hatch3r sync")} or inspect the failing tool(s).`);
27848
28204
  } else {
27849
28205
  info(recoveryHint);
27850
28206
  printNextSteps([
@@ -27853,7 +28209,7 @@ async function verifyCommand(options = {}) {
27853
28209
  }
27854
28210
  if (!isQuiet()) {
27855
28211
  console.log(
27856
- chalk15.dim(
28212
+ chalk16.dim(
27857
28213
  " Note: verify covers hatch3r-managed blocks only (HATCH3R:BEGIN/END). Content outside the markers is yours \u2014 use `git diff` to inspect it."
27858
28214
  )
27859
28215
  );
@@ -27872,11 +28228,11 @@ init_observability();
27872
28228
  init_costEstimator();
27873
28229
  init_types();
27874
28230
  init_version();
27875
- import { readFile as readFile41, access as access18 } from "fs/promises";
27876
- import { join as join56, dirname as dirname27 } from "path";
28231
+ import { readFile as readFile41, access as access19 } from "fs/promises";
28232
+ import { join as join57, dirname as dirname27 } from "path";
27877
28233
  import { fileURLToPath as fileURLToPath10 } from "url";
27878
- import chalk16 from "chalk";
27879
- import { parse as parseYaml13 } from "yaml";
28234
+ import chalk17 from "chalk";
28235
+ import { parse as parseYaml14 } from "yaml";
27880
28236
  init_ui();
27881
28237
  var __dirname6 = dirname27(fileURLToPath10(import.meta.url));
27882
28238
  var FRONTMATTER_REGEX4 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -27887,12 +28243,12 @@ async function resolveCommandPath(rootDir, commandId) {
27887
28243
  // Wave 7: legacy `.agents/commands/` probe for pre-1.9 installs that
27888
28244
  // still ship a project-side canonical tree. New installs resolve via
27889
28245
  // the bundled package root (second candidate).
27890
- join56(rootDir, ".agents", "commands", filename),
27891
- join56(findPackageRoot(__dirname6), "commands", filename)
28246
+ join57(rootDir, ".agents", "commands", filename),
28247
+ join57(findPackageRoot(__dirname6), "commands", filename)
27892
28248
  ];
27893
28249
  for (const candidate of candidates) {
27894
28250
  try {
27895
- await access18(candidate);
28251
+ await access19(candidate);
27896
28252
  return candidate;
27897
28253
  } catch (err) {
27898
28254
  if (err.code !== "ENOENT") throw err;
@@ -27916,7 +28272,7 @@ function parseCommandFrontmatter(raw, filePath) {
27916
28272
  );
27917
28273
  }
27918
28274
  const [, frontmatterStr] = match;
27919
- const parsed = parseYaml13(frontmatterStr ?? "");
28275
+ const parsed = parseYaml14(frontmatterStr ?? "");
27920
28276
  if (!parsed || typeof parsed !== "object") {
27921
28277
  throw new HatchError(
27922
28278
  `Invalid frontmatter in ${filePath}: expected YAML object.`,
@@ -28018,12 +28374,12 @@ function computeTierRows(fm, bodyCharCount, agentDefCharCounts, options) {
28018
28374
  async function resolveAgentDefCharCounts(fm) {
28019
28375
  const ids = new Set(fm.agentPipeline);
28020
28376
  if (fm.triageTiers.includes(3)) ids.add(RESEARCH_MODE_AGENT_ID);
28021
- const agentsDir = join56(findPackageRoot(__dirname6), "agents");
28377
+ const agentsDir = join57(findPackageRoot(__dirname6), "agents");
28022
28378
  const counts = /* @__PURE__ */ new Map();
28023
28379
  await Promise.all(
28024
28380
  [...ids].map(async (id) => {
28025
28381
  try {
28026
- const raw = await readFile41(join56(agentsDir, `${id}.md`), "utf-8");
28382
+ const raw = await readFile41(join57(agentsDir, `${id}.md`), "utf-8");
28027
28383
  counts.set(id, raw.length);
28028
28384
  } catch (err) {
28029
28385
  verbose(
@@ -28082,7 +28438,7 @@ async function explainCommand(opts) {
28082
28438
  if (modesRequested > 1) {
28083
28439
  error("Conflicting flags: --cost, --customizations, --source, and --efficiency are mutually exclusive.");
28084
28440
  if (!isQuiet()) {
28085
- console.log(chalk16.dim(" Pick one mode per invocation."));
28441
+ console.log(chalk17.dim(" Pick one mode per invocation."));
28086
28442
  console.log();
28087
28443
  }
28088
28444
  throw new HatchError(
@@ -28095,10 +28451,10 @@ async function explainCommand(opts) {
28095
28451
  if (modesRequested === 0) {
28096
28452
  error("Missing required mode flag: pass --cost <command-id>, --customizations, --source <output-path>, OR --efficiency.");
28097
28453
  if (!isQuiet()) {
28098
- console.log(chalk16.dim(" Example: hatch3r explain --cost hatch3r-quick-change"));
28099
- console.log(chalk16.dim(" Example: hatch3r explain --customizations"));
28100
- console.log(chalk16.dim(" Example: hatch3r explain --source CLAUDE.md"));
28101
- console.log(chalk16.dim(" Example: hatch3r explain --efficiency"));
28454
+ console.log(chalk17.dim(" Example: hatch3r explain --cost hatch3r-quick-change"));
28455
+ console.log(chalk17.dim(" Example: hatch3r explain --customizations"));
28456
+ console.log(chalk17.dim(" Example: hatch3r explain --source CLAUDE.md"));
28457
+ console.log(chalk17.dim(" Example: hatch3r explain --efficiency"));
28102
28458
  console.log();
28103
28459
  }
28104
28460
  throw new HatchError(
@@ -28224,24 +28580,24 @@ async function explainCommand(opts) {
28224
28580
  `${"Tier".padEnd(COL_LABEL)}${"Sub-agents".padEnd(COL_AGENTS)}${"Input".padEnd(COL_TOKENS)}${"Output".padEnd(COL_TOKENS)}${"Total".padEnd(COL_TOKENS)}${"USD".padEnd(COL_USD)}`
28225
28581
  );
28226
28582
  const tableWidth = COL_LABEL + COL_AGENTS + COL_TOKENS * 3 + COL_USD;
28227
- tableLines.push(chalk16.dim("\u2500".repeat(tableWidth)));
28583
+ tableLines.push(chalk17.dim("\u2500".repeat(tableWidth)));
28228
28584
  for (const row of rows) {
28229
28585
  tableLines.push(
28230
28586
  `${row.label.padEnd(COL_LABEL)}${String(row.subAgents).padEnd(COL_AGENTS)}${formatTokens(row.inputTokens).padEnd(COL_TOKENS)}${formatTokens(row.outputTokens).padEnd(COL_TOKENS)}${formatTokens(row.totalTokens).padEnd(COL_TOKENS)}${formatUsd(row.usd).padEnd(COL_USD)}`
28231
28587
  );
28232
28588
  }
28233
- tableLines.push(chalk16.dim("\u2500".repeat(tableWidth)));
28589
+ tableLines.push(chalk17.dim("\u2500".repeat(tableWidth)));
28234
28590
  tableLines.push(
28235
28591
  `${"All tiers (sum)".padEnd(COL_LABEL)}${"\u2014".padEnd(COL_AGENTS)}${"".padEnd(COL_TOKENS)}${"".padEnd(COL_TOKENS)}${formatTokens(totalTokens).padEnd(COL_TOKENS)}${formatUsd(totalUsd).padEnd(COL_USD)}`
28236
28592
  );
28237
28593
  printBox("Per-tier cost estimate", tableLines, "info");
28238
28594
  info(
28239
- chalk16.dim(
28595
+ chalk17.dim(
28240
28596
  `Rates: $${inputRate}/1M input, $${outputRate}/1M output${cacheHitRatio > 0 ? `, ${Math.round(cacheHitRatio * 100)}% input cache-hit (cached input billed at 0.1x)` : ""}. Token counts use CHARS_PER_TOKEN=${CHARS_PER_TOKEN} (English prose heuristic).`
28241
28597
  )
28242
28598
  );
28243
28599
  info(
28244
- chalk16.dim(
28600
+ chalk17.dim(
28245
28601
  `Input basis: orchestrator body once (${formatTokens(estimateTokens(bodyCharCount, CHARS_PER_TOKEN))} tokens) + per sub-agent its own agents/<id>.md + a ~${formatTokens(estimateTokens(TASK_CONTEXT_ALLOWANCE_CHARS, CHARS_PER_TOKEN))}-token task allowance. An unsized actor (tier-1 inline path / missing agent def) bills at ~${formatTokens(estimateTokens(UNKNOWN_AGENT_DEF_CHARS, CHARS_PER_TOKEN))} tokens. Upper-bound estimate.`
28246
28602
  )
28247
28603
  );
@@ -28287,7 +28643,7 @@ async function explainCustomizationsMode(format = "human") {
28287
28643
  if (summary.entries.length === 0) {
28288
28644
  printBox(
28289
28645
  "Customizations",
28290
- [chalk16.dim("No .customize.yaml or .customize.md files found under .hatch3r/{agents,skills,commands,rules}/.")],
28646
+ [chalk17.dim("No .customize.yaml or .customize.md files found under .hatch3r/{agents,skills,commands,rules}/.")],
28291
28647
  "info"
28292
28648
  );
28293
28649
  return;
@@ -28319,7 +28675,7 @@ async function explainCustomizationsMode(format = "human") {
28319
28675
  tableLines.push(
28320
28676
  `${"Type".padEnd(COL_TYPE)}${"Id".padEnd(COL_ID)}${"Outcome".padEnd(COL_OUTCOME)}${"Overrides".padEnd(COL_OVERRIDES)}${"Reason".padEnd(COL_REASON)}`
28321
28677
  );
28322
- tableLines.push(chalk16.dim("\u2500".repeat(COL_TYPE + COL_ID + COL_OUTCOME + COL_OVERRIDES + COL_REASON)));
28678
+ tableLines.push(chalk17.dim("\u2500".repeat(COL_TYPE + COL_ID + COL_OUTCOME + COL_OVERRIDES + COL_REASON)));
28323
28679
  for (const entry of sorted) {
28324
28680
  const overridesParts = [];
28325
28681
  if (entry.appliedOverrides.description !== void 0) overridesParts.push("desc");
@@ -28327,19 +28683,19 @@ async function explainCustomizationsMode(format = "human") {
28327
28683
  if (entry.appliedOverrides.scope !== void 0) overridesParts.push("scope");
28328
28684
  if (entry.appliedOverrides.enabled !== void 0) overridesParts.push(`enabled=${entry.appliedOverrides.enabled}`);
28329
28685
  if (entry.hasMd && !overridesParts.includes("md")) overridesParts.push("md");
28330
- const overridesCell = overridesParts.length > 0 ? overridesParts.join(",") : chalk16.dim("\u2014");
28686
+ const overridesCell = overridesParts.length > 0 ? overridesParts.join(",") : chalk17.dim("\u2014");
28331
28687
  const outcomeCell = (() => {
28332
28688
  switch (entry.outcome) {
28333
28689
  case "failed":
28334
- return chalk16.red("failed");
28690
+ return chalk17.red("failed");
28335
28691
  case "skipped":
28336
- return chalk16.yellow("skipped");
28692
+ return chalk17.yellow("skipped");
28337
28693
  case "inert":
28338
- return chalk16.yellow("inert");
28694
+ return chalk17.yellow("inert");
28339
28695
  case "active":
28340
- return chalk16.green("active");
28696
+ return chalk17.green("active");
28341
28697
  default:
28342
- return chalk16.dim("none");
28698
+ return chalk17.dim("none");
28343
28699
  }
28344
28700
  })();
28345
28701
  const idCell = entry.id.length > COL_ID - 1 ? `${entry.id.slice(0, COL_ID - 2)}\u2026` : entry.id;
@@ -28357,13 +28713,13 @@ async function explainCustomizationsMode(format = "human") {
28357
28713
  // string stays byte-identical for repos with no deselected overrides.
28358
28714
  (summary.counts.inert > 0 ? `, ${summary.counts.inert} inert` : "");
28359
28715
  info(
28360
- `${chalk16.bold("Customizations:")} ${summaryLine}. ` + chalk16.dim(`Run \`hatch3r status\` for a one-line summary.`)
28716
+ `${chalk17.bold("Customizations:")} ${summaryLine}. ` + chalk17.dim(`Run \`hatch3r status\` for a one-line summary.`)
28361
28717
  );
28362
28718
  console.log();
28363
28719
  }
28364
28720
  async function explainEfficiencyMode(format = "human") {
28365
28721
  const rootDir = process.cwd();
28366
- const logPath = join56(rootDir, HATCH3R_DIR, "efficiency-events.jsonl");
28722
+ const logPath = join57(rootDir, HATCH3R_DIR, "efficiency-events.jsonl");
28367
28723
  let raw;
28368
28724
  try {
28369
28725
  raw = await readFile41(logPath, "utf-8");
@@ -28373,7 +28729,7 @@ async function explainEfficiencyMode(format = "human") {
28373
28729
  error(`No efficiency telemetry found at ${HATCH3R_DIR}/efficiency-events.jsonl.`);
28374
28730
  if (!isQuiet()) {
28375
28731
  console.log(
28376
- chalk16.dim(
28732
+ chalk17.dim(
28377
28733
  " Telemetry is gated by HATCH3R_EFFICIENCY_TELEMETRY=1. Set the env var and re-run the orchestrator, then re-invoke `hatch3r explain --efficiency`."
28378
28734
  )
28379
28735
  );
@@ -28479,10 +28835,10 @@ async function explainEfficiencyMode(format = "human") {
28479
28835
  tableLines.push(
28480
28836
  `${"Artifact".padEnd(COL_ART)}${"Phase".padEnd(COL_PHASE)}${"Inv".padEnd(COL_INVOK)}${"In".padEnd(COL_TOK)}${"Out".padEnd(COL_TOK)}${"Latency(ms)".padEnd(COL_LAT)}`
28481
28837
  );
28482
- tableLines.push(chalk16.dim("\u2500".repeat(COL_ART + COL_PHASE + COL_INVOK + COL_TOK * 2 + COL_LAT)));
28838
+ tableLines.push(chalk17.dim("\u2500".repeat(COL_ART + COL_PHASE + COL_INVOK + COL_TOK * 2 + COL_LAT)));
28483
28839
  if (agg.spawnInvocations > 0) {
28484
28840
  tableLines.push(
28485
- `${artId.padEnd(COL_ART)}${"(spawns)".padEnd(COL_PHASE)}${String(agg.spawnInvocations).padEnd(COL_INVOK)}${"\u2014".padEnd(COL_TOK)}${"\u2014".padEnd(COL_TOK)}` + chalk16.dim(`${agg.spawnSubAgents} sub-agent(s) total`)
28841
+ `${artId.padEnd(COL_ART)}${"(spawns)".padEnd(COL_PHASE)}${String(agg.spawnInvocations).padEnd(COL_INVOK)}${"\u2014".padEnd(COL_TOK)}${"\u2014".padEnd(COL_TOK)}` + chalk17.dim(`${agg.spawnSubAgents} sub-agent(s) total`)
28486
28842
  );
28487
28843
  }
28488
28844
  const sortedPhases = [...agg.phases.keys()].sort((a, b) => a.localeCompare(b));
@@ -28495,7 +28851,7 @@ async function explainEfficiencyMode(format = "human") {
28495
28851
  printBox(artId, tableLines, "info");
28496
28852
  }
28497
28853
  info(
28498
- chalk16.dim(
28854
+ chalk17.dim(
28499
28855
  "Aggregates are sums across all sessions in the log. Truncate `.hatch3r/efficiency-events.jsonl` to reset."
28500
28856
  )
28501
28857
  );
@@ -28504,7 +28860,7 @@ async function explainEfficiencyMode(format = "human") {
28504
28860
  async function explainSourceMode(subject, detail = false, format = "human") {
28505
28861
  const jsonMode = format === "json";
28506
28862
  const rootDir = process.cwd();
28507
- const provenancePath = join56(rootDir, HATCH3R_DIR, "provenance.json");
28863
+ const provenancePath = join57(rootDir, HATCH3R_DIR, "provenance.json");
28508
28864
  let manifest;
28509
28865
  try {
28510
28866
  const raw = await readFile41(provenancePath, "utf-8");
@@ -28522,7 +28878,7 @@ async function explainSourceMode(subject, detail = false, format = "human") {
28522
28878
  });
28523
28879
  } else {
28524
28880
  error(`No provenance manifest found at ${HATCH3R_DIR}/provenance.json.`);
28525
- console.log(chalk16.dim(" Run `hatch3r sync` to generate adapter outputs and record their canonical sources."));
28881
+ console.log(chalk17.dim(" Run `hatch3r sync` to generate adapter outputs and record their canonical sources."));
28526
28882
  console.log();
28527
28883
  }
28528
28884
  throw new HatchError(
@@ -28554,7 +28910,7 @@ async function explainSourceMode(subject, detail = false, format = "human") {
28554
28910
  }
28555
28911
  printBox(
28556
28912
  "Source provenance",
28557
- [chalk16.dim(`No outputs recorded in ${HATCH3R_DIR}/provenance.json. Run \`hatch3r sync\` first.`)],
28913
+ [chalk17.dim(`No outputs recorded in ${HATCH3R_DIR}/provenance.json. Run \`hatch3r sync\` first.`)],
28558
28914
  "info"
28559
28915
  );
28560
28916
  return;
@@ -28629,18 +28985,18 @@ async function explainSourceMode(subject, detail = false, format = "human") {
28629
28985
  tableLines.push(
28630
28986
  `${"Output".padEnd(COL_OUTPUT)}${"Adapter".padEnd(COL_ADAPTER)}Sources`
28631
28987
  );
28632
- tableLines.push(chalk16.dim("\u2500".repeat(COL_OUTPUT + COL_ADAPTER + 8)));
28988
+ tableLines.push(chalk17.dim("\u2500".repeat(COL_OUTPUT + COL_ADAPTER + 8)));
28633
28989
  for (const entry of sorted) {
28634
28990
  const pathCell = entry.path.length > COL_OUTPUT - 1 ? `${entry.path.slice(0, COL_OUTPUT - 2)}\u2026` : entry.path;
28635
28991
  const count = entry.sourceFiles.length;
28636
- const countCell = count === 0 ? chalk16.dim("0") : String(count);
28992
+ const countCell = count === 0 ? chalk17.dim("0") : String(count);
28637
28993
  tableLines.push(
28638
28994
  `${pathCell.padEnd(COL_OUTPUT)}${entry.adapter.padEnd(COL_ADAPTER)}${countCell}`
28639
28995
  );
28640
28996
  }
28641
28997
  printBox("Per-output source counts", tableLines, "info");
28642
28998
  info(
28643
- chalk16.dim(
28999
+ chalk17.dim(
28644
29000
  "Showing source counts. Run `hatch3r explain --source <output-path>` for one output's full list, or `hatch3r explain --source all --verbose` for every list."
28645
29001
  )
28646
29002
  );
@@ -28657,8 +29013,8 @@ async function explainSourceMode(subject, detail = false, format = "human") {
28657
29013
  error(`Output path not recorded in provenance: ${normalized}`);
28658
29014
  const known = outputs.map((o) => o.path).sort();
28659
29015
  const preview = known.slice(0, 8).join(", ") + (known.length > 8 ? `, \u2026 (${known.length} total)` : "");
28660
- console.log(chalk16.dim(` Recorded outputs: ${preview}`));
28661
- console.log(chalk16.dim(" Tip: run `hatch3r explain --source all` to list every recorded output."));
29016
+ console.log(chalk17.dim(` Recorded outputs: ${preview}`));
29017
+ console.log(chalk17.dim(" Tip: run `hatch3r explain --source all` to list every recorded output."));
28662
29018
  console.log();
28663
29019
  throw new HatchError(
28664
29020
  `Output path not recorded: ${normalized}`,
@@ -28681,33 +29037,33 @@ function printSourceBlock(entry, manifest) {
28681
29037
  if (manifest.lastRunId) lines.push(label("Run id", manifest.lastRunId));
28682
29038
  }
28683
29039
  if (entry.sourceFiles.length === 0) {
28684
- lines.push(label("Sources", chalk16.dim("(none recorded)")));
29040
+ lines.push(label("Sources", chalk17.dim("(none recorded)")));
28685
29041
  } else {
28686
29042
  lines.push(label("Sources", `${entry.sourceFiles.length} canonical file(s):`));
28687
29043
  for (const src of entry.sourceFiles) {
28688
- lines.push(` ${chalk16.cyan("\u2022")} ${src}`);
29044
+ lines.push(` ${chalk17.cyan("\u2022")} ${src}`);
28689
29045
  }
28690
29046
  }
28691
29047
  printBox(`Source: ${entry.path}`, lines, "info");
28692
29048
  }
28693
29049
 
28694
29050
  // src/cli/commands/rollback.ts
28695
- import chalk17 from "chalk";
28696
- import inquirer12 from "inquirer";
29051
+ import chalk18 from "chalk";
29052
+ import inquirer13 from "inquirer";
28697
29053
  init_types();
28698
29054
  init_safeWrite();
28699
29055
  init_ui();
28700
29056
  function formatSnapshotRow(meta) {
28701
29057
  const ts = meta.timestamp.replace("T", " ").replace(/\.[0-9]+Z$/, "Z");
28702
29058
  const count = `${meta.paths.length} file${meta.paths.length === 1 ? "" : "s"}`;
28703
- return ` ${chalk17.cyan("\u2022")} ${chalk17.bold(meta.sessionId)} ${chalk17.dim(ts)} ${chalk17.dim(count)}`;
29059
+ return ` ${chalk18.cyan("\u2022")} ${chalk18.bold(meta.sessionId)} ${chalk18.dim(ts)} ${chalk18.dim(count)}`;
28704
29060
  }
28705
29061
  async function confirmRollback(meta) {
28706
- const { confirmed } = await inquirer12.prompt([
29062
+ const { confirmed } = await inquirer13.prompt([
28707
29063
  {
28708
29064
  type: "confirm",
28709
29065
  name: "confirmed",
28710
- message: `Restore ${meta.paths.length} file(s) from session ${chalk17.bold(meta.sessionId)} (captured ${meta.timestamp})? This overwrites the current on-disk state.`,
29066
+ message: `Restore ${meta.paths.length} file(s) from session ${chalk18.bold(meta.sessionId)} (captured ${meta.timestamp})? This overwrites the current on-disk state.`,
28711
29067
  default: false
28712
29068
  }
28713
29069
  ]);
@@ -28731,7 +29087,7 @@ async function rollbackCommand(opts = {}) {
28731
29087
  const snapshots = await listSnapshots();
28732
29088
  const meta = snapshots.find((s) => s.sessionId === opts.session);
28733
29089
  if (!meta) {
28734
- error(`Session ${chalk17.bold(opts.session)} not found.`);
29090
+ error(`Session ${chalk18.bold(opts.session)} not found.`);
28735
29091
  info("Run `hatch3r rollback list` to see available sessions.");
28736
29092
  throw new HatchError(
28737
29093
  `Session ${opts.session} not found.`,
@@ -28763,10 +29119,10 @@ async function rollbackCommand(opts = {}) {
28763
29119
  const result = await applyRollback(meta.sessionId, { dryRun: opts.dryRun });
28764
29120
  spinner.stop();
28765
29121
  const summaryLines = [
28766
- `${chalk17.green("\u2714")} Files restored: ${result.filesRestored} / ${meta.paths.length}`
29122
+ `${chalk18.green("\u2714")} Files restored: ${result.filesRestored} / ${meta.paths.length}`
28767
29123
  ];
28768
29124
  if (result.errors.length > 0) {
28769
- summaryLines.push(`${chalk17.red("\u2718")} Errors: ${result.errors.length}`);
29125
+ summaryLines.push(`${chalk18.red("\u2718")} Errors: ${result.errors.length}`);
28770
29126
  }
28771
29127
  const tag = opts.dryRun ? "rollback (dry-run)" : "rollback";
28772
29128
  const jsonPayload = {
@@ -28830,19 +29186,19 @@ async function rollbackListCommand(opts = {}) {
28830
29186
  info("Snapshots are captured by long-running orchestrators (init, sync, audit).");
28831
29187
  return;
28832
29188
  }
28833
- console.log(chalk17.bold(`Snapshot sessions (${snapshots.length}):`));
29189
+ console.log(chalk18.bold(`Snapshot sessions (${snapshots.length}):`));
28834
29190
  for (const meta of snapshots) {
28835
29191
  console.log(formatSnapshotRow(meta));
28836
29192
  }
28837
29193
  console.log();
28838
- info(`Run ${chalk17.cyan("hatch3r rollback --session=<id>")} to restore a session.`);
29194
+ info(`Run ${chalk18.cyan("hatch3r rollback --session=<id>")} to restore a session.`);
28839
29195
  }
28840
29196
 
28841
29197
  // src/cli/commands/show.ts
28842
29198
  import { readFile as readFile42 } from "fs/promises";
28843
- import { join as join57 } from "path";
28844
- import chalk18 from "chalk";
28845
- import { parse as parseYaml14 } from "yaml";
29199
+ import { join as join58 } from "path";
29200
+ import chalk19 from "chalk";
29201
+ import { parse as parseYaml15 } from "yaml";
28846
29202
  init_types();
28847
29203
  init_ui();
28848
29204
  var FRONTMATTER_REGEX5 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -28851,11 +29207,11 @@ function parseFrontmatter2(raw) {
28851
29207
  const match = raw.match(FRONTMATTER_REGEX5);
28852
29208
  if (!match) return { frontmatter: {}, body: raw };
28853
29209
  try {
28854
- const parsed = parseYaml14(match[1]);
29210
+ const parsed = parseYaml15(match[1]);
28855
29211
  return { frontmatter: parsed ?? {}, body: match[2] };
28856
29212
  } catch (err) {
28857
29213
  console.error(
28858
- ` ${chalk18.yellow("\u26A0")} show: YAML frontmatter parse failed \u2014 ${err instanceof Error ? err.message : String(err)}. Treating as frontmatter-less.`
29214
+ ` ${chalk19.yellow("\u26A0")} show: YAML frontmatter parse failed \u2014 ${err instanceof Error ? err.message : String(err)}. Treating as frontmatter-less.`
28859
29215
  );
28860
29216
  return { frontmatter: {}, body: match[2] };
28861
29217
  }
@@ -28896,7 +29252,7 @@ async function showCommand(idArg, opts = {}) {
28896
29252
  );
28897
29253
  }
28898
29254
  const fileRoot = item.source === "user" && userRoot ? userRoot : canonicalRoot;
28899
- const filePath = join57(fileRoot, item.relativePath);
29255
+ const filePath = join58(fileRoot, item.relativePath);
28900
29256
  const raw = await readFile42(filePath, "utf-8");
28901
29257
  const { frontmatter, body } = parseFrontmatter2(raw);
28902
29258
  const headerLines = [
@@ -28949,7 +29305,7 @@ async function showCommand(idArg, opts = {}) {
28949
29305
  if (truncated) {
28950
29306
  console.log();
28951
29307
  console.log(
28952
- chalk18.dim(` \u2026 ${bodyLines.length - BODY_PREVIEW_LINES} more line(s) \u2014 open ${filePath} for the full body.`)
29308
+ chalk19.dim(` \u2026 ${bodyLines.length - BODY_PREVIEW_LINES} more line(s) \u2014 open ${filePath} for the full body.`)
28953
29309
  );
28954
29310
  }
28955
29311
  console.log();
@@ -29015,12 +29371,12 @@ async function listCommand(typeArg, opts = {}) {
29015
29371
  info(`No artifacts of type "${type}" found.`);
29016
29372
  return;
29017
29373
  }
29018
- console.log(chalk18.bold(`${type} (${items.length}):`));
29374
+ console.log(chalk19.bold(`${type} (${items.length}):`));
29019
29375
  for (const item of items) {
29020
29376
  const tagPreview = item.tags.slice(0, 3).join(", ") + (item.tags.length > 3 ? ` \u2026 (+${item.tags.length - 3})` : "");
29021
- const protectedTag = item.protected ? chalk18.red(" [protected]") : "";
29022
- const sourceTag = item.source === "user" ? chalk18.yellow(" [user]") : "";
29023
- console.log(` ${chalk18.cyan("\u2022")} ${chalk18.bold(item.id)}${protectedTag}${sourceTag} ${chalk18.dim(tagPreview)}`);
29377
+ const protectedTag = item.protected ? chalk19.red(" [protected]") : "";
29378
+ const sourceTag = item.source === "user" ? chalk19.yellow(" [user]") : "";
29379
+ console.log(` ${chalk19.cyan("\u2022")} ${chalk19.bold(item.id)}${protectedTag}${sourceTag} ${chalk19.dim(tagPreview)}`);
29024
29380
  }
29025
29381
  console.log();
29026
29382
  info(`Run \`hatch3r show <id>\` to inspect frontmatter + body preview.`);
@@ -29031,13 +29387,13 @@ init_types();
29031
29387
  init_version();
29032
29388
  init_ui();
29033
29389
  import { readFile as readFile43 } from "fs/promises";
29034
- import { join as join58 } from "path";
29035
- import chalk19 from "chalk";
29390
+ import { join as join59 } from "path";
29391
+ import chalk20 from "chalk";
29036
29392
  async function provenanceCommand(opts) {
29037
29393
  const format = beginCommand(opts ?? {}, { banner: "compact" });
29038
29394
  const jsonMode = format === "json";
29039
29395
  const rootDir = process.cwd();
29040
- const provenancePath = join58(rootDir, HATCH3R_DIR, "provenance.json");
29396
+ const provenancePath = join59(rootDir, HATCH3R_DIR, "provenance.json");
29041
29397
  let manifest;
29042
29398
  try {
29043
29399
  const raw = await readFile43(provenancePath, "utf-8");
@@ -29056,7 +29412,7 @@ async function provenanceCommand(opts) {
29056
29412
  } else {
29057
29413
  error(`No provenance manifest found at ${HATCH3R_DIR}/provenance.json.`);
29058
29414
  console.log(
29059
- chalk19.dim(" Run `hatch3r sync` to generate adapter outputs and record their canonical sources.")
29415
+ chalk20.dim(" Run `hatch3r sync` to generate adapter outputs and record their canonical sources.")
29060
29416
  );
29061
29417
  console.log();
29062
29418
  }
@@ -29108,7 +29464,7 @@ async function provenanceCommand(opts) {
29108
29464
  }
29109
29465
  const adapterLines = [];
29110
29466
  for (const [adapter, count] of [...perAdapter.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
29111
- adapterLines.push(`${chalk19.cyan("\u2022")} ${adapter.padEnd(12)} ${count} output(s)`);
29467
+ adapterLines.push(`${chalk20.cyan("\u2022")} ${adapter.padEnd(12)} ${count} output(s)`);
29112
29468
  }
29113
29469
  finishCommand(format, {
29114
29470
  command: "provenance",
@@ -29123,9 +29479,9 @@ async function provenanceCommand(opts) {
29123
29479
 
29124
29480
  // src/cli/commands/deps.ts
29125
29481
  import { readFile as readFile44 } from "fs/promises";
29126
- import { join as join59 } from "path";
29127
- import chalk20 from "chalk";
29128
- import { parse as parseYaml15 } from "yaml";
29482
+ import { join as join60 } from "path";
29483
+ import chalk21 from "chalk";
29484
+ import { parse as parseYaml16 } from "yaml";
29129
29485
  init_types();
29130
29486
  init_ui();
29131
29487
  var FRONTMATTER_REGEX6 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -29133,10 +29489,10 @@ function parseFrontmatter3(raw, sourcePath) {
29133
29489
  const match = raw.match(FRONTMATTER_REGEX6);
29134
29490
  if (!match) return {};
29135
29491
  try {
29136
- return parseYaml15(match[1]) ?? {};
29492
+ return parseYaml16(match[1]) ?? {};
29137
29493
  } catch (err) {
29138
29494
  console.error(
29139
- ` ${chalk20.yellow("\u26A0")} deps: YAML frontmatter parse failed${sourcePath ? ` in ${sourcePath}` : ""} \u2014 ${err instanceof Error ? err.message : String(err)}`
29495
+ ` ${chalk21.yellow("\u26A0")} deps: YAML frontmatter parse failed${sourcePath ? ` in ${sourcePath}` : ""} \u2014 ${err instanceof Error ? err.message : String(err)}`
29140
29496
  );
29141
29497
  return {};
29142
29498
  }
@@ -29205,7 +29561,7 @@ async function depsCommand(idArg, opts = {}) {
29205
29561
  );
29206
29562
  }
29207
29563
  const fileRoot = item.source === "user" && userRoot ? userRoot : canonicalRoot;
29208
- const filePath = item.type === "skill" ? join59(fileRoot, item.relativePath, "SKILL.md") : join59(fileRoot, item.relativePath);
29564
+ const filePath = item.type === "skill" ? join60(fileRoot, item.relativePath, "SKILL.md") : join60(fileRoot, item.relativePath);
29209
29565
  let frontmatter = {};
29210
29566
  let body = "";
29211
29567
  try {
@@ -29235,7 +29591,7 @@ async function depsCommand(idArg, opts = {}) {
29235
29591
  for (const candidate of index.items) {
29236
29592
  if (candidate.id === item.id) continue;
29237
29593
  const candFileRoot = candidate.source === "user" && userRoot ? userRoot : canonicalRoot;
29238
- const candPath = candidate.type === "skill" ? join59(candFileRoot, candidate.relativePath, "SKILL.md") : join59(candFileRoot, candidate.relativePath);
29594
+ const candPath = candidate.type === "skill" ? join60(candFileRoot, candidate.relativePath, "SKILL.md") : join60(candFileRoot, candidate.relativePath);
29239
29595
  try {
29240
29596
  const candRaw = await readFile44(candPath, "utf-8");
29241
29597
  const candFm = parseFrontmatter3(candRaw, candidate.relativePath);
@@ -29248,7 +29604,7 @@ async function depsCommand(idArg, opts = {}) {
29248
29604
  } catch (err) {
29249
29605
  const code = err.code ?? "UNKNOWN";
29250
29606
  console.error(
29251
- ` ${chalk20.dim("\u2139")} deps: skipping ${candidate.relativePath} (${code}) \u2014 cannot scan upstream references`
29607
+ ` ${chalk21.dim("\u2139")} deps: skipping ${candidate.relativePath} (${code}) \u2014 cannot scan upstream references`
29252
29608
  );
29253
29609
  }
29254
29610
  }
@@ -29289,50 +29645,50 @@ async function depsCommand(idArg, opts = {}) {
29289
29645
  headerLines.push(label("Orchestrator", String(frontmatter.orchestrator)));
29290
29646
  }
29291
29647
  printBox(`Dependencies: ${item.id}`, headerLines, "info");
29292
- console.log(chalk20.bold("Downstream (this artifact delegates to, from frontmatter):"));
29648
+ console.log(chalk21.bold("Downstream (this artifact delegates to, from frontmatter):"));
29293
29649
  if (downstream.length === 0) {
29294
- console.log(chalk20.dim(" (no agentPipeline / delegates entries in frontmatter)"));
29650
+ console.log(chalk21.dim(" (no agentPipeline / delegates entries in frontmatter)"));
29295
29651
  } else {
29296
29652
  for (let i = 0; i < downstream.length; i++) {
29297
29653
  const { id, resolved } = downstream[i];
29298
- const status = resolved ? chalk20.green("\u2713") : chalk20.red("\u2717");
29299
- const tag = resolved ? "" : chalk20.red(" [not in content index]");
29300
- console.log(` ${status} ${id}${tag} ${chalk20.dim(`(${sources[i]})`)}`);
29654
+ const status = resolved ? chalk21.green("\u2713") : chalk21.red("\u2717");
29655
+ const tag = resolved ? "" : chalk21.red(" [not in content index]");
29656
+ console.log(` ${status} ${id}${tag} ${chalk21.dim(`(${sources[i]})`)}`);
29301
29657
  }
29302
29658
  }
29303
29659
  console.log();
29304
29660
  const refCount = refs.skills.length + refs.rules.length + refs.mcpServers.length;
29305
29661
  console.log(
29306
- chalk20.bold("References (best-effort, prose-derived \u2014 not authoritative):")
29662
+ chalk21.bold("References (best-effort, prose-derived \u2014 not authoritative):")
29307
29663
  );
29308
29664
  if (refCount === 0) {
29309
- console.log(chalk20.dim(" (no skill / rule / MCP references found in body prose)"));
29665
+ console.log(chalk21.dim(" (no skill / rule / MCP references found in body prose)"));
29310
29666
  } else {
29311
29667
  for (const id of refs.skills) {
29312
- console.log(` ${chalk20.cyan("\u2022")} ${id} ${chalk20.dim("(skill)")}`);
29668
+ console.log(` ${chalk21.cyan("\u2022")} ${id} ${chalk21.dim("(skill)")}`);
29313
29669
  }
29314
29670
  for (const id of refs.rules) {
29315
- console.log(` ${chalk20.cyan("\u2022")} ${id} ${chalk20.dim("(rule)")}`);
29671
+ console.log(` ${chalk21.cyan("\u2022")} ${id} ${chalk21.dim("(rule)")}`);
29316
29672
  }
29317
29673
  for (const name of refs.mcpServers) {
29318
- console.log(` ${chalk20.cyan("\u2022")} ${name} ${chalk20.dim("(MCP server, name-matched)")}`);
29674
+ console.log(` ${chalk21.cyan("\u2022")} ${name} ${chalk21.dim("(MCP server, name-matched)")}`);
29319
29675
  }
29320
29676
  }
29321
29677
  console.log();
29322
- console.log(chalk20.bold("Upstream (these artifacts delegate to this one):"));
29678
+ console.log(chalk21.bold("Upstream (these artifacts delegate to this one):"));
29323
29679
  if (upstream.length === 0) {
29324
- console.log(chalk20.dim(" (no artifacts reference this one in their agentPipeline / delegates)"));
29680
+ console.log(chalk21.dim(" (no artifacts reference this one in their agentPipeline / delegates)"));
29325
29681
  } else {
29326
29682
  for (const u of upstream) {
29327
- console.log(` ${chalk20.cyan("\u2022")} ${u.id} ${chalk20.dim(`(${u.type}, via ${u.via})`)}`);
29683
+ console.log(` ${chalk21.cyan("\u2022")} ${u.id} ${chalk21.dim(`(${u.type}, via ${u.via})`)}`);
29328
29684
  }
29329
29685
  }
29330
29686
  console.log();
29331
29687
  }
29332
29688
 
29333
29689
  // src/cli/commands/learn.ts
29334
- import { mkdir as mkdir19, readFile as readFile45 } from "fs/promises";
29335
- import { basename as basename4, isAbsolute as isAbsolute5, join as join60, resolve as resolve8 } from "path";
29690
+ import { mkdir as mkdir20, readFile as readFile45 } from "fs/promises";
29691
+ import { basename as basename5, isAbsolute as isAbsolute5, join as join61, resolve as resolve9 } from "path";
29336
29692
  init_types();
29337
29693
  init_ui();
29338
29694
  var FRONTMATTER_REGEX7 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
@@ -29361,7 +29717,7 @@ async function learnCaptureCommand(options) {
29361
29717
  );
29362
29718
  }
29363
29719
  const rootDir = process.cwd();
29364
- const sourcePath = isAbsolute5(options.file) ? options.file : resolve8(rootDir, options.file);
29720
+ const sourcePath = isAbsolute5(options.file) ? options.file : resolve9(rootDir, options.file);
29365
29721
  let raw;
29366
29722
  try {
29367
29723
  raw = await readFile45(sourcePath, "utf-8");
@@ -29375,7 +29731,7 @@ async function learnCaptureCommand(options) {
29375
29731
  "Confirm the path your /learn flow wrote to exists and is readable, then re-run `hatch3r learn capture --file <path>`."
29376
29732
  );
29377
29733
  }
29378
- const destName = typeof options.as === "string" && options.as.trim().length > 0 ? options.as.trim() : basename4(sourcePath);
29734
+ const destName = typeof options.as === "string" && options.as.trim().length > 0 ? options.as.trim() : basename5(sourcePath);
29379
29735
  const nameErrors = validateLearningFileName(destName);
29380
29736
  if (nameErrors.length > 0) {
29381
29737
  for (const e of nameErrors) error(e);
@@ -29386,8 +29742,8 @@ async function learnCaptureCommand(options) {
29386
29742
  "Use a `.md` filename with only alphanumerics, hyphens, underscores, and dots (no path separators)."
29387
29743
  );
29388
29744
  }
29389
- const learningsDir = join60(rootDir, HATCH3R_DIR, "learnings");
29390
- const targetPath = join60(learningsDir, destName);
29745
+ const learningsDir = join61(rootDir, HATCH3R_DIR, "learnings");
29746
+ const targetPath = join61(learningsDir, destName);
29391
29747
  const { body, declaredIntegrity } = extractBodyAndIntegrity(raw);
29392
29748
  if (declaredIntegrity) {
29393
29749
  const recomputed = computeLearningIntegrity(body);
@@ -29410,7 +29766,7 @@ async function learnCaptureCommand(options) {
29410
29766
  );
29411
29767
  }
29412
29768
  const dryRun = options.dryRun === true;
29413
- if (!dryRun) await mkdir19(learningsDir, { recursive: true });
29769
+ if (!dryRun) await mkdir20(learningsDir, { recursive: true });
29414
29770
  const result = await persistLearning(targetPath, raw, {
29415
29771
  source: "learn-command",
29416
29772
  dryRun
@@ -29488,13 +29844,13 @@ async function learnCaptureCommand(options) {
29488
29844
  // src/cli/commands/mcp.ts
29489
29845
  import { existsSync as existsSync6 } from "fs";
29490
29846
  import { readFile as readFile46 } from "fs/promises";
29491
- import { join as join61 } from "path";
29492
- import chalk21 from "chalk";
29847
+ import { join as join62 } from "path";
29848
+ import chalk22 from "chalk";
29493
29849
  init_safeWrite();
29494
29850
  init_types();
29495
29851
  init_ui();
29496
29852
  function wslThemeOrUndefined() {
29497
- return isWSL() ? { icon: { checked: chalk21.green("[x]"), unchecked: "[ ]", cursor: ">" } } : void 0;
29853
+ return isWSL() ? { icon: { checked: chalk22.green("[x]"), unchecked: "[ ]", cursor: ">" } } : void 0;
29498
29854
  }
29499
29855
  async function sweepOrphanTmpAtEntry(rootDir) {
29500
29856
  try {
@@ -29545,8 +29901,8 @@ async function mcpSetupCommand(opts = {}) {
29545
29901
  await ensureGitignoreEntry(rootDir);
29546
29902
  if (envResult.newVars.length > 0) {
29547
29903
  envAdvisoryLines.push("");
29548
- envAdvisoryLines.push(`${chalk21.yellow("!")} Add new secrets to ${chalk21.bold(".env.mcp")}: ${envResult.newVars.join(", ")}`);
29549
- envAdvisoryLines.push(` Then run: ${chalk21.dim(getSourceEnvMcpCommand())}`);
29904
+ envAdvisoryLines.push(`${chalk22.yellow("!")} Add new secrets to ${chalk22.bold(".env.mcp")}: ${envResult.newVars.join(", ")}`);
29905
+ envAdvisoryLines.push(` Then run: ${chalk22.dim(getSourceEnvMcpCommand())}`);
29550
29906
  }
29551
29907
  }
29552
29908
  finishCommand(format, {
@@ -29569,7 +29925,7 @@ async function mcpListCommand(opts = {}) {
29569
29925
  const manifest = await readManifest(rootDir);
29570
29926
  assertManifest(manifest);
29571
29927
  const servers = manifest.mcp.servers;
29572
- const envPath = join61(rootDir, ".env.mcp");
29928
+ const envPath = join62(rootDir, ".env.mcp");
29573
29929
  const hasEnvFile = existsSync6(envPath);
29574
29930
  const envExisting = hasEnvFile ? parseEnvFile(await readFile46(envPath, "utf-8")) : {};
29575
29931
  const requiredVars = collectRequiredEnvVars(servers);
@@ -29583,16 +29939,16 @@ async function mcpListCommand(opts = {}) {
29583
29939
  for (const id of servers) {
29584
29940
  const meta = AVAILABLE_MCP_SERVERS[id];
29585
29941
  const desc = meta?.description ?? "(unknown server)";
29586
- lines.push(` ${chalk21.cyan(id)} \u2014 ${desc}`);
29942
+ lines.push(` ${chalk22.cyan(id)} \u2014 ${desc}`);
29587
29943
  }
29588
29944
  lines.push("");
29589
- lines.push(label(".env.mcp", hasEnvFile ? "present" : chalk21.yellow("missing")));
29945
+ lines.push(label(".env.mcp", hasEnvFile ? "present" : chalk22.yellow("missing")));
29590
29946
  if (requiredVars.length > 0) {
29591
29947
  lines.push(label("Required vars", requiredVars.map((v) => v.name).join(", ")));
29592
29948
  if (missingVars.length > 0) {
29593
- lines.push(label("Missing", chalk21.yellow(missingVars.map((v) => v.name).join(", "))));
29949
+ lines.push(label("Missing", chalk22.yellow(missingVars.map((v) => v.name).join(", "))));
29594
29950
  } else {
29595
- lines.push(label("Status", chalk21.green("all required vars set")));
29951
+ lines.push(label("Status", chalk22.green("all required vars set")));
29596
29952
  }
29597
29953
  }
29598
29954
  }
@@ -29619,7 +29975,7 @@ async function mcpRemoveCommand(id, opts = {}) {
29619
29975
  if (!before.includes(id)) {
29620
29976
  error(`MCP server "${id}" is not configured.`);
29621
29977
  if (!isQuiet()) {
29622
- console.log(chalk21.dim(` Current servers: ${before.length > 0 ? before.join(", ") : "(none)"}
29978
+ console.log(chalk22.dim(` Current servers: ${before.length > 0 ? before.join(", ") : "(none)"}
29623
29979
  `));
29624
29980
  }
29625
29981
  throw new HatchError(
@@ -29674,7 +30030,7 @@ async function mcpEnvCheckCommand(opts = {}) {
29674
30030
  const manifest = await readManifest(rootDir);
29675
30031
  assertManifest(manifest);
29676
30032
  const servers = manifest.mcp.servers;
29677
- const envPath = join61(rootDir, ".env.mcp");
30033
+ const envPath = join62(rootDir, ".env.mcp");
29678
30034
  const hasEnvFile = existsSync6(envPath);
29679
30035
  const envExisting = hasEnvFile ? parseEnvFile(await readFile46(envPath, "utf-8")) : {};
29680
30036
  const lines = [];
@@ -29695,21 +30051,21 @@ async function mcpEnvCheckCommand(opts = {}) {
29695
30051
  const meta = AVAILABLE_MCP_SERVERS[id];
29696
30052
  const required = meta?.requiresEnv ?? [];
29697
30053
  if (required.length === 0) {
29698
- lines.push(`${chalk21.green("\u2713")} ${id} \u2014 no env vars required`);
30054
+ lines.push(`${chalk22.green("\u2713")} ${id} \u2014 no env vars required`);
29699
30055
  serverReports.push({ id, required: [], missing: [] });
29700
30056
  continue;
29701
30057
  }
29702
30058
  const missing = required.filter((name) => !(name in envExisting) || envExisting[name] === "");
29703
30059
  serverReports.push({ id, required, missing });
29704
30060
  if (missing.length === 0) {
29705
- lines.push(`${chalk21.green("\u2713")} ${id} \u2014 ${required.join(", ")}`);
30061
+ lines.push(`${chalk22.green("\u2713")} ${id} \u2014 ${required.join(", ")}`);
29706
30062
  } else {
29707
- lines.push(`${chalk21.yellow("!")} ${id} \u2014 missing: ${missing.join(", ")}`);
30063
+ lines.push(`${chalk22.yellow("!")} ${id} \u2014 missing: ${missing.join(", ")}`);
29708
30064
  missingTotal += missing.length;
29709
30065
  }
29710
30066
  }
29711
30067
  lines.push("");
29712
- lines.push(label(".env.mcp", hasEnvFile ? "present" : chalk21.yellow("missing")));
30068
+ lines.push(label(".env.mcp", hasEnvFile ? "present" : chalk22.yellow("missing")));
29713
30069
  if (missingTotal > 0) {
29714
30070
  lines.push(label("Action", `Fill ${missingTotal} env var(s) in .env.mcp, then \`${getSourceEnvMcpCommand()}\``));
29715
30071
  }
@@ -29723,11 +30079,11 @@ async function mcpEnvCheckCommand(opts = {}) {
29723
30079
  }
29724
30080
 
29725
30081
  // src/cli/commands/cliTools.ts
29726
- import chalk22 from "chalk";
30082
+ import chalk23 from "chalk";
29727
30083
  init_safeWrite();
29728
30084
  init_ui();
29729
30085
  function wslThemeOrUndefined2() {
29730
- return isWSL() ? { icon: { checked: chalk22.green("[x]"), unchecked: "[ ]", cursor: ">" } } : void 0;
30086
+ return isWSL() ? { icon: { checked: chalk23.green("[x]"), unchecked: "[ ]", cursor: ">" } } : void 0;
29731
30087
  }
29732
30088
  async function cliToolsCommand(opts = {}) {
29733
30089
  const format = beginCommand(opts, { banner: "compact", interactive: true });
@@ -29785,9 +30141,9 @@ async function cliToolsCommand(opts = {}) {
29785
30141
  }
29786
30142
  }
29787
30143
  if (secretNotes.length > 0) {
29788
- info(chalk22.dim("CLI tool environment variables required:"));
30144
+ info(chalk23.dim("CLI tool environment variables required:"));
29789
30145
  for (const note of secretNotes) {
29790
- info(chalk22.dim(` ${note}`));
30146
+ info(chalk23.dim(` ${note}`));
29791
30147
  }
29792
30148
  }
29793
30149
  }
@@ -29841,13 +30197,13 @@ async function cliToolsListCommand(opts = {}) {
29841
30197
  const tierLabel2 = meta ? `tier ${meta.tier}` : "(unknown)";
29842
30198
  let status;
29843
30199
  if (r.installed) {
29844
- status = `${chalk22.green("\u2713")} ${r.path}`;
30200
+ status = `${chalk23.green("\u2713")} ${r.path}`;
29845
30201
  } else if (r.extensionMissing) {
29846
- status = `${chalk22.yellow("\u2717")} extension missing: ${r.extensionMissing}`;
30202
+ status = `${chalk23.yellow("\u2717")} extension missing: ${r.extensionMissing}`;
29847
30203
  } else {
29848
- status = `${chalk22.yellow("\u2717")} not on PATH`;
30204
+ status = `${chalk23.yellow("\u2717")} not on PATH`;
29849
30205
  }
29850
- lines.push(` ${chalk22.cyan(r.id)} (${tierLabel2}) \u2014 ${status}`);
30206
+ lines.push(` ${chalk23.cyan(r.id)} (${tierLabel2}) \u2014 ${status}`);
29851
30207
  }
29852
30208
  const installed = results.filter((r) => r.installed).length;
29853
30209
  lines.push("");
@@ -29919,19 +30275,19 @@ async function cliToolsDetectCommand(opts = {}) {
29919
30275
  let installed = 0;
29920
30276
  for (const r of results) {
29921
30277
  if (r.installed) {
29922
- lines.push(` ${chalk22.green("\u2713")} ${r.id} \u2014 ${r.path}`);
30278
+ lines.push(` ${chalk23.green("\u2713")} ${r.id} \u2014 ${r.path}`);
29923
30279
  installed++;
29924
30280
  } else if (r.extensionMissing) {
29925
- lines.push(` ${chalk22.yellow("\u2717")} ${r.id} \u2014 extension missing: ${r.extensionMissing}`);
30281
+ lines.push(` ${chalk23.yellow("\u2717")} ${r.id} \u2014 extension missing: ${r.extensionMissing}`);
29926
30282
  } else {
29927
- lines.push(` ${chalk22.yellow("\u2717")} ${r.id} \u2014 not on PATH`);
30283
+ lines.push(` ${chalk23.yellow("\u2717")} ${r.id} \u2014 not on PATH`);
29928
30284
  }
29929
30285
  }
29930
30286
  lines.push("");
29931
30287
  lines.push(label("Installed", `${installed}/${results.length}`));
29932
30288
  if (installed < results.length) {
29933
30289
  lines.push("");
29934
- lines.push(`Run ${chalk22.bold("npx hatch3r cli-tools install")} to see install commands.`);
30290
+ lines.push(`Run ${chalk23.bold("npx hatch3r cli-tools install")} to see install commands.`);
29935
30291
  }
29936
30292
  finishCommand(format, {
29937
30293
  command: "cli-tools detect",
@@ -30019,6 +30375,11 @@ function createProgram() {
30019
30375
  "Output format for CI consumers: human (default) or json",
30020
30376
  "human"
30021
30377
  ).option("--dry-run", "Preview what init would create or change without writing files").option("--verbose", "Show detailed per-step output").option("--no-banner", "Skip the ASCII banner at startup").option("--resume", "Resume from the last checkpoint in .init-workspace/checkpoint.json").option("--maturity <tier>", "Project maturity tier: solo, team, scaleup, enterprise (default: solo) \u2014 calibrates investment depth; does not change which content is installed").option("--role <role>", "Role bundle: reviewer, security-lead, senior-eng \u2014 filters content to items tagged for the named role").option("--facets <list>", "Comma-separated graduated-customization facets to add on top of the preset: a11y, performance, observability").option("--per-package", "On a monorepo, also copy adapter output under each package (default: root-only). Capped at 25 packages, batched, and .gitignore'd").action(initCommand);
30378
+ program2.command("setup [dir]").description("Scaffold a fresh project (mkdir + git init, optional GitHub remote) then chain into `hatch3r init`. Needs only Node + git; --remote is opt-in").option("--remote", "Create a private GitHub remote via `gh repo create` after git init (skipped with a warning when gh is missing or unauthenticated)").option("--tools <tools>", `Comma-separated tools (${TOOL_CHOICES})`).option("--preset <preset>", "Content preset forwarded to init (e.g. minimal, standard, full, web-app)").option("--maturity <tier>", "Project maturity tier forwarded to init: solo, team, scaleup, enterprise (default: solo)").option("--yes", "Skip interactive prompts (scaffold + init use smart defaults)").option(
30379
+ "--format <format>",
30380
+ "Output format for CI consumers: human (default) or json",
30381
+ "human"
30382
+ ).option("--quiet", "Suppress stdout chrome (banner, spinner, boxes); stderr diagnostics still emit").option("--dry-run", "Preview the scaffold plan (create dir, git init, remote, then run init) without writing anything").option("--verbose", "Show detailed per-step output").action(setupCommand);
30022
30383
  program2.command("sync").description("Re-generate tool outputs from bundled canonical content (run after updating hatch3r or editing .hatch3r/ overrides)").option("--repos [paths...]", "Sync workspace content to sub-repos (all opted-in if no paths given)").option("--dry-run", "Show what would change without modifying files").option("--diff", "Show a before/after diff summary for each generated file").option("--force", "Overwrite locally modified files in sub-repos").option("--minimal", "Generate stripped-down output (no comments, minimal formatting) to reduce token usage").option("--strict-budget", "Fail sync if any adapter's generated output exceeds its context budget (default: warn)").option("--clean-orphans", "Remove generated adapter output files that no longer match canonical-inventory naming (no hatch3r- prefix). Default is informational only.").option("--verbose", "Show detailed output for each file processed").option("--resume", "Resume from the last checkpoint in .sync-workspace/checkpoint.json").option(
30023
30384
  "--concurrency <n>",
30024
30385
  "Parallel workspace sub-repo sync limit (default: min(CPU count, 8); raise on SSD-bound runners)"
@@ -30249,7 +30610,7 @@ function classifyCliError(err, flags) {
30249
30610
 
30250
30611
  // src/cli/shared/errors.ts
30251
30612
  init_types();
30252
- import chalk23 from "chalk";
30613
+ import chalk24 from "chalk";
30253
30614
  import boxen2 from "boxen";
30254
30615
  var DEFAULT_RECOVERY_HINT = {
30255
30616
  VALIDATION_ERROR: "Re-run with --verbose to see the failing checks, then fix the listed errors and re-run.",
@@ -30272,11 +30633,11 @@ function formatActionableError(err, flags = {}) {
30272
30633
  const runId2 = getRunId();
30273
30634
  const hint = resolveRecoveryHint(err);
30274
30635
  if (hint) {
30275
- const title = chalk23.red.bold("hatch3r error");
30636
+ const title = chalk24.red.bold("hatch3r error");
30276
30637
  const body = `${err.message}
30277
30638
 
30278
- ${chalk23.dim("Try:")} ${hint}
30279
- ${chalk23.dim("Run id:")} ${runId2}`;
30639
+ ${chalk24.dim("Try:")} ${hint}
30640
+ ${chalk24.dim("Run id:")} ${runId2}`;
30280
30641
  const box = boxen2(body, {
30281
30642
  title,
30282
30643
  titleAlignment: "left",
@@ -30341,15 +30702,15 @@ function writeFormattedCliError(formatted) {
30341
30702
  // src/cli/shared/updateNotifier.ts
30342
30703
  init_version();
30343
30704
  import updateNotifier from "update-notifier";
30344
- import chalk24 from "chalk";
30705
+ import chalk25 from "chalk";
30345
30706
  var ONE_DAY_MS = 1e3 * 60 * 60 * 24;
30346
30707
  function buildMessage(update) {
30347
- const fromTo = `${chalk24.dim(update.current)} \u2192 ${chalk24.green(update.latest)}`;
30348
- const cyan = chalk24.hex("#06b6d4");
30708
+ const fromTo = `${chalk25.dim(update.current)} \u2192 ${chalk25.green(update.latest)}`;
30709
+ const cyan = chalk25.hex("#06b6d4");
30349
30710
  return [
30350
30711
  `Update available ${fromTo}`,
30351
- chalk24.dim("Run ") + cyan("npm i -g hatch3r@latest") + chalk24.dim(" to update the CLI"),
30352
- chalk24.dim("Then run ") + cyan("hatch3r update") + chalk24.dim(" to refresh project content")
30712
+ chalk25.dim("Run ") + cyan("npm i -g hatch3r@latest") + chalk25.dim(" to update the CLI"),
30713
+ chalk25.dim("Then run ") + cyan("hatch3r update") + chalk25.dim(" to refresh project content")
30353
30714
  ].join("\n");
30354
30715
  }
30355
30716
  function checkForUpdates() {
@@ -30375,7 +30736,7 @@ function checkForUpdates() {
30375
30736
  }
30376
30737
 
30377
30738
  // src/cli/shared/backablePrompts.ts
30378
- import inquirer13 from "inquirer";
30739
+ import inquirer14 from "inquirer";
30379
30740
  import {
30380
30741
  createPrompt,
30381
30742
  useState,
@@ -30873,10 +31234,10 @@ function registerBackablePrompts() {
30873
31234
  if (registered) return;
30874
31235
  if (!process.stdin.isTTY) return;
30875
31236
  registered = true;
30876
- inquirer13.registerPrompt("select", backableSelect);
30877
- inquirer13.registerPrompt("checkbox", backableCheckbox);
30878
- inquirer13.registerPrompt("input", backableInput);
30879
- inquirer13.registerPrompt("confirm", backableConfirm);
31237
+ inquirer14.registerPrompt("select", backableSelect);
31238
+ inquirer14.registerPrompt("checkbox", backableCheckbox);
31239
+ inquirer14.registerPrompt("input", backableInput);
31240
+ inquirer14.registerPrompt("confirm", backableConfirm);
30880
31241
  }
30881
31242
 
30882
31243
  // src/cli/shared/invokedCommand.ts