@haus-tech/haus-workflow 0.10.1 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { readFileSync as readFileSync3 } from "fs";
5
- import path27 from "path";
5
+ import path28 from "path";
6
6
  import { Command } from "commander";
7
7
 
8
8
  // src/commands/apply.ts
9
- import path10 from "path";
9
+ import path11 from "path";
10
10
  import checkbox from "@inquirer/checkbox";
11
11
 
12
12
  // src/catalog/remote-catalog.ts
@@ -158,8 +158,8 @@ async function getCacheManifestAge() {
158
158
  }
159
159
 
160
160
  // src/claude/write-claude-files.ts
161
- import path9 from "path";
162
- import fs8 from "fs-extra";
161
+ import path10 from "path";
162
+ import fs9 from "fs-extra";
163
163
 
164
164
  // src/update/hash-installed.ts
165
165
  import path3 from "path";
@@ -512,7 +512,8 @@ import path7 from "path";
512
512
  import fs6 from "fs-extra";
513
513
  var BLOCK_BEGIN = "<!-- HAUS:BEGIN haus-imports v=1 -->";
514
514
  var BLOCK_END = "<!-- HAUS:END haus-imports -->";
515
- var IMPORT_CONTENT = `@.haus-workflow/haus-way-of-work.md
515
+ var IMPORT_CONTENT = `@.haus-workflow/WORKFLOW.md
516
+ @.haus-workflow/workflow-config.md
516
517
  @.haus-workflow/project.md`;
517
518
  function buildImportBlock() {
518
519
  return `${BLOCK_BEGIN}
@@ -562,20 +563,52 @@ async function writeRootClaudeMd(root, dryRun) {
562
563
  return filePath;
563
564
  }
564
565
 
565
- // src/claude/write-way-of-work.ts
566
- import os3 from "os";
566
+ // src/claude/write-workflow-config.ts
567
567
  import path8 from "path";
568
568
  import fs7 from "fs-extra";
569
- var STABLE_ID2 = "template.way-of-work";
570
- var SCHEMA_VERSION2 = "1";
571
- var TEMPLATE_REL = "library/global/templates/haus-way-of-work.md";
572
- var CATALOG_CACHE_TEMPLATE = path8.join(
573
- os3.homedir(),
574
- CATALOG_CACHE_SUBDIR,
575
- "templates/haus-way-of-work.md"
576
- );
577
- function makeWayOfWorkHeader(pkgVersion, contentHash) {
578
- return `<!-- HAUS-MANAGED id=${STABLE_ID2} v=${SCHEMA_VERSION2} source=@haus-tech/haus-workflow@${pkgVersion} hash=${contentHash} -->`;
569
+ function buildWorkflowConfig(ctx) {
570
+ const pm = ctx.packageManager === "unknown" ? "npm" : ctx.packageManager;
571
+ const testCmd = pm + " test";
572
+ const auditCmd = pm + " audit";
573
+ return "# Project workflow configuration\n\n> Project-specific values for the workflow standard in WORKFLOW.md.\n> Edit freely \u2014 this file is project-owned and will not be overwritten by haus.\n\n## Source-of-truth documents\n- Spec: <!-- fill in path, e.g. docs/SPEC.md -->\n- Design: <!-- fill in path, e.g. docs/DESIGN.md -->\n- UX flows: <!-- fill in path, e.g. docs/UX.md -->\n\n## Commands\n- Test (unit + integration): `" + testCmd + "`\n- Test (E2E): <!-- fill in command -->\n- Type check: <!-- fill in command, e.g. tsc --noEmit -->\n- Lint: <!-- fill in command, e.g. npm run lint -->\n- Lint fix: <!-- fill in command, e.g. npm run lint -- --fix -->\n- Format check: <!-- fill in command, e.g. prettier --check . -->\n- Security audit: `" + auditCmd + "`\n\n## Validation library\n<!-- fill in, e.g. zod, yup, joi -->\n\n## Highest-stakes logic\n<!-- fill in domain areas requiring TDD-only treatment, e.g. payment flows, auth, medical data -->\n\n## Pre-commit tool\n<!-- fill in, e.g. lefthook, husky -->\n";
574
+ }
575
+ async function writeWorkflowConfig(root, dryRun) {
576
+ const destPath = hausPath(root, "workflow-config.md");
577
+ const printable = displayPath(root, destPath);
578
+ if (await fs7.pathExists(destPath)) {
579
+ if (dryRun) log(printable + ": exists (project-owned, skipping)");
580
+ return null;
581
+ }
582
+ const ctx = await readJson(hausPath(root, "context-map.json")) ?? {
583
+ mode: "fast",
584
+ generatedAt: "",
585
+ root,
586
+ repoName: path8.basename(root),
587
+ packageManager: "unknown",
588
+ repoRoles: [],
589
+ confidence: 0,
590
+ detectedStacks: {},
591
+ dependencies: [],
592
+ securityRisks: [],
593
+ crossRepoHints: [],
594
+ warnings: []
595
+ };
596
+ const content = buildWorkflowConfig(ctx);
597
+ if (dryRun) {
598
+ log(printable + ": would create");
599
+ return destPath;
600
+ }
601
+ await writeText(destPath, content);
602
+ return destPath;
603
+ }
604
+
605
+ // src/claude/write-workflow.ts
606
+ import path9 from "path";
607
+ import fs8 from "fs-extra";
608
+
609
+ // src/claude/managed-template.ts
610
+ function normaliseLF(content) {
611
+ return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
579
612
  }
580
613
  function parseHausManagedHeader(line) {
581
614
  const match = line.match(/<!-- HAUS-MANAGED id=([\w.:-]+)/);
@@ -583,23 +616,32 @@ function parseHausManagedHeader(line) {
583
616
  const hashMatch = line.match(/hash=(sha256-[a-f0-9]+)/);
584
617
  return { id: match[1], hash: hashMatch?.[1] };
585
618
  }
586
- async function writeWayOfWork(root, pkgVersion, dryRun) {
619
+
620
+ // src/claude/write-workflow.ts
621
+ var STABLE_ID2 = "template.workflow";
622
+ var SCHEMA_VERSION2 = "1";
623
+ var TEMPLATE_REL = "library/global/templates/agentic-workflow-standard.md";
624
+ var CATALOG_CACHE_TEMPLATE = path9.join(CACHE_DIR, "templates/agentic-workflow-standard.md");
625
+ function makeWorkflowHeader(pkgVersion, contentHash) {
626
+ return `<!-- HAUS-MANAGED id=${STABLE_ID2} v=${SCHEMA_VERSION2} source=@haus-tech/haus-workflow@${pkgVersion} hash=${contentHash} -->`;
627
+ }
628
+ async function writeWorkflow(root, pkgVersion, dryRun) {
587
629
  const cachePath = CATALOG_CACHE_TEMPLATE;
588
- const packagePath = path8.join(packageRoot(), TEMPLATE_REL);
589
- const templatePath = await fs7.pathExists(cachePath) ? cachePath : packagePath;
590
- if (!await fs7.pathExists(templatePath)) {
591
- warn(`Way-of-work template not found \u2014 run \`haus update\` to fetch from catalog`);
630
+ const packagePath = path9.join(packageRoot(), TEMPLATE_REL);
631
+ const templatePath = await fs8.pathExists(cachePath) ? cachePath : packagePath;
632
+ if (!await fs8.pathExists(templatePath)) {
633
+ warn(`Workflow template not found \u2014 run \`haus update\` to fetch from catalog`);
592
634
  return null;
593
635
  }
594
- const templateContent = await fs7.readFile(templatePath, "utf8");
595
- const contentHash = hashText(templateContent);
596
- const header = makeWayOfWorkHeader(pkgVersion, contentHash);
636
+ const templateContent = await fs8.readFile(templatePath, "utf8");
637
+ const contentHash = hashText(normaliseLF(templateContent));
638
+ const header = makeWorkflowHeader(pkgVersion, contentHash);
597
639
  const next = `${header}
598
640
  ${templateContent}`;
599
- const destPath = hausPath(root, "haus-way-of-work.md");
641
+ const destPath = hausPath(root, "WORKFLOW.md");
600
642
  const printable = displayPath(root, destPath);
601
- if (await fs7.pathExists(destPath)) {
602
- const existing = await fs7.readFile(destPath, "utf8");
643
+ if (await fs8.pathExists(destPath)) {
644
+ const existing = await fs8.readFile(destPath, "utf8");
603
645
  const firstLine = existing.split("\n")[0] ?? "";
604
646
  const parsed = parseHausManagedHeader(firstLine);
605
647
  if (!parsed) {
@@ -611,7 +653,7 @@ ${templateContent}`;
611
653
  return null;
612
654
  }
613
655
  const existingContent = existing.slice(firstLine.length + 1);
614
- if (parsed.hash && hashText(existingContent) !== parsed.hash) {
656
+ if (parsed.hash && hashText(normaliseLF(existingContent)) !== parsed.hash) {
615
657
  warn(`${printable}: content modified by user \u2014 skipping. Use --force to overwrite.`);
616
658
  return null;
617
659
  }
@@ -621,7 +663,7 @@ ${templateContent}`;
621
663
  }
622
664
  }
623
665
  if (dryRun) {
624
- const prev = await fs7.pathExists(destPath) ? await fs7.readFile(destPath, "utf8") : "";
666
+ const prev = await fs8.pathExists(destPath) ? await fs8.readFile(destPath, "utf8") : "";
625
667
  if (!prev) {
626
668
  log(createUnifiedDiff(printable, "", next));
627
669
  } else {
@@ -648,7 +690,7 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
648
690
  estimatedTokenReductionPct: 0
649
691
  };
650
692
  const pkgRoot = packageRoot();
651
- const hausVersion = (await readJson(path9.join(pkgRoot, "package.json")))?.version ?? "0.0.0";
693
+ const hausVersion = (await readJson(path10.join(pkgRoot, "package.json")))?.version ?? "0.0.0";
652
694
  const coreFiles = [
653
695
  claudePath(root, "settings.json"),
654
696
  claudePath(root, "rules", "haus.md"),
@@ -657,9 +699,15 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
657
699
  claudePath(root, "commands", "haus-review.md")
658
700
  ];
659
701
  const rootClaudeMdPath = await writeRootClaudeMd(root, dryRun);
660
- const wayOfWorkPath = await writeWayOfWork(root, hausVersion, dryRun);
702
+ const workflowPath = await writeWorkflow(root, hausVersion, dryRun);
703
+ const workflowConfigPath = await writeWorkflowConfig(root, dryRun);
661
704
  const projectFactsPath = await writeProjectFacts(root, hausVersion, dryRun);
662
- const p6Files = [rootClaudeMdPath, projectFactsPath, ...wayOfWorkPath ? [wayOfWorkPath] : []];
705
+ const p6Files = [
706
+ rootClaudeMdPath,
707
+ projectFactsPath,
708
+ ...workflowPath ? [workflowPath] : [],
709
+ ...workflowConfigPath ? [workflowConfigPath] : []
710
+ ];
663
711
  const files = dryRun ? [...coreFiles, ...p6Files] : [
664
712
  ...coreFiles,
665
713
  ...p6Files,
@@ -670,7 +718,7 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
670
718
  await writeManagedJson(root, claudePath(root, "settings.json"), hookSettings, dryRun);
671
719
  if (!dryRun) await assertPostApplySettingsMatchCanonical(root, hookSettings);
672
720
  const configPath = hausPath(root, "config.json");
673
- if (!await fs8.pathExists(configPath)) {
721
+ if (!await fs9.pathExists(configPath)) {
674
722
  await writeManagedJson(root, configPath, DEFAULT_HOOKS_CONFIG, dryRun);
675
723
  }
676
724
  await writeManagedText(
@@ -698,12 +746,12 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
698
746
  dryRun
699
747
  );
700
748
  const fixtureManifestPath = process.env["HAUS_FIXTURE_CATALOG"];
701
- const manifestPath = fixtureManifestPath ?? path9.join(pkgRoot, "library", "catalog", "manifest.json");
702
- const manifestDir = path9.dirname(manifestPath);
749
+ const manifestPath = fixtureManifestPath ?? path10.join(pkgRoot, "library", "catalog", "manifest.json");
750
+ const manifestDir = path10.dirname(manifestPath);
703
751
  const manifest = await readJson(manifestPath) ?? { items: [] };
704
752
  const manifestById = new Map((manifest.items ?? []).map((item) => [item.id, item]));
705
753
  const cacheManifest = await readJson(
706
- path9.join(CACHE_DIR, "manifest.json")
754
+ path10.join(CACHE_DIR, "manifest.json")
707
755
  );
708
756
  const cacheManifestById = new Map((cacheManifest?.items ?? []).map((item) => [item.id, item]));
709
757
  const installedPathsByItem = /* @__PURE__ */ new Map();
@@ -725,23 +773,23 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
725
773
  }
726
774
  }
727
775
  const cachedItem = cacheManifestById.get(item.id);
728
- const cachePath = cachedItem?.path ? path9.join(CACHE_DIR, cachedItem.path) : null;
729
- const sourcePath = cachePath && await fs8.pathExists(cachePath) ? cachePath : path9.join(manifestDir, manifestItem.path);
776
+ const cachePath = cachedItem?.path ? path10.join(CACHE_DIR, cachedItem.path) : null;
777
+ const sourcePath = cachePath && await fs9.pathExists(cachePath) ? cachePath : path10.join(manifestDir, manifestItem.path);
730
778
  const target = item.type === "agent" ? "agents" : item.type === "template" ? "templates" : "skills";
731
- const destination = claudePath(root, target, path9.basename(sourcePath));
732
- if (await fs8.pathExists(sourcePath)) {
779
+ const destination = claudePath(root, target, path10.basename(sourcePath));
780
+ if (await fs9.pathExists(sourcePath)) {
733
781
  if (dryRun) {
734
- const exists = await fs8.pathExists(destination);
782
+ const exists = await fs9.pathExists(destination);
735
783
  log(
736
784
  `${displayPath(root, destination)}: ${exists ? "would overwrite" : "would create"} (${item.id})`
737
785
  );
738
786
  } else {
739
- await fs8.ensureDir(path9.dirname(destination));
740
- await fs8.copy(sourcePath, destination, { overwrite: true, errorOnExist: false });
787
+ await fs9.ensureDir(path10.dirname(destination));
788
+ await fs9.copy(sourcePath, destination, { overwrite: true, errorOnExist: false });
741
789
  }
742
790
  files.push(destination);
743
791
  const current = installedPathsByItem.get(item.id) ?? [];
744
- installedPathsByItem.set(item.id, [...current, path9.relative(root, destination)]);
792
+ installedPathsByItem.set(item.id, [...current, path10.relative(root, destination)]);
745
793
  installedIds.add(item.id);
746
794
  } else {
747
795
  warn(
@@ -792,7 +840,7 @@ async function writeClaudeFiles(root, dryRun, selectedIds) {
792
840
  return [...new Set(files)];
793
841
  }
794
842
  async function writeManagedText(root, filePath, nextText, dryRun) {
795
- const prev = await fs8.pathExists(filePath) ? await fs8.readFile(filePath, "utf8") : "";
843
+ const prev = await fs9.pathExists(filePath) ? await fs9.readFile(filePath, "utf8") : "";
796
844
  const printable = displayPath(root, filePath);
797
845
  if (dryRun) {
798
846
  if (!prev) {
@@ -819,7 +867,7 @@ async function writeManagedJson(root, filePath, value, dryRun) {
819
867
 
820
868
  // src/commands/apply.ts
821
869
  async function cacheHasItems() {
822
- const data = await readJson(path10.join(CACHE_DIR, "manifest.json"));
870
+ const data = await readJson(path11.join(CACHE_DIR, "manifest.json"));
823
871
  return Array.isArray(data?.items) && data.items.length > 0;
824
872
  }
825
873
  async function runApply(options) {
@@ -886,9 +934,9 @@ async function runApply(options) {
886
934
  }
887
935
 
888
936
  // src/catalog/load-catalog.ts
889
- import os4 from "os";
890
- import path11 from "path";
891
- var CACHE_MANIFEST = path11.join(os4.homedir(), CATALOG_CACHE_SUBDIR, "manifest.json");
937
+ import os3 from "os";
938
+ import path12 from "path";
939
+ var CACHE_MANIFEST = path12.join(os3.homedir(), CATALOG_CACHE_SUBDIR, "manifest.json");
892
940
  async function loadCatalog(root) {
893
941
  const envPath = process.env["HAUS_FIXTURE_CATALOG"];
894
942
  if (envPath) {
@@ -897,10 +945,10 @@ async function loadCatalog(root) {
897
945
  }
898
946
  const cacheData = await readJson(CACHE_MANIFEST);
899
947
  if (cacheData?.items?.length) return cacheData.items;
900
- const localManifest = path11.join(root, "library/catalog/manifest.json");
948
+ const localManifest = path12.join(root, "library/catalog/manifest.json");
901
949
  const localData = await readJson(localManifest);
902
950
  if (localData?.items?.length) return localData.items;
903
- const packageManifest = path11.join(packageRoot(), "library/catalog/manifest.json");
951
+ const packageManifest = path12.join(packageRoot(), "library/catalog/manifest.json");
904
952
  const data = await readJson(packageManifest);
905
953
  return data?.items ?? [];
906
954
  }
@@ -940,7 +988,7 @@ async function runCatalogAudit() {
940
988
  }
941
989
 
942
990
  // src/commands/config.ts
943
- import path12 from "path";
991
+ import path13 from "path";
944
992
  var CONFIG_PATH2 = ".haus-workflow/config.json";
945
993
  var HOOK_ALIASES = {
946
994
  "hook.context": "context",
@@ -954,7 +1002,7 @@ async function runConfig(key, action) {
954
1002
  );
955
1003
  }
956
1004
  const root = process.cwd();
957
- const configPath = path12.join(root, CONFIG_PATH2);
1005
+ const configPath = path13.join(root, CONFIG_PATH2);
958
1006
  const existing = await readJson(configPath);
959
1007
  const cfg = existing ?? structuredClone(DEFAULT_HOOKS_CONFIG);
960
1008
  cfg.hooks ??= {};
@@ -1312,7 +1360,7 @@ function computeRuleIntents(rule) {
1312
1360
 
1313
1361
  // src/scanner/scan-project.ts
1314
1362
  import { readFile } from "fs/promises";
1315
- import path14 from "path";
1363
+ import path15 from "path";
1316
1364
 
1317
1365
  // src/utils/audit-checks.ts
1318
1366
  function isRecord(v) {
@@ -1339,8 +1387,8 @@ function compareVersions(a, b) {
1339
1387
  }
1340
1388
 
1341
1389
  // src/scanner/detect-package-manager.ts
1342
- import path13 from "path";
1343
- import fs9 from "fs-extra";
1390
+ import path14 from "path";
1391
+ import fs10 from "fs-extra";
1344
1392
  function detectPackageManager(root, packageManagerField) {
1345
1393
  const field = String(packageManagerField ?? "").trim();
1346
1394
  if (field.startsWith("yarn@")) {
@@ -1358,9 +1406,9 @@ function detectPackageManager(root, packageManagerField) {
1358
1406
  if (satisfiesVersion(version, ">=9")) return "npm";
1359
1407
  return "unknown";
1360
1408
  }
1361
- if (fs9.existsSync(path13.join(root, "yarn.lock"))) return "yarn";
1362
- if (fs9.existsSync(path13.join(root, "pnpm-lock.yaml"))) return "pnpm";
1363
- if (fs9.existsSync(path13.join(root, "package-lock.json"))) return "npm";
1409
+ if (fs10.existsSync(path14.join(root, "yarn.lock"))) return "yarn";
1410
+ if (fs10.existsSync(path14.join(root, "pnpm-lock.yaml"))) return "pnpm";
1411
+ if (fs10.existsSync(path14.join(root, "package-lock.json"))) return "npm";
1364
1412
  return "unknown";
1365
1413
  }
1366
1414
 
@@ -1421,8 +1469,8 @@ function blocked(rel) {
1421
1469
  return SENSITIVE.some((x) => x.test(rel));
1422
1470
  }
1423
1471
  async function scanProject(root, mode = "fast") {
1424
- const pkg = await readJson(path14.join(root, "package.json"));
1425
- const composer = await readJson(path14.join(root, "composer.json"));
1472
+ const pkg = await readJson(path15.join(root, "package.json"));
1473
+ const composer = await readJson(path15.join(root, "composer.json"));
1426
1474
  const files = await listFiles(root, SAFE_FILES);
1427
1475
  const safeFiles = files.filter((f) => !blocked(f));
1428
1476
  const deps = dependencySet(pkg, composer);
@@ -1450,7 +1498,7 @@ async function scanProject(root, mode = "fast") {
1450
1498
  mode,
1451
1499
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1452
1500
  root,
1453
- repoName: String(pkg?.name ?? path14.basename(root)),
1501
+ repoName: String(pkg?.name ?? path15.basename(root)),
1454
1502
  packageManager,
1455
1503
  repoRoles: roles,
1456
1504
  confidence: computeConfidence(roles, stacks),
@@ -1467,7 +1515,7 @@ async function scanProject(root, mode = "fast") {
1467
1515
  const scanHashes = Object.fromEntries(
1468
1516
  await Promise.all(
1469
1517
  safeFiles.map(
1470
- async (f) => [f, hashText(await readFile(path14.join(root, f), "utf8"))]
1518
+ async (f) => [f, hashText(await readFile(path15.join(root, f), "utf8"))]
1471
1519
  )
1472
1520
  )
1473
1521
  );
@@ -1634,7 +1682,7 @@ async function hasNeedle(root, files, needle) {
1634
1682
  );
1635
1683
  for (const rel of candidates.slice(0, 300)) {
1636
1684
  try {
1637
- const content = await readFile(path14.join(root, rel), "utf8");
1685
+ const content = await readFile(path15.join(root, rel), "utf8");
1638
1686
  if (content.includes(needle)) return true;
1639
1687
  } catch {
1640
1688
  continue;
@@ -1727,8 +1775,8 @@ async function runContext(options) {
1727
1775
  }
1728
1776
 
1729
1777
  // src/commands/doctor.ts
1730
- import path15 from "path";
1731
- import fs10 from "fs-extra";
1778
+ import path16 from "path";
1779
+ import fs11 from "fs-extra";
1732
1780
 
1733
1781
  // src/update/npm-version.ts
1734
1782
  var NPM_PACKAGE_NAME = "@haus-tech/haus-workflow";
@@ -1798,7 +1846,7 @@ async function runDoctor(options) {
1798
1846
  const enabled = await isHookEnabled(root, key);
1799
1847
  log(`- HOOK ${key}: ${enabled ? "enabled" : "disabled (default)"}`);
1800
1848
  }
1801
- const rootClaudeMdPath = path15.join(root, "CLAUDE.md");
1849
+ const rootClaudeMdPath = path16.join(root, "CLAUDE.md");
1802
1850
  const rootClaudeMdContent = await readText(rootClaudeMdPath);
1803
1851
  if (!rootClaudeMdContent) {
1804
1852
  warn("- CLAUDE.md: missing (run `haus apply --write` to create)");
@@ -1807,41 +1855,48 @@ async function runDoctor(options) {
1807
1855
  } else {
1808
1856
  log("- CLAUDE.md: import block present");
1809
1857
  }
1810
- const wayOfWorkPath = hausPath(root, "haus-way-of-work.md");
1811
- const wayOfWorkExists = await fs10.pathExists(wayOfWorkPath);
1812
- if (!wayOfWorkExists) {
1813
- warn("- .haus-workflow/haus-way-of-work.md: missing (run `haus apply --write`)");
1858
+ const workflowPath = hausPath(root, "WORKFLOW.md");
1859
+ const workflowExists = await fs11.pathExists(workflowPath);
1860
+ if (!workflowExists) {
1861
+ warn("- .haus-workflow/WORKFLOW.md: missing (run `haus apply --write`)");
1814
1862
  } else {
1815
- const wayOfWorkContent = await readText(wayOfWorkPath);
1816
- const firstLine = wayOfWorkContent?.split("\n")[0] ?? "";
1863
+ const workflowContent = await readText(workflowPath);
1864
+ const firstLine = workflowContent?.split("\n")[0] ?? "";
1817
1865
  if (!firstLine.includes("HAUS-MANAGED")) {
1818
- warn("- .haus-workflow/haus-way-of-work.md: no HAUS-MANAGED header (user-owned)");
1866
+ log("- .haus-workflow/WORKFLOW.md: OK (user-owned)");
1819
1867
  } else {
1820
1868
  const storedHashMatch = firstLine.match(/hash=(sha256-[a-f0-9]+)/);
1821
- const templatePath = path15.join(
1869
+ const cachePath = path16.join(CACHE_DIR, "templates/agentic-workflow-standard.md");
1870
+ const bundledPath = path16.join(
1822
1871
  packageRoot(),
1823
1872
  "library",
1824
1873
  "global",
1825
1874
  "templates",
1826
- "haus-way-of-work.md"
1875
+ "agentic-workflow-standard.md"
1827
1876
  );
1877
+ const templatePath = await fs11.pathExists(cachePath) ? cachePath : bundledPath;
1828
1878
  const templateContent = await readText(templatePath);
1829
1879
  if (storedHashMatch && templateContent) {
1830
- const currentHash = hashText(templateContent);
1880
+ const currentHash = hashText(normaliseLF(templateContent));
1831
1881
  if (storedHashMatch[1] !== currentHash) {
1832
- warn(
1833
- "- .haus-workflow/haus-way-of-work.md: stale (template updated \u2014 run `haus apply --write`)"
1834
- );
1882
+ warn("- .haus-workflow/WORKFLOW.md: stale (template updated \u2014 run `haus apply --write`)");
1835
1883
  } else {
1836
- log("- .haus-workflow/haus-way-of-work.md: OK");
1884
+ log("- .haus-workflow/WORKFLOW.md: OK");
1837
1885
  }
1838
1886
  } else {
1839
- log("- .haus-workflow/haus-way-of-work.md: OK");
1887
+ log("- .haus-workflow/WORKFLOW.md: OK");
1840
1888
  }
1841
1889
  }
1842
1890
  }
1891
+ const workflowConfigPath = hausPath(root, "workflow-config.md");
1892
+ const workflowConfigExists = await fs11.pathExists(workflowConfigPath);
1893
+ if (!workflowConfigExists) {
1894
+ warn("- .haus-workflow/workflow-config.md: missing (run `haus apply --write`)");
1895
+ } else {
1896
+ log("- .haus-workflow/workflow-config.md: OK (project-owned)");
1897
+ }
1843
1898
  const projectMdPath = hausPath(root, "project.md");
1844
- const projectMdExists = await fs10.pathExists(projectMdPath);
1899
+ const projectMdExists = await fs11.pathExists(projectMdPath);
1845
1900
  if (!projectMdExists) {
1846
1901
  warn("- .haus-workflow/project.md: missing (run `haus apply --write`)");
1847
1902
  } else {
@@ -1864,7 +1919,7 @@ async function runDoctor(options) {
1864
1919
  log(`- CATALOG CACHE: OK (${cacheAgeDays}d old)`);
1865
1920
  }
1866
1921
  }
1867
- const pkgJson = await readJson(path15.join(packageRoot(), "package.json"));
1922
+ const pkgJson = await readJson(path16.join(packageRoot(), "package.json"));
1868
1923
  const currentVersion = pkgJson?.version ?? "0.0.0";
1869
1924
  const npmStatus = await fetchNpmVersionStatus(currentVersion);
1870
1925
  if (npmStatus.updateAvailable && npmStatus.latest !== null) {
@@ -2031,8 +2086,8 @@ async function runGuard(kind, _options) {
2031
2086
  }
2032
2087
 
2033
2088
  // src/commands/init.ts
2034
- import path16 from "path";
2035
- import fs11 from "fs-extra";
2089
+ import path17 from "path";
2090
+ import fs12 from "fs-extra";
2036
2091
 
2037
2092
  // src/utils/exec.ts
2038
2093
  import { execa } from "execa";
@@ -2575,8 +2630,8 @@ async function runSetupProject(options) {
2575
2630
  // src/commands/init.ts
2576
2631
  async function runInit(options) {
2577
2632
  const root = process.cwd();
2578
- const hausDir = path16.join(root, ".haus-workflow");
2579
- const alreadyInit = await fs11.pathExists(hausDir);
2633
+ const hausDir = path17.join(root, ".haus-workflow");
2634
+ const alreadyInit = await fs12.pathExists(hausDir);
2580
2635
  if (alreadyInit) {
2581
2636
  log("Haus AI already initialized in this project.");
2582
2637
  log("Run `haus setup-project` to reconfigure.");
@@ -2588,8 +2643,8 @@ async function runInit(options) {
2588
2643
 
2589
2644
  // src/install/apply.ts
2590
2645
  import crypto2 from "crypto";
2591
- import path19 from "path";
2592
- import fs13 from "fs-extra";
2646
+ import path20 from "path";
2647
+ import fs14 from "fs-extra";
2593
2648
 
2594
2649
  // src/install/header.ts
2595
2650
  var MD_PREFIX = "<!-- HAUS-MANAGED";
@@ -2622,14 +2677,14 @@ ${content}`;
2622
2677
  }
2623
2678
 
2624
2679
  // src/install/manifest.ts
2625
- import os5 from "os";
2626
- import path17 from "path";
2680
+ import os4 from "os";
2681
+ import path18 from "path";
2627
2682
  var MANIFEST_SCHEMA = "haus-install-manifest/1";
2628
2683
  function globalClaudeDir() {
2629
- return path17.join(os5.homedir(), ".claude");
2684
+ return path18.join(os4.homedir(), ".claude");
2630
2685
  }
2631
2686
  function hausManifestPath() {
2632
- return path17.join(globalClaudeDir(), "haus", "install-manifest.json");
2687
+ return path18.join(globalClaudeDir(), "haus", "install-manifest.json");
2633
2688
  }
2634
2689
  async function readManifest() {
2635
2690
  return readJson(hausManifestPath());
@@ -2648,10 +2703,10 @@ function buildManifest(source, files, hooks) {
2648
2703
  }
2649
2704
 
2650
2705
  // src/install/settings-merge.ts
2651
- import path18 from "path";
2652
- import fs12 from "fs-extra";
2706
+ import path19 from "path";
2707
+ import fs13 from "fs-extra";
2653
2708
  function settingsJsonPath() {
2654
- return path18.join(globalClaudeDir(), "settings.json");
2709
+ return path19.join(globalClaudeDir(), "settings.json");
2655
2710
  }
2656
2711
  async function readSettings() {
2657
2712
  const parsed = await readJson(settingsJsonPath());
@@ -2707,7 +2762,7 @@ function stripHausHooks(settings) {
2707
2762
  async function loadHooksFragment(fragmentPath) {
2708
2763
  let raw;
2709
2764
  try {
2710
- raw = await fs12.readJson(fragmentPath);
2765
+ raw = await fs13.readJson(fragmentPath);
2711
2766
  } catch {
2712
2767
  return [];
2713
2768
  }
@@ -2722,40 +2777,40 @@ function hashContent(content) {
2722
2777
  }
2723
2778
  function sourceVersion() {
2724
2779
  try {
2725
- const pkgPath = path19.join(packageRoot(), "package.json");
2726
- const pkg = JSON.parse(fs13.readFileSync(pkgPath, "utf8"));
2780
+ const pkgPath = path20.join(packageRoot(), "package.json");
2781
+ const pkg = JSON.parse(fs14.readFileSync(pkgPath, "utf8"));
2727
2782
  return `${pkg.name ?? "haus"}@${pkg.version ?? "0.0.0"}`;
2728
2783
  } catch {
2729
2784
  return "haus@0.0.0";
2730
2785
  }
2731
2786
  }
2732
2787
  function globalSrcDir() {
2733
- return path19.join(packageRoot(), "library", "global");
2788
+ return path20.join(packageRoot(), "library", "global");
2734
2789
  }
2735
2790
  function collectSourceFiles(srcDir, claudeDir) {
2736
2791
  const entries = [];
2737
- const skillsDir = path19.join(srcDir, "skills");
2738
- if (fs13.pathExistsSync(skillsDir)) {
2739
- for (const skillName of fs13.readdirSync(skillsDir)) {
2740
- const skillFile = path19.join(skillsDir, skillName, "SKILL.md");
2741
- if (fs13.pathExistsSync(skillFile)) {
2792
+ const skillsDir = path20.join(srcDir, "skills");
2793
+ if (fs14.pathExistsSync(skillsDir)) {
2794
+ for (const skillName of fs14.readdirSync(skillsDir)) {
2795
+ const skillFile = path20.join(skillsDir, skillName, "SKILL.md");
2796
+ if (fs14.pathExistsSync(skillFile)) {
2742
2797
  entries.push({
2743
2798
  stableId: `skill.${skillName}`,
2744
- srcRelPath: path19.join("library", "global", "skills", skillName, "SKILL.md"),
2745
- destPath: path19.join(claudeDir, "skills", skillName, "SKILL.md")
2799
+ srcRelPath: path20.join("library", "global", "skills", skillName, "SKILL.md"),
2800
+ destPath: path20.join(claudeDir, "skills", skillName, "SKILL.md")
2746
2801
  });
2747
2802
  }
2748
2803
  }
2749
2804
  }
2750
- const agentsDir = path19.join(srcDir, "agents");
2751
- if (fs13.pathExistsSync(agentsDir)) {
2752
- for (const agentFile of fs13.readdirSync(agentsDir)) {
2805
+ const agentsDir = path20.join(srcDir, "agents");
2806
+ if (fs14.pathExistsSync(agentsDir)) {
2807
+ for (const agentFile of fs14.readdirSync(agentsDir)) {
2753
2808
  if (!agentFile.endsWith(".md")) continue;
2754
2809
  const agentName = agentFile.replace(/\.md$/, "");
2755
2810
  entries.push({
2756
2811
  stableId: `agent.${agentName}`,
2757
- srcRelPath: path19.join("library", "global", "agents", agentFile),
2758
- destPath: path19.join(claudeDir, "agents", agentFile)
2812
+ srcRelPath: path20.join("library", "global", "agents", agentFile),
2813
+ destPath: path20.join(claudeDir, "agents", agentFile)
2759
2814
  });
2760
2815
  }
2761
2816
  }
@@ -2779,7 +2834,7 @@ async function applyInstall(options = {}) {
2779
2834
  };
2780
2835
  const manifestFiles = [];
2781
2836
  for (const entry of sourceFiles) {
2782
- const srcPath = path19.join(packageRoot(), entry.srcRelPath);
2837
+ const srcPath = path20.join(packageRoot(), entry.srcRelPath);
2783
2838
  const rawContent = await readText(srcPath);
2784
2839
  if (rawContent === void 0) {
2785
2840
  warn(`Source file not found: ${entry.srcRelPath}`);
@@ -2799,7 +2854,7 @@ async function applyInstall(options = {}) {
2799
2854
  }
2800
2855
  continue;
2801
2856
  }
2802
- const destExists = fs13.pathExistsSync(entry.destPath);
2857
+ const destExists = fs14.pathExistsSync(entry.destPath);
2803
2858
  if (destExists) {
2804
2859
  const currentContent = await readText(entry.destPath);
2805
2860
  if (currentContent !== void 0) {
@@ -2835,7 +2890,7 @@ async function applyInstall(options = {}) {
2835
2890
  schemaVersion: SCHEMA_VERSION3
2836
2891
  });
2837
2892
  }
2838
- const fragmentPath = path19.join(srcDir, "settings-fragments", "hooks.json");
2893
+ const fragmentPath = path20.join(srcDir, "settings-fragments", "hooks.json");
2839
2894
  const fragments = await loadHooksFragment(fragmentPath);
2840
2895
  const settings = await readSettings();
2841
2896
  const { settings: mergedSettings, addedIds } = mergeHooks(settings, fragments);
@@ -2844,13 +2899,13 @@ async function applyInstall(options = {}) {
2844
2899
  const currentDestPaths = new Set(sourceFiles.map((f) => f.destPath));
2845
2900
  for (const entry of existingManifest.files) {
2846
2901
  if (currentDestPaths.has(entry.destPath)) continue;
2847
- if (!fs13.pathExistsSync(entry.destPath)) continue;
2902
+ if (!fs14.pathExistsSync(entry.destPath)) continue;
2848
2903
  const content = await readText(entry.destPath);
2849
2904
  if (!content) continue;
2850
2905
  const hasHeader = parseMarkdownHeader(content) !== void 0;
2851
2906
  const currentHash = hashContent(content);
2852
2907
  if (hasHeader && currentHash === entry.hash) {
2853
- if (!dryRun) await fs13.remove(entry.destPath);
2908
+ if (!dryRun) await fs14.remove(entry.destPath);
2854
2909
  result.deleted.push(entry.destPath);
2855
2910
  } else {
2856
2911
  warn(`Orphaned file ${entry.destPath} was user-modified \u2014 leaving in place`);
@@ -3033,20 +3088,20 @@ async function runScan(options) {
3033
3088
  }
3034
3089
 
3035
3090
  // src/commands/undo.ts
3036
- import path20 from "path";
3037
- import fs14 from "fs-extra";
3091
+ import path21 from "path";
3092
+ import fs15 from "fs-extra";
3038
3093
  var CLAUDE_DIR = ".claude";
3039
3094
  async function runUndo(options) {
3040
3095
  const root = process.cwd();
3041
- const targets = [path20.join(root, CLAUDE_DIR), path20.join(root, HAUS_DIR)];
3042
- const existing = targets.filter((p) => fs14.existsSync(p));
3096
+ const targets = [path21.join(root, CLAUDE_DIR), path21.join(root, HAUS_DIR)];
3097
+ const existing = targets.filter((p) => fs15.existsSync(p));
3043
3098
  if (existing.length === 0) {
3044
3099
  log("Nothing to remove: no .claude/ or .haus-workflow/ in this directory.");
3045
3100
  return;
3046
3101
  }
3047
3102
  if (!options.yes) {
3048
3103
  const ok = await confirm(
3049
- `Remove ${existing.map((p) => path20.relative(root, p)).join(" and ")}? This cannot be undone.`
3104
+ `Remove ${existing.map((p) => path21.relative(root, p)).join(" and ")}? This cannot be undone.`
3050
3105
  );
3051
3106
  if (!ok) {
3052
3107
  log("Cancelled.");
@@ -3054,15 +3109,15 @@ async function runUndo(options) {
3054
3109
  }
3055
3110
  }
3056
3111
  for (const p of existing) {
3057
- await fs14.remove(p);
3058
- log(`Removed ${path20.relative(root, p)}`);
3112
+ await fs15.remove(p);
3113
+ log(`Removed ${path21.relative(root, p)}`);
3059
3114
  }
3060
3115
  }
3061
3116
 
3062
3117
  // src/install/uninstall.ts
3063
3118
  import crypto3 from "crypto";
3064
- import path21 from "path";
3065
- import fs15 from "fs-extra";
3119
+ import path22 from "path";
3120
+ import fs16 from "fs-extra";
3066
3121
  async function runUninstall(options = {}) {
3067
3122
  const { force = false } = options;
3068
3123
  const manifest = await readManifest();
@@ -3072,7 +3127,7 @@ async function runUninstall(options = {}) {
3072
3127
  return result;
3073
3128
  }
3074
3129
  for (const entry of manifest.files) {
3075
- const exists = fs15.pathExistsSync(entry.destPath);
3130
+ const exists = fs16.pathExistsSync(entry.destPath);
3076
3131
  if (!exists) continue;
3077
3132
  const content = await readText(entry.destPath);
3078
3133
  if (content === void 0) continue;
@@ -3090,22 +3145,22 @@ async function runUninstall(options = {}) {
3090
3145
  result.skipped.push(entry.destPath);
3091
3146
  continue;
3092
3147
  }
3093
- await fs15.remove(entry.destPath);
3094
- await pruneEmptyDir(path21.dirname(entry.destPath));
3148
+ await fs16.remove(entry.destPath);
3149
+ await pruneEmptyDir(path22.dirname(entry.destPath));
3095
3150
  result.deleted.push(entry.destPath);
3096
3151
  }
3097
3152
  const settings = await readSettings();
3098
3153
  const stripped = stripHausHooks(settings);
3099
3154
  await writeSettings(stripped);
3100
3155
  result.hooksStripped = true;
3101
- const hausDir = path21.join(globalClaudeDir(), "haus");
3156
+ const hausDir = path22.join(globalClaudeDir(), "haus");
3102
3157
  const manifestPath = hausManifestPath();
3103
- if (fs15.pathExistsSync(manifestPath)) {
3104
- await fs15.remove(manifestPath);
3158
+ if (fs16.pathExistsSync(manifestPath)) {
3159
+ await fs16.remove(manifestPath);
3105
3160
  }
3106
- if (fs15.pathExistsSync(hausDir)) {
3107
- const remaining = await fs15.readdir(hausDir);
3108
- if (remaining.length === 0) await fs15.remove(hausDir);
3161
+ if (fs16.pathExistsSync(hausDir)) {
3162
+ const remaining = await fs16.readdir(hausDir);
3163
+ if (remaining.length === 0) await fs16.remove(hausDir);
3109
3164
  }
3110
3165
  return result;
3111
3166
  }
@@ -3124,8 +3179,8 @@ function printUninstallResult(result) {
3124
3179
  }
3125
3180
  async function pruneEmptyDir(dir) {
3126
3181
  try {
3127
- const entries = await fs15.readdir(dir);
3128
- if (entries.length === 0) await fs15.remove(dir);
3182
+ const entries = await fs16.readdir(dir);
3183
+ if (entries.length === 0) await fs16.remove(dir);
3129
3184
  } catch {
3130
3185
  }
3131
3186
  }
@@ -3143,7 +3198,7 @@ async function runUninstallCommand(options) {
3143
3198
  }
3144
3199
 
3145
3200
  // src/commands/update.ts
3146
- import path23 from "path";
3201
+ import path24 from "path";
3147
3202
 
3148
3203
  // src/update/diff-generated-files.ts
3149
3204
  function diffGeneratedFiles() {
@@ -3170,7 +3225,7 @@ function summarizeLockDiff(before, after) {
3170
3225
 
3171
3226
  // src/update/lockfile.ts
3172
3227
  import { mkdir, readFile as readFile2, copyFile } from "fs/promises";
3173
- import path22 from "path";
3228
+ import path23 from "path";
3174
3229
  async function checkLock(root) {
3175
3230
  const lock = await readJson(hausPath(root, "haus.lock.json")) ?? [];
3176
3231
  const hasValidVersions = lock.every(
@@ -3191,7 +3246,7 @@ async function applyLock(root) {
3191
3246
  try {
3192
3247
  const backupDir = hausPath(root, "backups");
3193
3248
  await mkdir(backupDir, { recursive: true });
3194
- await copyFile(lockPath, path22.join(backupDir, `haus.lock.${Date.now()}.json`));
3249
+ await copyFile(lockPath, path23.join(backupDir, `haus.lock.${Date.now()}.json`));
3195
3250
  } catch {
3196
3251
  }
3197
3252
  const enriched = await Promise.all(
@@ -3213,7 +3268,7 @@ function diffLock(before, after) {
3213
3268
  }
3214
3269
  async function hasLocalOverrides(root) {
3215
3270
  try {
3216
- await readFile2(path22.join(root, ".claude", "settings.json"), "utf8");
3271
+ await readFile2(path23.join(root, ".claude", "settings.json"), "utf8");
3217
3272
  return true;
3218
3273
  } catch {
3219
3274
  return false;
@@ -3225,7 +3280,7 @@ var NPM_PACKAGE_NAME2 = "@haus-tech/haus-workflow";
3225
3280
  async function runUpdate(options) {
3226
3281
  const root = process.cwd();
3227
3282
  if (options.check) {
3228
- const pkgJson2 = await readJson(path23.join(packageRoot(), "package.json"));
3283
+ const pkgJson2 = await readJson(path24.join(packageRoot(), "package.json"));
3229
3284
  const currentVersion2 = pkgJson2?.version ?? "0.0.0";
3230
3285
  const [status, npmVersion, latestCatalogTag] = await Promise.all([
3231
3286
  checkLock(root),
@@ -3252,7 +3307,7 @@ async function runUpdate(options) {
3252
3307
  if (!status.ok) process.exitCode = 1;
3253
3308
  return;
3254
3309
  }
3255
- const pkgJson = await readJson(path23.join(packageRoot(), "package.json"));
3310
+ const pkgJson = await readJson(path24.join(packageRoot(), "package.json"));
3256
3311
  const currentVersion = pkgJson?.version ?? "0.0.0";
3257
3312
  const npmStatus = await fetchNpmVersionStatus(currentVersion);
3258
3313
  if (npmStatus.updateAvailable && npmStatus.latest !== null) {
@@ -3282,14 +3337,14 @@ async function runUpdate(options) {
3282
3337
  }
3283
3338
 
3284
3339
  // src/commands/validate-catalog.ts
3285
- import fs16 from "fs";
3286
- import path25 from "path";
3340
+ import fs17 from "fs";
3341
+ import path26 from "path";
3287
3342
 
3288
3343
  // src/catalog/allowed-stacks.ts
3289
- import path24 from "path";
3344
+ import path25 from "path";
3290
3345
  async function readAllowedStacks(root) {
3291
3346
  const data = await readJson(
3292
- path24.join(root, "library", "catalog", "allowed-stacks.json")
3347
+ path25.join(root, "library", "catalog", "allowed-stacks.json")
3293
3348
  );
3294
3349
  return data?.stacks ?? [];
3295
3350
  }
@@ -3394,23 +3449,23 @@ function auditShippedFiles(manifestDir, items) {
3394
3449
  const failures = [];
3395
3450
  for (const item of items) {
3396
3451
  if (!item.path) continue;
3397
- const absPath = path25.join(manifestDir, item.path);
3452
+ const absPath = path26.join(manifestDir, item.path);
3398
3453
  if (item.type === "skill") {
3399
- const skillMd = path25.join(absPath, "SKILL.md");
3400
- if (!fs16.existsSync(skillMd)) {
3401
- failures.push(`${item.id}: missing ${path25.relative(manifestDir, skillMd)}`);
3454
+ const skillMd = path26.join(absPath, "SKILL.md");
3455
+ if (!fs17.existsSync(skillMd)) {
3456
+ failures.push(`${item.id}: missing ${path26.relative(manifestDir, skillMd)}`);
3402
3457
  continue;
3403
3458
  }
3404
- const text = fs16.readFileSync(skillMd, "utf8");
3459
+ const text = fs17.readFileSync(skillMd, "utf8");
3405
3460
  for (const section of REQUIRED_SKILL_SECTIONS) {
3406
3461
  if (!text.includes(section)) failures.push(`${item.id}: SKILL.md missing ${section}`);
3407
3462
  }
3408
3463
  } else if (item.type === "agent") {
3409
- if (!fs16.existsSync(absPath)) {
3464
+ if (!fs17.existsSync(absPath)) {
3410
3465
  failures.push(`${item.id}: missing agent file ${item.path}`);
3411
3466
  continue;
3412
3467
  }
3413
- const text = fs16.readFileSync(absPath, "utf8");
3468
+ const text = fs17.readFileSync(absPath, "utf8");
3414
3469
  if (!text.startsWith("---")) failures.push(`${item.id}: agent file missing YAML frontmatter`);
3415
3470
  for (const section of REQUIRED_AGENT_SECTIONS) {
3416
3471
  if (!text.includes(section)) failures.push(`${item.id}: agent file missing ${section}`);
@@ -3421,7 +3476,7 @@ function auditShippedFiles(manifestDir, items) {
3421
3476
  failures.push(`${item.id}: agent file contains disallowed phrase "${phrase}"`);
3422
3477
  }
3423
3478
  } else if (item.type === "template") {
3424
- if (!fs16.existsSync(absPath)) {
3479
+ if (!fs17.existsSync(absPath)) {
3425
3480
  failures.push(`${item.id}: missing template file ${item.path}`);
3426
3481
  }
3427
3482
  }
@@ -3432,11 +3487,11 @@ function auditMarkdownContent(manifestDir) {
3432
3487
  const failures = [];
3433
3488
  const dirs = ["skills", "agents"];
3434
3489
  for (const dir of dirs) {
3435
- const abs = path25.join(manifestDir, dir);
3436
- if (!fs16.existsSync(abs)) continue;
3490
+ const abs = path26.join(manifestDir, dir);
3491
+ if (!fs17.existsSync(abs)) continue;
3437
3492
  walkMd(abs, (file) => {
3438
- const text = fs16.readFileSync(file, "utf8");
3439
- const rel = path25.relative(manifestDir, file);
3493
+ const text = fs17.readFileSync(file, "utf8");
3494
+ const rel = path26.relative(manifestDir, file);
3440
3495
  const lines = text.split(/\r?\n/);
3441
3496
  for (let i = 0; i < lines.length; i++) {
3442
3497
  const line = lines[i] ?? "";
@@ -3455,8 +3510,8 @@ function auditMarkdownContent(manifestDir) {
3455
3510
  return failures;
3456
3511
  }
3457
3512
  function walkMd(dir, fn) {
3458
- for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
3459
- const full = path25.join(dir, entry.name);
3513
+ for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
3514
+ const full = path26.join(dir, entry.name);
3460
3515
  if (entry.isDirectory()) walkMd(full, fn);
3461
3516
  else if (entry.name.endsWith(".md")) fn(full);
3462
3517
  }
@@ -3467,8 +3522,8 @@ async function runValidateCatalog(manifestPath) {
3467
3522
  process.exitCode = 1;
3468
3523
  return;
3469
3524
  }
3470
- const abs = path25.resolve(process.cwd(), manifestPath);
3471
- const manifestDir = path25.dirname(abs);
3525
+ const abs = path26.resolve(process.cwd(), manifestPath);
3526
+ const manifestDir = path26.dirname(abs);
3472
3527
  const data = await readJson(abs);
3473
3528
  if (!data?.items) {
3474
3529
  error(`Could not read catalog manifest at ${abs}`);
@@ -3507,7 +3562,7 @@ async function runValidateCatalog(manifestPath) {
3507
3562
  }
3508
3563
 
3509
3564
  // src/commands/workspace.ts
3510
- import path26 from "path";
3565
+ import path27 from "path";
3511
3566
  import YAML from "yaml";
3512
3567
  async function runWorkspace(action) {
3513
3568
  if (action === "init") {
@@ -3540,7 +3595,7 @@ relationships: []
3540
3595
  const summaries = [];
3541
3596
  const ownership = {};
3542
3597
  for (const repo of repos) {
3543
- const repoRoot = path26.resolve(process.cwd(), repo.path);
3598
+ const repoRoot = path27.resolve(process.cwd(), repo.path);
3544
3599
  const result = await scanProject(repoRoot, "fast");
3545
3600
  summaries.push({
3546
3601
  name: repo.name,
@@ -3576,7 +3631,7 @@ ${summaries.map(
3576
3631
  // src/cli.ts
3577
3632
  function cliVersion() {
3578
3633
  try {
3579
- const pkgPath = path27.join(packageRoot(), "package.json");
3634
+ const pkgPath = path28.join(packageRoot(), "package.json");
3580
3635
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
3581
3636
  return pkg.version ?? "0.0.0";
3582
3637
  } catch {
@@ -3586,7 +3641,7 @@ function cliVersion() {
3586
3641
  var program = new Command();
3587
3642
  function validateRuntimeNodeVersion() {
3588
3643
  try {
3589
- const pkgPath = path27.join(packageRoot(), "package.json");
3644
+ const pkgPath = path28.join(packageRoot(), "package.json");
3590
3645
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
3591
3646
  const requiredRange = pkg.engines?.node;
3592
3647
  if (requiredRange && !satisfiesVersion(process.version, requiredRange)) {