@haus-tech/haus-workflow 0.12.0 → 0.12.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cli.js +103 -91
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.12.1](https://github.com/WeAreHausTech/haus-workflow/compare/v0.12.0...v0.12.1) (2026-06-03)
4
+
5
+ ### Bug Fixes
6
+
7
+ - **template:** address review — dry-run safety, empty-body, hermetic test ([6744f94](https://github.com/WeAreHausTech/haus-workflow/commit/6744f94104b03de51829962c25b7706d3d0124bf))
8
+ - **template:** fetch workflow standard from catalog when cache is empty ([0168f5e](https://github.com/WeAreHausTech/haus-workflow/commit/0168f5ed028f8d3bf6588297d5472511cf4813b6))
9
+
3
10
  ## [0.12.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.11.1...v0.12.0) (2026-06-03)
4
11
 
5
12
  ### Features
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 path30 from "path";
5
+ import path29 from "path";
6
6
  import { Command } from "commander";
7
7
 
8
8
  // src/commands/apply.ts
9
- import path12 from "path";
9
+ import path11 from "path";
10
10
  import checkbox from "@inquirer/checkbox";
11
11
 
12
12
  // src/catalog/remote-catalog.ts
@@ -53,6 +53,18 @@ async function fetchRemoteManifest() {
53
53
  return null;
54
54
  }
55
55
  }
56
+ var WORKFLOW_TEMPLATE_REL = "templates/agentic-workflow-standard.md";
57
+ async function readWorkflowTemplate(opts = {}) {
58
+ const dest = path.join(CACHE_DIR, WORKFLOW_TEMPLATE_REL);
59
+ if (await fs.pathExists(dest)) return fs.readFile(dest, "utf8");
60
+ const text = await fetchText(`${REMOTE_BASE}/${WORKFLOW_TEMPLATE_REL}`);
61
+ if (text === null) return null;
62
+ if (!opts.dryRun) {
63
+ await fs.ensureDir(path.dirname(dest));
64
+ await fs.writeFile(dest, text, "utf8");
65
+ }
66
+ return text;
67
+ }
56
68
  function isSafeCatalogPath(itemPath) {
57
69
  if (!itemPath || path.isAbsolute(itemPath) || itemPath.includes("\\")) return false;
58
70
  const normalized = path.normalize(itemPath);
@@ -80,7 +92,8 @@ async function syncRemoteCatalog() {
80
92
  let unchanged = 0;
81
93
  const failed = [];
82
94
  for (const item of items) {
83
- if (item.type !== "skill" && item.type !== "agent" || !item.path) continue;
95
+ if (item.type !== "skill" && item.type !== "agent" && item.type !== "template" || !item.path)
96
+ continue;
84
97
  if (!isSafeCatalogPath(item.path)) {
85
98
  warn(`Skipping ${item.id}: invalid path "${item.path}"`);
86
99
  failed.push(item.id);
@@ -158,7 +171,7 @@ async function getCacheManifestAge() {
158
171
  }
159
172
 
160
173
  // src/claude/write-claude-files.ts
161
- import path11 from "path";
174
+ import path10 from "path";
162
175
  import fs10 from "fs-extra";
163
176
 
164
177
  // src/update/hash-installed.ts
@@ -403,8 +416,8 @@ function buildDenyRules() {
403
416
  for (const command of DANGEROUS_COMMANDS) {
404
417
  rules.push(`Bash(${command}:*)`);
405
418
  }
406
- for (const path31 of SENSITIVE_PATHS) {
407
- const pattern = SENSITIVE_DIRS.has(path31) ? `${path31}/**` : path31;
419
+ for (const path30 of SENSITIVE_PATHS) {
420
+ const pattern = SENSITIVE_DIRS.has(path30) ? `${path30}/**` : path30;
408
421
  for (const tool of FILE_TOOLS) {
409
422
  rules.push(`${tool}(${pattern})`);
410
423
  }
@@ -802,7 +815,6 @@ async function writeWorkflowConfig(root, dryRun, opts = {}) {
802
815
  }
803
816
 
804
817
  // src/claude/write-workflow.ts
805
- import path10 from "path";
806
818
  import fs9 from "fs-extra";
807
819
 
808
820
  // src/claude/managed-template.ts
@@ -819,17 +831,17 @@ function parseHausManagedHeader(line2) {
819
831
  // src/claude/write-workflow.ts
820
832
  var STABLE_ID2 = "template.workflow";
821
833
  var SCHEMA_VERSION2 = "1";
822
- var CATALOG_CACHE_TEMPLATE = path10.join(CACHE_DIR, "templates/agentic-workflow-standard.md");
823
834
  function makeWorkflowHeader(pkgVersion, contentHash) {
824
835
  return `<!-- HAUS-MANAGED id=${STABLE_ID2} v=${SCHEMA_VERSION2} source=@haus-tech/haus-workflow@${pkgVersion} hash=${contentHash} -->`;
825
836
  }
826
837
  async function writeWorkflow(root, pkgVersion, dryRun) {
827
- if (!await fs9.pathExists(CATALOG_CACHE_TEMPLATE)) {
828
- warn(`Workflow template not found \u2014 run \`haus update\` to fetch from catalog`);
838
+ const templateContent = await readWorkflowTemplate({ dryRun });
839
+ if (templateContent === null) {
840
+ warn(
841
+ `Workflow template could not be fetched from the catalog \u2014 check your network, then re-run \`haus apply --write\` (or \`haus update\`)`
842
+ );
829
843
  return null;
830
844
  }
831
- const templatePath = CATALOG_CACHE_TEMPLATE;
832
- const templateContent = await fs9.readFile(templatePath, "utf8");
833
845
  const contentHash = hashText(normaliseLF(templateContent));
834
846
  const header = makeWorkflowHeader(pkgVersion, contentHash);
835
847
  const next = `${header}
@@ -886,7 +898,7 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
886
898
  estimatedTokenReductionPct: 0
887
899
  };
888
900
  const pkgRoot = packageRoot();
889
- const hausVersion = (await readJson(path11.join(pkgRoot, "package.json")))?.version ?? "0.0.0";
901
+ const hausVersion = (await readJson(path10.join(pkgRoot, "package.json")))?.version ?? "0.0.0";
890
902
  const coreFiles = [
891
903
  claudePath(root, "settings.json"),
892
904
  claudePath(root, "rules", "haus.md"),
@@ -944,12 +956,12 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
944
956
  dryRun
945
957
  );
946
958
  const fixtureManifestPath = process.env["HAUS_FIXTURE_CATALOG"];
947
- const manifestPath = fixtureManifestPath ?? path11.join(pkgRoot, "library", "catalog", "manifest.json");
948
- const manifestDir = path11.dirname(manifestPath);
959
+ const manifestPath = fixtureManifestPath ?? path10.join(pkgRoot, "library", "catalog", "manifest.json");
960
+ const manifestDir = path10.dirname(manifestPath);
949
961
  const manifest = await readJson(manifestPath) ?? { items: [] };
950
962
  const manifestById = new Map((manifest.items ?? []).map((item) => [item.id, item]));
951
963
  const cacheManifest = await readJson(
952
- path11.join(CACHE_DIR, "manifest.json")
964
+ path10.join(CACHE_DIR, "manifest.json")
953
965
  );
954
966
  const cacheManifestById = new Map((cacheManifest?.items ?? []).map((item) => [item.id, item]));
955
967
  const installedPathsByItem = /* @__PURE__ */ new Map();
@@ -971,10 +983,10 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
971
983
  }
972
984
  }
973
985
  const cachedItem = cacheManifestById.get(item.id);
974
- const cachePath = cachedItem?.path ? path11.join(CACHE_DIR, cachedItem.path) : null;
975
- const sourcePath = cachePath && await fs10.pathExists(cachePath) ? cachePath : path11.join(manifestDir, manifestItem.path);
986
+ const cachePath = cachedItem?.path ? path10.join(CACHE_DIR, cachedItem.path) : null;
987
+ const sourcePath = cachePath && await fs10.pathExists(cachePath) ? cachePath : path10.join(manifestDir, manifestItem.path);
976
988
  const target = item.type === "agent" ? "agents" : item.type === "template" ? "templates" : "skills";
977
- const destination = claudePath(root, target, path11.basename(sourcePath));
989
+ const destination = claudePath(root, target, path10.basename(sourcePath));
978
990
  if (await fs10.pathExists(sourcePath)) {
979
991
  if (dryRun) {
980
992
  const exists = await fs10.pathExists(destination);
@@ -982,12 +994,12 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
982
994
  `${displayPath(root, destination)}: ${exists ? "would overwrite" : "would create"} (${item.id})`
983
995
  );
984
996
  } else {
985
- await fs10.ensureDir(path11.dirname(destination));
997
+ await fs10.ensureDir(path10.dirname(destination));
986
998
  await fs10.copy(sourcePath, destination, { overwrite: true, errorOnExist: false });
987
999
  }
988
1000
  files.push(destination);
989
1001
  const current = installedPathsByItem.get(item.id) ?? [];
990
- installedPathsByItem.set(item.id, [...current, path11.relative(root, destination)]);
1002
+ installedPathsByItem.set(item.id, [...current, path10.relative(root, destination)]);
991
1003
  installedIds.add(item.id);
992
1004
  } else {
993
1005
  warn(
@@ -1065,7 +1077,7 @@ async function writeManagedJson(root, filePath, value, dryRun) {
1065
1077
 
1066
1078
  // src/commands/apply.ts
1067
1079
  async function cacheHasItems() {
1068
- const data = await readJson(path12.join(CACHE_DIR, "manifest.json"));
1080
+ const data = await readJson(path11.join(CACHE_DIR, "manifest.json"));
1069
1081
  return Array.isArray(data?.items) && data.items.length > 0;
1070
1082
  }
1071
1083
  async function runApply(options) {
@@ -1135,8 +1147,8 @@ async function runApply(options) {
1135
1147
 
1136
1148
  // src/catalog/load-catalog.ts
1137
1149
  import os3 from "os";
1138
- import path13 from "path";
1139
- var CACHE_MANIFEST = path13.join(os3.homedir(), CATALOG_CACHE_SUBDIR, "manifest.json");
1150
+ import path12 from "path";
1151
+ var CACHE_MANIFEST = path12.join(os3.homedir(), CATALOG_CACHE_SUBDIR, "manifest.json");
1140
1152
  async function loadCatalog(root) {
1141
1153
  const envPath = process.env["HAUS_FIXTURE_CATALOG"];
1142
1154
  if (envPath) {
@@ -1145,10 +1157,10 @@ async function loadCatalog(root) {
1145
1157
  }
1146
1158
  const cacheData = await readJson(CACHE_MANIFEST);
1147
1159
  if (cacheData?.items?.length) return cacheData.items;
1148
- const localManifest = path13.join(root, "library/catalog/manifest.json");
1160
+ const localManifest = path12.join(root, "library/catalog/manifest.json");
1149
1161
  const localData = await readJson(localManifest);
1150
1162
  if (localData?.items?.length) return localData.items;
1151
- const packageManifest = path13.join(packageRoot(), "library/catalog/manifest.json");
1163
+ const packageManifest = path12.join(packageRoot(), "library/catalog/manifest.json");
1152
1164
  const data = await readJson(packageManifest);
1153
1165
  return data?.items ?? [];
1154
1166
  }
@@ -1188,7 +1200,7 @@ async function runCatalogAudit() {
1188
1200
  }
1189
1201
 
1190
1202
  // src/commands/config.ts
1191
- import path14 from "path";
1203
+ import path13 from "path";
1192
1204
  var CONFIG_PATH2 = ".haus-workflow/config.json";
1193
1205
  var HOOK_ALIASES = {
1194
1206
  "hook.context": "context"
@@ -1201,7 +1213,7 @@ async function runConfig(key, action) {
1201
1213
  );
1202
1214
  }
1203
1215
  const root = process.cwd();
1204
- const configPath = path14.join(root, CONFIG_PATH2);
1216
+ const configPath = path13.join(root, CONFIG_PATH2);
1205
1217
  const existing = await readJson(configPath);
1206
1218
  const cfg = existing ?? structuredClone(DEFAULT_HOOKS_CONFIG);
1207
1219
  cfg.hooks ??= {};
@@ -1588,7 +1600,7 @@ function selectRules(recommended, task, taskIntents) {
1588
1600
 
1589
1601
  // src/scanner/scan-project.ts
1590
1602
  import { readFile as readFile2 } from "fs/promises";
1591
- import path18 from "path";
1603
+ import path17 from "path";
1592
1604
 
1593
1605
  // src/utils/audit-checks.ts
1594
1606
  function isRecord(v) {
@@ -1615,7 +1627,7 @@ function compareVersions(a, b) {
1615
1627
  }
1616
1628
 
1617
1629
  // src/scanner/detect-package-manager.ts
1618
- import path15 from "path";
1630
+ import path14 from "path";
1619
1631
  import fs11 from "fs-extra";
1620
1632
  function detectPackageManager(root, packageManagerField) {
1621
1633
  const field = String(packageManagerField ?? "").trim();
@@ -1634,9 +1646,9 @@ function detectPackageManager(root, packageManagerField) {
1634
1646
  if (satisfiesVersion(version, ">=9")) return "npm";
1635
1647
  return "unknown";
1636
1648
  }
1637
- if (fs11.existsSync(path15.join(root, "yarn.lock"))) return "yarn";
1638
- if (fs11.existsSync(path15.join(root, "pnpm-lock.yaml"))) return "pnpm";
1639
- if (fs11.existsSync(path15.join(root, "package-lock.json"))) return "npm";
1649
+ if (fs11.existsSync(path14.join(root, "yarn.lock"))) return "yarn";
1650
+ if (fs11.existsSync(path14.join(root, "pnpm-lock.yaml"))) return "pnpm";
1651
+ if (fs11.existsSync(path14.join(root, "package-lock.json"))) return "npm";
1640
1652
  return "unknown";
1641
1653
  }
1642
1654
 
@@ -1809,7 +1821,7 @@ function runDetection(ctx, rules = STACK_RULES) {
1809
1821
  }
1810
1822
 
1811
1823
  // src/scanner/detection.ts
1812
- import path16 from "path";
1824
+ import path15 from "path";
1813
1825
  var UNSUPPORTED_MARKERS = {
1814
1826
  "requirements.txt": "python",
1815
1827
  "pyproject.toml": "python",
@@ -1863,14 +1875,14 @@ function finalizeRoles(registryRoles, deps, files) {
1863
1875
  function collectUnsupportedSignals(files) {
1864
1876
  return [
1865
1877
  ...new Set(
1866
- files.map((f) => UNSUPPORTED_MARKERS[path16.basename(f)]).filter((s) => Boolean(s))
1878
+ files.map((f) => UNSUPPORTED_MARKERS[path15.basename(f)]).filter((s) => Boolean(s))
1867
1879
  )
1868
1880
  ].sort();
1869
1881
  }
1870
1882
 
1871
1883
  // src/scanner/render.ts
1872
1884
  import { readFile } from "fs/promises";
1873
- import path17 from "path";
1885
+ import path16 from "path";
1874
1886
 
1875
1887
  // src/scanner/role-labels.ts
1876
1888
  var ROLE_LABELS = {
@@ -1936,7 +1948,7 @@ async function buildContentBlob(root, files) {
1936
1948
  const batch = await Promise.all(
1937
1949
  slice.slice(i, i + CHUNK).map(async (rel) => {
1938
1950
  try {
1939
- return await readFile(path17.join(root, rel), "utf8");
1951
+ return await readFile(path16.join(root, rel), "utf8");
1940
1952
  } catch {
1941
1953
  return "";
1942
1954
  }
@@ -2010,8 +2022,8 @@ var SAFE_FILES = [
2010
2022
  "Gemfile"
2011
2023
  ];
2012
2024
  async function scanProject(root, mode = "fast") {
2013
- const pkg = await readJson(path18.join(root, "package.json"));
2014
- const composer = await readJson(path18.join(root, "composer.json"));
2025
+ const pkg = await readJson(path17.join(root, "package.json"));
2026
+ const composer = await readJson(path17.join(root, "composer.json"));
2015
2027
  const files = await listFiles(root, SAFE_FILES);
2016
2028
  const safeFiles = files.filter((f) => !blocked(f));
2017
2029
  const deps = dependencySet(pkg, composer);
@@ -2045,7 +2057,7 @@ async function scanProject(root, mode = "fast") {
2045
2057
  mode,
2046
2058
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2047
2059
  root,
2048
- repoName: String(pkg?.name ?? path18.basename(root)),
2060
+ repoName: String(pkg?.name ?? path17.basename(root)),
2049
2061
  packageManager,
2050
2062
  repoRoles: roles,
2051
2063
  confidence: computeConfidence(roles, stacks),
@@ -2064,7 +2076,7 @@ async function scanProject(root, mode = "fast") {
2064
2076
  const scanHashes = Object.fromEntries(
2065
2077
  await Promise.all(
2066
2078
  safeFiles.map(
2067
- async (f) => [f, hashText(await readFile2(path18.join(root, f), "utf8"))]
2079
+ async (f) => [f, hashText(await readFile2(path17.join(root, f), "utf8"))]
2068
2080
  )
2069
2081
  )
2070
2082
  );
@@ -2148,7 +2160,7 @@ async function runContext(options) {
2148
2160
  }
2149
2161
 
2150
2162
  // src/commands/doctor.ts
2151
- import path19 from "path";
2163
+ import path18 from "path";
2152
2164
  import fs12 from "fs-extra";
2153
2165
 
2154
2166
  // src/update/npm-version.ts
@@ -2229,7 +2241,7 @@ async function runDoctor(options) {
2229
2241
  const enabled = await isHookEnabled(root, key);
2230
2242
  ok(`- HOOK ${key}: ${enabled ? "enabled" : "disabled (default)"}`);
2231
2243
  }
2232
- const rootClaudeMdPath = path19.join(root, "CLAUDE.md");
2244
+ const rootClaudeMdPath = path18.join(root, "CLAUDE.md");
2233
2245
  const rootClaudeMdContent = await readText(rootClaudeMdPath);
2234
2246
  if (!rootClaudeMdContent) {
2235
2247
  flag(
@@ -2282,8 +2294,8 @@ async function runDoctor(options) {
2282
2294
  ok("- .haus-workflow/WORKFLOW.md: OK (user-owned)");
2283
2295
  } else {
2284
2296
  const storedHashMatch = firstLine.match(/hash=(sha256-[a-f0-9]+)/);
2285
- const cachePath = path19.join(CACHE_DIR, "templates/agentic-workflow-standard.md");
2286
- const bundledPath = path19.join(
2297
+ const cachePath = path18.join(CACHE_DIR, "templates/agentic-workflow-standard.md");
2298
+ const bundledPath = path18.join(
2287
2299
  packageRoot(),
2288
2300
  "library",
2289
2301
  "global",
@@ -2365,7 +2377,7 @@ async function runDoctor(options) {
2365
2377
  ok(`- CATALOG CACHE: OK (${cacheAgeDays}d old)`);
2366
2378
  }
2367
2379
  }
2368
- const pkgJson = await readJson(path19.join(packageRoot(), "package.json"));
2380
+ const pkgJson = await readJson(path18.join(packageRoot(), "package.json"));
2369
2381
  const currentVersion = pkgJson?.version ?? "0.0.0";
2370
2382
  const npmStatus = await fetchNpmVersionStatus(currentVersion);
2371
2383
  if (npmStatus.updateAvailable && npmStatus.latest !== null) {
@@ -2507,7 +2519,7 @@ async function runGuard(kind, _options) {
2507
2519
  }
2508
2520
 
2509
2521
  // src/commands/init.ts
2510
- import path20 from "path";
2522
+ import path19 from "path";
2511
2523
  import fs13 from "fs-extra";
2512
2524
 
2513
2525
  // src/recommender/ecosystem.ts
@@ -3074,7 +3086,7 @@ async function runSetupProject(options) {
3074
3086
  // src/commands/init.ts
3075
3087
  async function runInit(options) {
3076
3088
  const root = process.cwd();
3077
- const hausDir = path20.join(root, ".haus-workflow");
3089
+ const hausDir = path19.join(root, ".haus-workflow");
3078
3090
  const alreadyInit = await fs13.pathExists(hausDir);
3079
3091
  if (alreadyInit) {
3080
3092
  log("Haus AI already initialized in this project.");
@@ -3087,7 +3099,7 @@ async function runInit(options) {
3087
3099
 
3088
3100
  // src/install/apply.ts
3089
3101
  import crypto2 from "crypto";
3090
- import path23 from "path";
3102
+ import path22 from "path";
3091
3103
  import fs15 from "fs-extra";
3092
3104
 
3093
3105
  // src/install/allow-rules.ts
@@ -3135,13 +3147,13 @@ ${content2}`;
3135
3147
 
3136
3148
  // src/install/manifest.ts
3137
3149
  import os4 from "os";
3138
- import path21 from "path";
3150
+ import path20 from "path";
3139
3151
  var MANIFEST_SCHEMA = "haus-install-manifest/1";
3140
3152
  function globalClaudeDir() {
3141
- return path21.join(os4.homedir(), ".claude");
3153
+ return path20.join(os4.homedir(), ".claude");
3142
3154
  }
3143
3155
  function hausManifestPath() {
3144
- return path21.join(globalClaudeDir(), "haus", "install-manifest.json");
3156
+ return path20.join(globalClaudeDir(), "haus", "install-manifest.json");
3145
3157
  }
3146
3158
  async function readManifest() {
3147
3159
  return readJson(hausManifestPath());
@@ -3160,10 +3172,10 @@ function buildManifest(source, files, hooks) {
3160
3172
  }
3161
3173
 
3162
3174
  // src/install/settings-merge.ts
3163
- import path22 from "path";
3175
+ import path21 from "path";
3164
3176
  import fs14 from "fs-extra";
3165
3177
  function settingsJsonPath() {
3166
- return path22.join(globalClaudeDir(), "settings.json");
3178
+ return path21.join(globalClaudeDir(), "settings.json");
3167
3179
  }
3168
3180
  async function readSettings() {
3169
3181
  const parsed = await readJson(settingsJsonPath());
@@ -3319,7 +3331,7 @@ function hashContent(content2) {
3319
3331
  }
3320
3332
  function sourceVersion() {
3321
3333
  try {
3322
- const pkgPath = path23.join(packageRoot(), "package.json");
3334
+ const pkgPath = path22.join(packageRoot(), "package.json");
3323
3335
  const pkg = JSON.parse(fs15.readFileSync(pkgPath, "utf8"));
3324
3336
  return `${pkg.name ?? "haus"}@${pkg.version ?? "0.0.0"}`;
3325
3337
  } catch {
@@ -3327,32 +3339,32 @@ function sourceVersion() {
3327
3339
  }
3328
3340
  }
3329
3341
  function globalSrcDir() {
3330
- return path23.join(packageRoot(), "library", "global");
3342
+ return path22.join(packageRoot(), "library", "global");
3331
3343
  }
3332
3344
  function collectSourceFiles(srcDir, claudeDir) {
3333
3345
  const entries = [];
3334
- const skillsDir = path23.join(srcDir, "skills");
3346
+ const skillsDir = path22.join(srcDir, "skills");
3335
3347
  if (fs15.pathExistsSync(skillsDir)) {
3336
3348
  for (const skillName of fs15.readdirSync(skillsDir)) {
3337
- const skillFile = path23.join(skillsDir, skillName, "SKILL.md");
3349
+ const skillFile = path22.join(skillsDir, skillName, "SKILL.md");
3338
3350
  if (fs15.pathExistsSync(skillFile)) {
3339
3351
  entries.push({
3340
3352
  stableId: `skill.${skillName}`,
3341
- srcRelPath: path23.join("library", "global", "skills", skillName, "SKILL.md"),
3342
- destPath: path23.join(claudeDir, "skills", skillName, "SKILL.md")
3353
+ srcRelPath: path22.join("library", "global", "skills", skillName, "SKILL.md"),
3354
+ destPath: path22.join(claudeDir, "skills", skillName, "SKILL.md")
3343
3355
  });
3344
3356
  }
3345
3357
  }
3346
3358
  }
3347
- const commandsDir = path23.join(srcDir, "commands");
3359
+ const commandsDir = path22.join(srcDir, "commands");
3348
3360
  if (fs15.pathExistsSync(commandsDir)) {
3349
3361
  for (const fileName of fs15.readdirSync(commandsDir)) {
3350
3362
  if (!fileName.endsWith(".md")) continue;
3351
3363
  const commandName = fileName.slice(0, -".md".length);
3352
3364
  entries.push({
3353
3365
  stableId: `command.${commandName}`,
3354
- srcRelPath: path23.join("library", "global", "commands", fileName),
3355
- destPath: path23.join(claudeDir, "commands", fileName)
3366
+ srcRelPath: path22.join("library", "global", "commands", fileName),
3367
+ destPath: path22.join(claudeDir, "commands", fileName)
3356
3368
  });
3357
3369
  }
3358
3370
  }
@@ -3376,7 +3388,7 @@ async function applyInstall(options = {}) {
3376
3388
  };
3377
3389
  const manifestFiles = [];
3378
3390
  for (const entry of sourceFiles) {
3379
- const srcPath = path23.join(packageRoot(), entry.srcRelPath);
3391
+ const srcPath = path22.join(packageRoot(), entry.srcRelPath);
3380
3392
  const rawContent = await readText(srcPath);
3381
3393
  if (rawContent === void 0) {
3382
3394
  warn(`Source file not found: ${entry.srcRelPath}`);
@@ -3432,7 +3444,7 @@ async function applyInstall(options = {}) {
3432
3444
  schemaVersion: SCHEMA_VERSION3
3433
3445
  });
3434
3446
  }
3435
- const fragmentPath = path23.join(srcDir, "settings-fragments", "hooks.json");
3447
+ const fragmentPath = path22.join(srcDir, "settings-fragments", "hooks.json");
3436
3448
  const fragments = await loadHooksFragment(fragmentPath);
3437
3449
  const settings = await readSettings();
3438
3450
  const { settings: hookSettings, addedIds } = mergeHooks(settings, fragments);
@@ -3572,12 +3584,12 @@ async function runScan(options) {
3572
3584
  }
3573
3585
 
3574
3586
  // src/commands/undo.ts
3575
- import path24 from "path";
3587
+ import path23 from "path";
3576
3588
  import fs16 from "fs-extra";
3577
3589
  var CLAUDE_DIR = ".claude";
3578
3590
  async function runUndo(options) {
3579
3591
  const root = process.cwd();
3580
- const targets = [path24.join(root, CLAUDE_DIR), path24.join(root, HAUS_DIR)];
3592
+ const targets = [path23.join(root, CLAUDE_DIR), path23.join(root, HAUS_DIR)];
3581
3593
  const existing = targets.filter((p) => fs16.existsSync(p));
3582
3594
  if (existing.length === 0) {
3583
3595
  log("Nothing to remove: no .claude/ or .haus-workflow/ in this directory.");
@@ -3585,7 +3597,7 @@ async function runUndo(options) {
3585
3597
  }
3586
3598
  if (!options.yes) {
3587
3599
  const ok = await confirm(
3588
- `Remove ${existing.map((p) => path24.relative(root, p)).join(" and ")}? This cannot be undone.`
3600
+ `Remove ${existing.map((p) => path23.relative(root, p)).join(" and ")}? This cannot be undone.`
3589
3601
  );
3590
3602
  if (!ok) {
3591
3603
  log("Cancelled.");
@@ -3594,13 +3606,13 @@ async function runUndo(options) {
3594
3606
  }
3595
3607
  for (const p of existing) {
3596
3608
  await fs16.remove(p);
3597
- log(`Removed ${path24.relative(root, p)}`);
3609
+ log(`Removed ${path23.relative(root, p)}`);
3598
3610
  }
3599
3611
  }
3600
3612
 
3601
3613
  // src/install/uninstall.ts
3602
3614
  import crypto3 from "crypto";
3603
- import path25 from "path";
3615
+ import path24 from "path";
3604
3616
  import fs17 from "fs-extra";
3605
3617
  async function runUninstall(options = {}) {
3606
3618
  const { force = false } = options;
@@ -3630,14 +3642,14 @@ async function runUninstall(options = {}) {
3630
3642
  continue;
3631
3643
  }
3632
3644
  await fs17.remove(entry.destPath);
3633
- await pruneEmptyDir(path25.dirname(entry.destPath));
3645
+ await pruneEmptyDir(path24.dirname(entry.destPath));
3634
3646
  result.deleted.push(entry.destPath);
3635
3647
  }
3636
3648
  const settings = await readSettings();
3637
3649
  const stripped = stripHausHooks(stripHausAllow(stripHausDeny(settings)));
3638
3650
  await writeSettings(stripped);
3639
3651
  result.hooksStripped = true;
3640
- const hausDir = path25.join(globalClaudeDir(), "haus");
3652
+ const hausDir = path24.join(globalClaudeDir(), "haus");
3641
3653
  const manifestPath = hausManifestPath();
3642
3654
  if (fs17.pathExistsSync(manifestPath)) {
3643
3655
  await fs17.remove(manifestPath);
@@ -3682,7 +3694,7 @@ async function runUninstallCommand(options) {
3682
3694
  }
3683
3695
 
3684
3696
  // src/commands/update.ts
3685
- import path27 from "path";
3697
+ import path26 from "path";
3686
3698
 
3687
3699
  // src/update/diff-generated-files.ts
3688
3700
  function diffGeneratedFiles() {
@@ -3709,7 +3721,7 @@ function summarizeLockDiff(before, after) {
3709
3721
 
3710
3722
  // src/update/lockfile.ts
3711
3723
  import { mkdir, readFile as readFile3, copyFile } from "fs/promises";
3712
- import path26 from "path";
3724
+ import path25 from "path";
3713
3725
  async function checkLock(root) {
3714
3726
  const lock = await readJson(hausPath(root, "haus.lock.json")) ?? [];
3715
3727
  const hasValidVersions = lock.every(
@@ -3730,7 +3742,7 @@ async function applyLock(root) {
3730
3742
  try {
3731
3743
  const backupDir = hausPath(root, "backups");
3732
3744
  await mkdir(backupDir, { recursive: true });
3733
- await copyFile(lockPath, path26.join(backupDir, `haus.lock.${Date.now()}.json`));
3745
+ await copyFile(lockPath, path25.join(backupDir, `haus.lock.${Date.now()}.json`));
3734
3746
  } catch {
3735
3747
  }
3736
3748
  const enriched = await Promise.all(
@@ -3752,7 +3764,7 @@ function diffLock(before, after) {
3752
3764
  }
3753
3765
  async function hasLocalOverrides(root) {
3754
3766
  try {
3755
- await readFile3(path26.join(root, ".claude", "settings.json"), "utf8");
3767
+ await readFile3(path25.join(root, ".claude", "settings.json"), "utf8");
3756
3768
  return true;
3757
3769
  } catch {
3758
3770
  return false;
@@ -3764,7 +3776,7 @@ var NPM_PACKAGE_NAME2 = "@haus-tech/haus-workflow";
3764
3776
  async function runUpdate(options) {
3765
3777
  const root = process.cwd();
3766
3778
  if (options.check) {
3767
- const pkgJson2 = await readJson(path27.join(packageRoot(), "package.json"));
3779
+ const pkgJson2 = await readJson(path26.join(packageRoot(), "package.json"));
3768
3780
  const currentVersion2 = pkgJson2?.version ?? "0.0.0";
3769
3781
  const [status, npmVersion, latestCatalogTag] = await Promise.all([
3770
3782
  checkLock(root),
@@ -3791,7 +3803,7 @@ async function runUpdate(options) {
3791
3803
  if (!status.ok) process.exitCode = 1;
3792
3804
  return;
3793
3805
  }
3794
- const pkgJson = await readJson(path27.join(packageRoot(), "package.json"));
3806
+ const pkgJson = await readJson(path26.join(packageRoot(), "package.json"));
3795
3807
  const currentVersion = pkgJson?.version ?? "0.0.0";
3796
3808
  const npmStatus = await fetchNpmVersionStatus(currentVersion);
3797
3809
  if (npmStatus.updateAvailable && npmStatus.latest !== null) {
@@ -3822,7 +3834,7 @@ async function runUpdate(options) {
3822
3834
 
3823
3835
  // src/commands/validate-catalog.ts
3824
3836
  import fs18 from "fs";
3825
- import path28 from "path";
3837
+ import path27 from "path";
3826
3838
 
3827
3839
  // library/catalog/validation-rules.json
3828
3840
  var validation_rules_default = {
@@ -4071,11 +4083,11 @@ function auditShippedFiles(manifestDir, items) {
4071
4083
  const failures = [];
4072
4084
  for (const item of items) {
4073
4085
  if (!item.path) continue;
4074
- const absPath = path28.join(manifestDir, item.path);
4086
+ const absPath = path27.join(manifestDir, item.path);
4075
4087
  if (item.type === "skill") {
4076
- const skillMd = path28.join(absPath, "SKILL.md");
4088
+ const skillMd = path27.join(absPath, "SKILL.md");
4077
4089
  if (!fs18.existsSync(skillMd)) {
4078
- failures.push(`${item.id}: missing ${path28.relative(manifestDir, skillMd)}`);
4090
+ failures.push(`${item.id}: missing ${path27.relative(manifestDir, skillMd)}`);
4079
4091
  continue;
4080
4092
  }
4081
4093
  const text = fs18.readFileSync(skillMd, "utf8");
@@ -4109,11 +4121,11 @@ function auditMarkdownContent(manifestDir) {
4109
4121
  const failures = [];
4110
4122
  const dirs = ["skills", "agents"];
4111
4123
  for (const dir of dirs) {
4112
- const abs = path28.join(manifestDir, dir);
4124
+ const abs = path27.join(manifestDir, dir);
4113
4125
  if (!fs18.existsSync(abs)) continue;
4114
4126
  walkMd(abs, (file) => {
4115
4127
  const text = fs18.readFileSync(file, "utf8");
4116
- const rel = path28.relative(manifestDir, file);
4128
+ const rel = path27.relative(manifestDir, file);
4117
4129
  const lines = text.split(/\r?\n/);
4118
4130
  for (let i = 0; i < lines.length; i++) {
4119
4131
  const line2 = lines[i] ?? "";
@@ -4133,7 +4145,7 @@ function auditMarkdownContent(manifestDir) {
4133
4145
  }
4134
4146
  function walkMd(dir, fn) {
4135
4147
  for (const entry of fs18.readdirSync(dir, { withFileTypes: true })) {
4136
- const full = path28.join(dir, entry.name);
4148
+ const full = path27.join(dir, entry.name);
4137
4149
  if (entry.isDirectory()) walkMd(full, fn);
4138
4150
  else if (entry.name.endsWith(".md")) fn(full);
4139
4151
  }
@@ -4144,8 +4156,8 @@ async function runValidateCatalog(manifestPath) {
4144
4156
  process.exitCode = 1;
4145
4157
  return;
4146
4158
  }
4147
- const abs = path28.resolve(process.cwd(), manifestPath);
4148
- const manifestDir = path28.dirname(abs);
4159
+ const abs = path27.resolve(process.cwd(), manifestPath);
4160
+ const manifestDir = path27.dirname(abs);
4149
4161
  const data = await readJson(abs);
4150
4162
  if (!data?.items) {
4151
4163
  error(`Could not read catalog manifest at ${abs}`);
@@ -4174,7 +4186,7 @@ async function runValidateCatalog(manifestPath) {
4174
4186
  }
4175
4187
 
4176
4188
  // src/commands/workspace.ts
4177
- import path29 from "path";
4189
+ import path28 from "path";
4178
4190
  import YAML from "yaml";
4179
4191
  async function runWorkspace(action) {
4180
4192
  if (action === "init") {
@@ -4207,7 +4219,7 @@ relationships: []
4207
4219
  const summaries = [];
4208
4220
  const ownership = {};
4209
4221
  for (const repo of repos) {
4210
- const repoRoot = path29.resolve(process.cwd(), repo.path);
4222
+ const repoRoot = path28.resolve(process.cwd(), repo.path);
4211
4223
  const result = await scanProject(repoRoot, "fast");
4212
4224
  summaries.push({
4213
4225
  name: repo.name,
@@ -4243,7 +4255,7 @@ ${summaries.map(
4243
4255
  // src/cli.ts
4244
4256
  function cliVersion() {
4245
4257
  try {
4246
- const pkgPath = path30.join(packageRoot(), "package.json");
4258
+ const pkgPath = path29.join(packageRoot(), "package.json");
4247
4259
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
4248
4260
  return pkg.version ?? "0.0.0";
4249
4261
  } catch {
@@ -4253,7 +4265,7 @@ function cliVersion() {
4253
4265
  var program = new Command();
4254
4266
  function validateRuntimeNodeVersion() {
4255
4267
  try {
4256
- const pkgPath = path30.join(packageRoot(), "package.json");
4268
+ const pkgPath = path29.join(packageRoot(), "package.json");
4257
4269
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
4258
4270
  const requiredRange = pkg.engines?.node;
4259
4271
  if (requiredRange && !satisfiesVersion(process.version, requiredRange)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/haus-workflow",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Haus AI workflow CLI for Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {