@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 +6 -0
- package/dist/cli.js +165 -52
- package/library/catalog/manifest.json +14 -14
- package/package.json +3 -3
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
|
-
|
|
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"])
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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:
|
|
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
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
)
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
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(([
|
|
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 =
|
|
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(
|
|
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
|
|
4449
|
-
if (
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
4488
|
-
skip(item.id, "source-approval", "Source not approved", `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
|
-
|
|
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" || !
|
|
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.
|
|
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.
|
|
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/
|
|
1777
|
-
"pinnedRef": "
|
|
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.
|
|
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/
|
|
1810
|
-
"pinnedRef": "
|
|
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.
|
|
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/
|
|
1843
|
-
"pinnedRef": "
|
|
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.
|
|
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":
|
|
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/
|
|
2001
|
-
"pinnedRef": "
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|