@haus-tech/haus-workflow 0.32.0 → 0.32.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.32.1](https://github.com/WeAreHausTech/haus-workflow/compare/v0.32.0...v0.32.1) (2026-06-30)
4
+
5
+ ### Bug Fixes
6
+
7
+ - **security:** audit hardening — fail-closed, recommender gates, CI hardening ([#157](https://github.com/WeAreHausTech/haus-workflow/issues/157)) ([e545194](https://github.com/WeAreHausTech/haus-workflow/commit/e545194fd3d3eb553bc2955a7ca17574bd98be71))
8
+
3
9
  ## [0.32.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.31.1...v0.32.0) (2026-06-24)
4
10
 
5
11
  ### Features
package/dist/cli.js CHANGED
@@ -650,48 +650,103 @@ function parseManifest(json) {
650
650
  }
651
651
 
652
652
  // src/catalog/remote-catalog.ts
653
+ function isTestMode() {
654
+ return process.env["HAUS_TEST_MODE"] === "1" || process.env["NODE_ENV"] === "test";
655
+ }
653
656
  function getCacheDir() {
654
657
  return process.env["HAUS_CATALOG_CACHE_DIR_OVERRIDE"] ?? path3.join(os2.homedir(), CATALOG_CACHE_SUBDIR);
655
658
  }
656
659
  var cachedCatalogRef;
657
660
  var cachedBlobPaths;
661
+ function getBundledCatalogRef() {
662
+ try {
663
+ const manifestPath2 = path3.join(packageRoot(), "library/catalog/manifest.json");
664
+ const raw = fs2.readFileSync(manifestPath2, "utf8");
665
+ const data = JSON.parse(raw);
666
+ if (typeof data.version === "string" && data.version) {
667
+ return data.version.startsWith("v") ? data.version : `v${data.version}`;
668
+ }
669
+ } catch {
670
+ }
671
+ return void 0;
672
+ }
658
673
  function getResolvedCatalogRef() {
659
- return cachedCatalogRef ?? process.env["HAUS_CATALOG_REF"] ?? "main";
674
+ const resolved = cachedCatalogRef ?? process.env["HAUS_CATALOG_REF"] ?? getBundledCatalogRef();
675
+ if (!resolved) {
676
+ warn(
677
+ "Could not determine catalog ref from cache, env, or bundled snapshot \u2014 falling back to main (moving target)."
678
+ );
679
+ return "main";
680
+ }
681
+ return resolved;
660
682
  }
661
683
  function isCatalogRefResolved() {
662
684
  return cachedCatalogRef !== void 0 || process.env["HAUS_CATALOG_REF"] !== void 0;
663
685
  }
664
686
  async function resolveCatalogRef(opts) {
665
687
  const env = opts?.env ?? process.env;
666
- if (env["HAUS_CATALOG_REF"]) return env["HAUS_CATALOG_REF"];
688
+ if (env["HAUS_CATALOG_REF"]) {
689
+ if (env["HAUS_CATALOG_REF"] === "main") {
690
+ warn(
691
+ "HAUS_CATALOG_REF=main is set \u2014 fetching from the moving main branch. Pin to a release tag for reproducible installs."
692
+ );
693
+ }
694
+ return env["HAUS_CATALOG_REF"];
695
+ }
667
696
  const fetchLatest = opts?.fetchLatestTag ?? fetchLatestCatalogTag;
668
697
  const tag = await fetchLatest();
669
- return tag ?? "main";
698
+ if (tag !== null) return tag;
699
+ const fallback = opts?.fallbackRef;
700
+ if (fallback) {
701
+ warn(
702
+ `Tag resolution failed \u2014 using cached ref ${fallback}. To use latest, retry or set HAUS_CATALOG_REF explicitly.`
703
+ );
704
+ return fallback;
705
+ }
706
+ const bundled = getBundledCatalogRef();
707
+ if (bundled) {
708
+ warn(
709
+ `Tag resolution failed \u2014 using bundled snapshot ref ${bundled}. To use latest, retry or set HAUS_CATALOG_REF explicitly.`
710
+ );
711
+ return bundled;
712
+ }
713
+ warn(
714
+ "Tag resolution failed and no fallback ref is available. Set HAUS_CATALOG_REF explicitly to avoid fetching from main."
715
+ );
716
+ return "main";
670
717
  }
671
718
  async function remoteBase() {
672
- if (process.env["HAUS_CATALOG_REMOTE_BASE"]) {
719
+ if (isTestMode() && process.env["HAUS_CATALOG_REMOTE_BASE"]) {
673
720
  return process.env["HAUS_CATALOG_REMOTE_BASE"];
674
721
  }
675
722
  if (cachedCatalogRef === void 0) {
676
- cachedCatalogRef = await resolveCatalogRef();
723
+ cachedCatalogRef = await resolveCatalogRef({ fallbackRef: getBundledCatalogRef() });
677
724
  }
678
725
  return `${CATALOG_REPO_URL}/${cachedCatalogRef}`;
679
726
  }
680
727
  async function fetchText(url) {
681
728
  try {
682
729
  const res = await fetch(url, { signal: AbortSignal.timeout(1e4) });
683
- if (!res.ok) return null;
730
+ if (!res.ok) {
731
+ warn(`Catalog fetch HTTP ${res.status}: ${url}`);
732
+ return null;
733
+ }
684
734
  return await res.text();
685
- } catch {
735
+ } catch (e) {
736
+ warn(`Catalog fetch failed (${e instanceof Error ? e.constructor.name : String(e)}): ${url}`);
686
737
  return null;
687
738
  }
688
739
  }
689
740
  async function fetchBytes(url) {
690
741
  try {
691
742
  const res = await fetch(url, { signal: AbortSignal.timeout(1e4) });
692
- if (!res.ok) return null;
743
+ if (!res.ok) {
744
+ warn(`Catalog fetch HTTP ${res.status}: ${url}`);
745
+ return null;
746
+ }
693
747
  return Buffer.from(await res.arrayBuffer());
694
- } catch {
748
+ } catch (e) {
749
+ warn(`Catalog fetch failed (${e instanceof Error ? e.constructor.name : String(e)}): ${url}`);
695
750
  return null;
696
751
  }
697
752
  }
@@ -828,7 +883,7 @@ async function fetchGitHubRecursiveBlobPaths(ref) {
828
883
  }
829
884
  async function fetchCatalogBlobPaths(_base) {
830
885
  if (cachedBlobPaths) return cachedBlobPaths;
831
- if (process.env["HAUS_CATALOG_REMOTE_BASE"]) return null;
886
+ if (isTestMode() && process.env["HAUS_CATALOG_REMOTE_BASE"]) return null;
832
887
  const ref = getResolvedCatalogRef();
833
888
  const paths = await fetchGitHubRecursiveBlobPaths(ref);
834
889
  if (paths) cachedBlobPaths = paths;
@@ -838,7 +893,7 @@ async function listFilesUnderCatalogPrefix(prefix, base) {
838
893
  const normalized = prefix.replace(/\\/g, "/").replace(/\/+$/, "");
839
894
  const prefixSlash = `${normalized}/`;
840
895
  let relFiles;
841
- if (process.env["HAUS_CATALOG_REMOTE_BASE"]) {
896
+ if (isTestMode() && process.env["HAUS_CATALOG_REMOTE_BASE"]) {
842
897
  relFiles = await listMockPrefixFiles(base, normalized);
843
898
  } else {
844
899
  const blobs = await fetchCatalogBlobPaths(base);
@@ -985,7 +1040,7 @@ async function syncConfigItem(item, base) {
985
1040
  if (relFiles && relFiles.length > 0) {
986
1041
  try {
987
1042
  return await syncDirectoryFromPrefix(item, item.path, dest, base, {
988
- validateMarkdown: false,
1043
+ validateMarkdown: true,
989
1044
  requireSkillMd: false,
990
1045
  relFiles
991
1046
  });
@@ -1125,7 +1180,7 @@ function compareSemver(a, b) {
1125
1180
  return 0;
1126
1181
  }
1127
1182
  async function fetchLatestCatalogTag() {
1128
- if (process.env["HAUS_CATALOG_REMOTE_BASE"]) return null;
1183
+ if (isTestMode() && process.env["HAUS_CATALOG_REMOTE_BASE"]) return null;
1129
1184
  try {
1130
1185
  const res = await fetch(CATALOG_TAGS_API_URL, {
1131
1186
  signal: AbortSignal.timeout(5e3),
@@ -2290,15 +2345,32 @@ ${templateContent}`;
2290
2345
  return null;
2291
2346
  }
2292
2347
  const existingContent = existing.slice(firstLine.length + 1);
2293
- if (!parsed.hash && !force) {
2294
- warn(
2295
- `${printable}: managed header missing hash \u2014 cannot verify tamper status, skipping. Use --force to overwrite.`
2296
- );
2297
- return null;
2298
- }
2299
- if (parsed.hash && hashText(normaliseLF(existingContent)) !== parsed.hash && !force) {
2300
- warn(`${printable}: content modified by user \u2014 skipping. Use --force to overwrite.`);
2301
- return null;
2348
+ const bodyMatchesTemplate = hashText(normaliseLF(existingContent)) === contentHash;
2349
+ if (parsed.hash) {
2350
+ const bodyIntact = hashText(normaliseLF(existingContent)) === parsed.hash;
2351
+ if (!bodyIntact && !force) {
2352
+ warn(`${printable}: content modified by user \u2014 skipping. Use --force to overwrite.`);
2353
+ return null;
2354
+ }
2355
+ } else {
2356
+ if (!bodyMatchesTemplate && !force) {
2357
+ const migratedHeader = makeWorkflowHeader(
2358
+ pkgVersion,
2359
+ hashText(normaliseLF(existingContent))
2360
+ );
2361
+ const migrated = `${migratedHeader}
2362
+ ${existingContent}`;
2363
+ if (hasTextChanged(existing, migrated)) {
2364
+ if (dryRun) {
2365
+ log(`${printable}: migrating legacy hash header (body preserved)`);
2366
+ } else {
2367
+ await writeText(destPath, migrated);
2368
+ }
2369
+ } else if (dryRun) {
2370
+ log(`${printable}: unchanged`);
2371
+ }
2372
+ return destPath;
2373
+ }
2302
2374
  }
2303
2375
  if (!hasTextChanged(existing, next)) {
2304
2376
  if (dryRun) log(`${printable}: unchanged`);
@@ -2491,11 +2563,50 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
2491
2563
  `${displayPath(root, destination)}: ${exists ? "would overwrite" : "would create"} (${item.id})`
2492
2564
  );
2493
2565
  } else if (item.type === "skill") {
2566
+ const skillFiles = (await fs14.readdir(sourcePath, { recursive: true })).filter(
2567
+ (f) => typeof f === "string" && f.endsWith(".md")
2568
+ );
2569
+ let skillValid = true;
2570
+ for (const relFile of skillFiles) {
2571
+ const mdPath = path14.join(sourcePath, relFile);
2572
+ const mdContent = await fs14.readFile(mdPath, "utf8");
2573
+ const mdValidation = validateCatalogItem(
2574
+ {
2575
+ id: manifestItem.id,
2576
+ type: "skill",
2577
+ path: manifestItem.path,
2578
+ source: manifestItem.source
2579
+ },
2580
+ mdContent
2581
+ );
2582
+ if (!mdValidation.ok) {
2583
+ warn(
2584
+ `Skipping ${item.id}: pre-copy validation failed (${relFile}) \u2014 ${mdValidation.reason}`
2585
+ );
2586
+ skillValid = false;
2587
+ break;
2588
+ }
2589
+ }
2590
+ if (!skillValid) continue;
2494
2591
  await installCatalogSkill(sourcePath, destination, {
2495
2592
  originSourceId: manifestItem.originSourceId,
2496
2593
  dryRun: false
2497
2594
  });
2498
2595
  } else {
2596
+ const fileContent = await fs14.readFile(sourcePath, "utf8");
2597
+ const validation = validateCatalogItem(
2598
+ {
2599
+ id: manifestItem.id,
2600
+ type: manifestItem.type,
2601
+ path: manifestItem.path,
2602
+ source: manifestItem.source
2603
+ },
2604
+ fileContent
2605
+ );
2606
+ if (!validation.ok) {
2607
+ warn(`Skipping ${item.id}: pre-copy validation failed \u2014 ${validation.reason}`);
2608
+ continue;
2609
+ }
2499
2610
  await fs14.ensureDir(path14.dirname(destination));
2500
2611
  await fs14.copy(sourcePath, destination, { overwrite: true, errorOnExist: false });
2501
2612
  }
@@ -3705,7 +3816,7 @@ function buildSourcesReport(items) {
3705
3816
  }
3706
3817
  return {
3707
3818
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3708
- items: [...statusBySource.entries()].map(([id, status]) => ({ id, status })).sort((a, b) => a.id.localeCompare(b.id))
3819
+ items: [...statusBySource.entries()].map(([source, status]) => ({ source, status })).sort((a, b) => a.source.localeCompare(b.source))
3709
3820
  };
3710
3821
  }
3711
3822
  async function writeSourcesReport(root) {
@@ -4417,9 +4528,7 @@ function mergeRecommendationWarnings(context) {
4417
4528
  // src/recommender/recommend.ts
4418
4529
  async function recommend(root, context, opts = {}) {
4419
4530
  const items = await loadCatalog(root);
4420
- const sources = await readJson(
4421
- hausPath(root, "sources-report.json")
4422
- ) ?? {};
4531
+ const sources = buildSourcesReport(items);
4423
4532
  const deep = await readJson(hausPath(root, "deep-context.json")) ?? {};
4424
4533
  const scannerStacks = buildStackSet(context);
4425
4534
  const scannerRoles = new Set(context.repoRoles.map((r) => r.toLowerCase()));
@@ -4437,7 +4546,7 @@ async function recommend(root, context, opts = {}) {
4437
4546
  const depSet = scannerDeps;
4438
4547
  const recommended = [];
4439
4548
  const skipped = [];
4440
- const sourceTrust = new Map((sources.items ?? []).map((x) => [x.id, x.status ?? "candidate"]));
4549
+ const sourceTrust = new Map(sources.items.map((x) => [x.source, x.status]));
4441
4550
  const changedFiles = await readChangedFiles(root);
4442
4551
  const skip = (id, code, message, signal) => {
4443
4552
  skipped.push({ id, reason: message, skipReasons: [{ code, message, signal }] });
@@ -4445,8 +4554,13 @@ async function recommend(root, context, opts = {}) {
4445
4554
  const roleSignal = (name) => scannerRoles.has(name.toLowerCase()) ? `role:${name}` : `deep:role:${name}`;
4446
4555
  const stackSignal = (name) => scannerStacks.has(name.toLowerCase()) ? `tag:${name}` : `deep:tag:${name}`;
4447
4556
  for (const item of items) {
4448
- const itemSearchText = `${item.id} ${item.tags.join(" ")}`.toLowerCase();
4449
- if (UNSUPPORTED.some((x) => itemSearchText.includes(x))) {
4557
+ const normSource = typeof item.source === "string" ? item.source.trim() : "";
4558
+ if (!normSource) {
4559
+ skip(item.id, "invalid-source", "Item source is missing or empty");
4560
+ continue;
4561
+ }
4562
+ const itemTags = item.tags.map((t) => t.toLowerCase());
4563
+ if (UNSUPPORTED.some((x) => itemTags.includes(x))) {
4450
4564
  skip(item.id, "unsupported-policy", "Unsupported stack policy");
4451
4565
  continue;
4452
4566
  }
@@ -4454,7 +4568,7 @@ async function recommend(root, context, opts = {}) {
4454
4568
  skip(item.id, "deprecated", "Catalog item is deprecated", "reviewStatus:deprecated");
4455
4569
  continue;
4456
4570
  }
4457
- if (item.source === "curated") {
4571
+ if (normSource === "curated") {
4458
4572
  const rs = item.reviewStatus;
4459
4573
  if (!rs || rs !== "approved") {
4460
4574
  skip(
@@ -4475,17 +4589,19 @@ async function recommend(root, context, opts = {}) {
4475
4589
  continue;
4476
4590
  }
4477
4591
  }
4478
- if (SENSITIVE_ITEM_KEYWORDS.some((x) => itemSearchText.includes(x))) {
4592
+ const sensitiveInId = SENSITIVE_ITEM_KEYWORDS.some((x) => item.id.toLowerCase().includes(x));
4593
+ const sensitiveInTags = SENSITIVE_ITEM_KEYWORDS.some((x) => itemTags.includes(x));
4594
+ if (sensitiveInId || sensitiveInTags) {
4479
4595
  skip(item.id, "sensitive-policy", "Sensitive content policy block");
4480
4596
  continue;
4481
4597
  }
4482
- const trust = sourceTrust.get(item.source);
4598
+ const trust = sourceTrust.get(normSource);
4483
4599
  if (trust === "candidate" || trust === "rejected") {
4484
4600
  skip(item.id, "source-trust", "Source trust policy block", `trust:${trust}`);
4485
4601
  continue;
4486
4602
  }
4487
- if (item.source && item.source !== "haus" && trust !== "approved") {
4488
- skip(item.id, "source-approval", "Source not approved", `source:${item.source}`);
4603
+ if (normSource !== "haus" && trust !== "approved") {
4604
+ skip(item.id, "source-approval", "Source not approved", `source:${normSource}`);
4489
4605
  continue;
4490
4606
  }
4491
4607
  if (item.id === "haus.nx21-monorepo-patterns" && !roleSet.has("nx-monorepo")) {
@@ -4510,15 +4626,6 @@ async function recommend(root, context, opts = {}) {
4510
4626
  skip(item.id, "required-role-missing", "Required role missing: database", "role:database");
4511
4627
  continue;
4512
4628
  }
4513
- if (item.id === "haus.ecc-typescript-reviewer" && (stackSet.has("react") || stackSet.has("react19") || stackSet.has("nextjs") || stackSet.has("next") || roleSet.has("react-app") || roleSet.has("next-app"))) {
4514
- skip(
4515
- item.id,
4516
- "co-install-react-reviewer",
4517
- "Skip typescript-reviewer on React/Next stacks (use react-reviewer)",
4518
- "stack:react|nextjs|role:react-app|next-app"
4519
- );
4520
- continue;
4521
- }
4522
4629
  const isDefaultBaseline = item.default === true;
4523
4630
  const reasons = [];
4524
4631
  const push = (code, message, signal) => reasons.push({ code, message, signal });
@@ -4536,9 +4643,11 @@ async function recommend(root, context, opts = {}) {
4536
4643
  `packageManager:${context.packageManager}`
4537
4644
  );
4538
4645
  }
4539
- const configSignal = item.tags.find(
4540
- (t) => context.warnings.join(" ").toLowerCase().includes(t.toLowerCase())
4541
- );
4646
+ const configSignal = item.tags.find((t) => {
4647
+ const escaped = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4648
+ const re = new RegExp(`(?:^|\\W)${escaped}(?:\\W|$)`, "i");
4649
+ return context.warnings.some((w) => re.test(w));
4650
+ });
4542
4651
  if (configSignal) push("config-signal-match", "config signal match", `warning:${configSignal}`);
4543
4652
  const idSegment = item.id.split(".").pop() ?? "";
4544
4653
  const changedMatch = idSegment ? changedFiles.find((f) => f.includes(idSegment)) : void 0;
@@ -4671,6 +4780,13 @@ function applyCoInstallSuppression(recommended, skipped) {
4671
4780
  code: "co-install-redis-official",
4672
4781
  message: "Skip ecc-redis-patterns when official redis-connections skill is selected",
4673
4782
  signal: "co-install:redis-connections"
4783
+ },
4784
+ {
4785
+ suppress: "haus.ecc-typescript-reviewer",
4786
+ whenAnyRecommended: ["haus.ecc-react-reviewer"],
4787
+ code: "co-install-react-reviewer",
4788
+ message: "Skip typescript-reviewer on React/Next stacks (use react-reviewer)",
4789
+ signal: "co-install:react-reviewer"
4674
4790
  }
4675
4791
  ];
4676
4792
  for (const rule of rules) {
@@ -4951,7 +5067,7 @@ ${content2}`;
4951
5067
  // src/install/apply.ts
4952
5068
  var SCHEMA_VERSION2 = "1";
4953
5069
  function hashContent(content2) {
4954
- return `sha256-${crypto2.createHash("sha256").update(content2).digest("hex")}`;
5070
+ return `sha256-${crypto2.createHash("sha256").update(normaliseLF(content2)).digest("hex")}`;
4955
5071
  }
4956
5072
  function sourceVersion() {
4957
5073
  try {
@@ -5838,7 +5954,6 @@ import path37 from "node:path";
5838
5954
  import fs29 from "node:fs";
5839
5955
  import path36 from "node:path";
5840
5956
  var ITEM_SEMVER_RE = /^\d+\.\d+\.\d+$/;
5841
- var MANIFEST_SEMVER_RE = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
5842
5957
  function isSafeCatalogPath2(itemPath) {
5843
5958
  if (!itemPath || path36.isAbsolute(itemPath) || itemPath.includes("\\")) return false;
5844
5959
  const normalized = path36.normalize(itemPath);
@@ -5892,10 +6007,8 @@ function auditManifestStructure(manifestVersion, items) {
5892
6007
  const failures = [];
5893
6008
  const seenIds = /* @__PURE__ */ new Map();
5894
6009
  const seenPaths = /* @__PURE__ */ new Map();
5895
- if (typeof manifestVersion !== "string" || !MANIFEST_SEMVER_RE.test(manifestVersion)) {
5896
- failures.push(
5897
- 'manifest.json: top-level "version" is not valid semver (expected X.Y.Z or X.Y.Z-pre)'
5898
- );
6010
+ if (typeof manifestVersion !== "string" || !ITEM_SEMVER_RE.test(manifestVersion)) {
6011
+ failures.push('manifest.json: top-level "version" is not valid semver (expected X.Y.Z)');
5899
6012
  }
5900
6013
  for (let i = 0; i < items.length; i++) {
5901
6014
  const item = items[i];
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.2.0",
2
+ "version": "3.2.1",
3
3
  "items": [
4
4
  {
5
5
  "id": "haus.agentic-workflow-standard",
@@ -1754,7 +1754,7 @@
1754
1754
  },
1755
1755
  {
1756
1756
  "id": "haus.sentry-sentry-nestjs-sdk",
1757
- "version": "1.0.0",
1757
+ "version": "1.0.1",
1758
1758
  "source": "curated",
1759
1759
  "type": "skill",
1760
1760
  "path": "skills/sentry/sentry-nestjs-sdk",
@@ -1773,8 +1773,8 @@
1773
1773
  "license": "MIT",
1774
1774
  "licenseConfidence": "high",
1775
1775
  "originSourceId": "getsentry-sentry-for-ai",
1776
- "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/9ab3551758913a4144517d4fafb71476a48c64f8/skills/sentry-nestjs-sdk/SKILL.md",
1777
- "pinnedRef": "9ab3551758913a4144517d4fafb71476a48c64f8",
1776
+ "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3/skills/sentry-nestjs-sdk/SKILL.md",
1777
+ "pinnedRef": "cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3",
1778
1778
  "ecosystem": "sentry",
1779
1779
  "requiresAny": [
1780
1780
  {
@@ -1787,7 +1787,7 @@
1787
1787
  },
1788
1788
  {
1789
1789
  "id": "haus.sentry-sentry-nextjs-sdk",
1790
- "version": "1.0.1",
1790
+ "version": "1.0.2",
1791
1791
  "source": "curated",
1792
1792
  "type": "skill",
1793
1793
  "path": "skills/sentry/sentry-nextjs-sdk",
@@ -1806,8 +1806,8 @@
1806
1806
  "license": "MIT",
1807
1807
  "licenseConfidence": "high",
1808
1808
  "originSourceId": "getsentry-sentry-for-ai",
1809
- "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/bd84576db74e3e1d13361c7eda3bb303ae017b90/skills/sentry-nextjs-sdk/SKILL.md",
1810
- "pinnedRef": "bd84576db74e3e1d13361c7eda3bb303ae017b90",
1809
+ "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3/skills/sentry-nextjs-sdk/SKILL.md",
1810
+ "pinnedRef": "cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3",
1811
1811
  "ecosystem": "sentry",
1812
1812
  "requiresAny": [
1813
1813
  {
@@ -1820,7 +1820,7 @@
1820
1820
  },
1821
1821
  {
1822
1822
  "id": "haus.sentry-sentry-node-sdk",
1823
- "version": "1.0.1",
1823
+ "version": "1.0.2",
1824
1824
  "source": "curated",
1825
1825
  "type": "skill",
1826
1826
  "path": "skills/sentry/sentry-node-sdk",
@@ -1839,8 +1839,8 @@
1839
1839
  "license": "MIT",
1840
1840
  "licenseConfidence": "high",
1841
1841
  "originSourceId": "getsentry-sentry-for-ai",
1842
- "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/bd84576db74e3e1d13361c7eda3bb303ae017b90/skills/sentry-node-sdk/SKILL.md",
1843
- "pinnedRef": "bd84576db74e3e1d13361c7eda3bb303ae017b90",
1842
+ "originUrl": "https://github.com/getsentry/sentry-for-ai/blob/cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3/skills/sentry-node-sdk/SKILL.md",
1843
+ "pinnedRef": "cc302403ebf647fd21c3ca736e1ec7ef6e4ed5a3",
1844
1844
  "ecosystem": "sentry",
1845
1845
  "requiresAny": [
1846
1846
  {
@@ -1978,7 +1978,7 @@
1978
1978
  },
1979
1979
  {
1980
1980
  "id": "haus.stripe-stripe-best-practices",
1981
- "version": "1.0.2",
1981
+ "version": "1.0.3",
1982
1982
  "source": "curated",
1983
1983
  "type": "skill",
1984
1984
  "path": "skills/stripe/stripe-best-practices",
@@ -1988,7 +1988,7 @@
1988
1988
  "whenNotToUse": "Do not use when a more specific skill or command applies.",
1989
1989
  "tags": ["workflow", "stripe", "payments"],
1990
1990
  "repoRoles": [],
1991
- "tokenEstimate": 7509,
1991
+ "tokenEstimate": 1018,
1992
1992
  "installMode": "copy-selected",
1993
1993
  "default": false,
1994
1994
  "reviewStatus": "approved",
@@ -1997,8 +1997,8 @@
1997
1997
  "license": "MIT",
1998
1998
  "licenseConfidence": "medium",
1999
1999
  "originSourceId": "stripe-ai",
2000
- "originUrl": "https://github.com/stripe/ai/blob/7e7f72eb42e06f79e7bc75f86e9395f4c8232c00/skills/stripe-best-practices/SKILL.md",
2001
- "pinnedRef": "7e7f72eb42e06f79e7bc75f86e9395f4c8232c00",
2000
+ "originUrl": "https://github.com/stripe/ai/blob/f54c9e6c0e8b6cfe4e648745eec68ecab3ddc994/skills/stripe-best-practices/SKILL.md",
2001
+ "pinnedRef": "f54c9e6c0e8b6cfe4e648745eec68ecab3ddc994",
2002
2002
  "ecosystem": "stripe",
2003
2003
  "requiresAny": [
2004
2004
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/haus-workflow",
3
- "version": "0.32.0",
3
+ "version": "0.32.1",
4
4
  "description": "Haus AI workflow CLI for Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,13 +47,13 @@
47
47
  "execa": "9.6.1",
48
48
  "fast-glob": "3.3.3",
49
49
  "fs-extra": "11.3.5",
50
- "semver": "7.8.0",
50
+ "semver": "7.8.5",
51
51
  "yaml": "2.9.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@release-it/conventional-changelog": "11.0.1",
55
55
  "@types/fs-extra": "11.0.4",
56
- "@types/node": "22.19.17",
56
+ "@types/node": "22.20.0",
57
57
  "@types/semver": "7.7.1",
58
58
  "@typescript-eslint/eslint-plugin": "8.59.3",
59
59
  "@typescript-eslint/parser": "8.59.3",