@haus-tech/haus-workflow 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.30.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.29.0...v0.30.0) (2026-06-22)
4
+
5
+ ### Features
6
+
7
+ - auto recommend config setups ([#137](https://github.com/WeAreHausTech/haus-workflow/issues/137)) ([56e7941](https://github.com/WeAreHausTech/haus-workflow/commit/56e79413a6cfe00e35f5e48abd03baeeb377214f))
8
+ - **cloneandsetup:** reuse local env values, never commit secrets ([#130](https://github.com/WeAreHausTech/haus-workflow/issues/130)) ([cf74a19](https://github.com/WeAreHausTech/haus-workflow/commit/cf74a196d369fbc59798de97e76bbcd3120155f7))
9
+ - co-install suppression and recommender gates ([#134](https://github.com/WeAreHausTech/haus-workflow/issues/134)) ([4ca1f20](https://github.com/WeAreHausTech/haus-workflow/commit/4ca1f20a6b90645cd7007d195e6cc474558cc971))
10
+ - curated source tools exempt from non-tsx npx ban and update archetype dependencies ([#132](https://github.com/WeAreHausTech/haus-workflow/issues/132)) ([a63d0b2](https://github.com/WeAreHausTech/haus-workflow/commit/a63d0b224ee9d1fd7b686b9aa3979562228f2333))
11
+ - prune deprecated catalog items on apply/update ([#126](https://github.com/WeAreHausTech/haus-workflow/issues/126)) ([fdf6ecc](https://github.com/WeAreHausTech/haus-workflow/commit/fdf6ecc1d36f4eb090cdaa8d9b044381c5836c77))
12
+ - stripe server gate [#139](https://github.com/WeAreHausTech/haus-workflow/issues/139) ([fb2dc35](https://github.com/WeAreHausTech/haus-workflow/commit/fb2dc35b0d332cdd6f3c7dcbaebef94dce06899c))
13
+
3
14
  ## [0.29.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.28.1...v0.29.0) (2026-06-17)
4
15
 
5
16
  ### Features
package/README.md CHANGED
@@ -82,8 +82,10 @@ Content lives in [`haus-workflow-catalog`](https://github.com/WeAreHausTech/haus
82
82
  (version pinned in `library/catalog/manifest.json`). Fetched at runtime from the latest release tag (override with `HAUS_CATALOG_REF`; fallback `main` when no release tag resolves).
83
83
  Validation rules sync from catalog → `library/catalog/validation-rules.json` (ADR-0001).
84
84
 
85
- On `haus apply` / `haus update`, items **removed from the catalog** are pruned from the
86
- project when their on-disk copy still matches the lock hash; user-edited copies are kept.
85
+ On `haus apply` / `haus update`, items **removed from the catalog** or marked
86
+ **deprecated** are pruned from the project when their on-disk copy still matches the
87
+ lock hash; user-edited copies are kept. Approved items deselected via `--select` are
88
+ not pruned.
87
89
 
88
90
  ## Internal docs
89
91
 
package/dist/cli.js CHANGED
@@ -154,7 +154,7 @@ var validation_rules_default = {
154
154
  ],
155
155
  allowedNpxPattern: { source: "\\bnpx\\s+tsx\\b", flags: "i" },
156
156
  anyNpxPattern: { source: "\\bnpx\\s+\\S+", flags: "i" },
157
- npxTsxOnlyExemptTypes: ["agent"],
157
+ npxTsxOnlyExemptSources: ["curated"],
158
158
  httpUrlPattern: { source: "^http:\\/\\/", flags: "i" },
159
159
  placeholderPattern: { source: "\\bTODO\\b|\\bPLACEHOLDER\\b", flags: "i" },
160
160
  allowedStacks: [
@@ -274,7 +274,29 @@ var REQUIRED_SKILL_FRONTMATTER = validation_rules_default.requiredSkillFrontmatt
274
274
  var RISKY_INSTALL_PATTERNS = validation_rules_default.riskyInstallPatterns.map(toRegExp);
275
275
  var ALLOWED_NPX_PATTERN = toRegExp(validation_rules_default.allowedNpxPattern);
276
276
  var ANY_NPX_PATTERN = toRegExp(validation_rules_default.anyNpxPattern);
277
- var NPX_TSX_ONLY_EXEMPT_TYPES = validation_rules_default.npxTsxOnlyExemptTypes ?? [];
277
+ var NPX_TSX_ONLY_EXEMPT_SOURCES = validation_rules_default.npxTsxOnlyExemptSources ?? [];
278
+ function isNpxTsxOnlyExempt(_itemType, itemSource) {
279
+ return Boolean(itemSource && NPX_TSX_ONLY_EXEMPT_SOURCES.includes(itemSource));
280
+ }
281
+ function buildItemPathSourceMap(items) {
282
+ const map = /* @__PURE__ */ new Map();
283
+ for (const item of items) {
284
+ if (!item.path) continue;
285
+ map.set(String(item.path).replace(/\\/g, "/"), item.source);
286
+ }
287
+ return map;
288
+ }
289
+ function resolveMarkdownItemSource(relPath, pathSourceMap) {
290
+ const norm = relPath.replace(/\\/g, "/");
291
+ let cursor = norm;
292
+ while (true) {
293
+ if (pathSourceMap.has(cursor)) return pathSourceMap.get(cursor);
294
+ const slash = cursor.lastIndexOf("/");
295
+ if (slash === -1) break;
296
+ cursor = cursor.slice(0, slash);
297
+ }
298
+ return void 0;
299
+ }
278
300
  var HTTP_URL_PATTERN = toRegExp(validation_rules_default.httpUrlPattern);
279
301
  var PLACEHOLDER_PATTERN = toRegExp(validation_rules_default.placeholderPattern);
280
302
  var ALLOWED_STACKS = validation_rules_default.allowedStacks;
@@ -353,7 +375,7 @@ ${extractUseWhenSection(text)}`;
353
375
  // src/catalog/ingest-catalog.ts
354
376
  function validateCatalogItem(item, content2) {
355
377
  const label = item.id;
356
- const checkNonTsxNpx = !NPX_TSX_ONLY_EXEMPT_TYPES.includes(item.type);
378
+ const checkNonTsxNpx = !isNpxTsxOnlyExempt(item.type, item.source);
357
379
  const lines = content2.split(/\r?\n/);
358
380
  for (let i = 0; i < lines.length; i++) {
359
381
  const line2 = lines[i] ?? "";
@@ -803,7 +825,12 @@ async function syncDirectoryFromPrefix(item, catalogPrefix, destDir, base, opts)
803
825
  for (const file of fetched) {
804
826
  if (file.kind !== "text" || !isMarkdownPath(file.rel)) continue;
805
827
  const verdict = validateCatalogItem(
806
- { id: item.id, type: "skill", path: catalogPrefix },
828
+ {
829
+ id: item.id,
830
+ type: "skill",
831
+ path: catalogPrefix,
832
+ ..."source" in item && item.source ? { source: item.source } : {}
833
+ },
807
834
  file.body
808
835
  );
809
836
  if (!verdict.ok) {
@@ -2291,7 +2318,7 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
2291
2318
  `${curatedReviewStatusSkips} curated items skipped: reviewStatus is not approved \u2014 possible catalog field rename upstream`
2292
2319
  );
2293
2320
  }
2294
- await cleanupStaleCatalogItems(root, new Set(manifestById.keys()), dryRun);
2321
+ await cleanupStaleCatalogItems(root, manifestById, dryRun);
2295
2322
  if (dryRun) return [...new Set(files)];
2296
2323
  const installedItems = catalogItems.filter((r) => installedIds.has(r.id));
2297
2324
  const prevLock = await readJson(hausPath(root, "haus.lock.json"));
@@ -2328,11 +2355,15 @@ async function writeClaudeFiles(root, dryRun, selectedIds, opts = {}) {
2328
2355
  await writeManagedJson(root, hausPath(root, "haus.lock.json"), lock, false);
2329
2356
  return [...new Set(files)];
2330
2357
  }
2331
- async function cleanupStaleCatalogItems(root, knownIds, dryRun) {
2358
+ async function cleanupStaleCatalogItems(root, manifestById, dryRun) {
2332
2359
  const prevLock = await readJson(hausPath(root, "haus.lock.json"));
2333
2360
  if (!prevLock?.length) return;
2334
2361
  for (const entry of prevLock) {
2335
- if (!entry.id || knownIds.has(entry.id)) continue;
2362
+ if (!entry.id) continue;
2363
+ const manifestItem = manifestById.get(entry.id);
2364
+ const removedFromManifest = manifestItem === void 0;
2365
+ const deprecated = manifestItem?.reviewStatus === "deprecated";
2366
+ if (!removedFromManifest && !deprecated) continue;
2336
2367
  const relPaths = entry.paths ?? [];
2337
2368
  if (relPaths.length === 0) continue;
2338
2369
  const existing = [];
@@ -2340,28 +2371,29 @@ async function cleanupStaleCatalogItems(root, knownIds, dryRun) {
2340
2371
  if (await fs12.pathExists(path12.join(root, rel))) existing.push(rel);
2341
2372
  }
2342
2373
  if (existing.length === 0) continue;
2374
+ const pruneReason = deprecated ? "deprecated" : "stale";
2343
2375
  if (entry.hash === void 0) {
2344
2376
  warn(
2345
- `Stale catalog item ${entry.id} has no lock hash \u2014 leaving in place: ${existing.join(", ")}`
2377
+ `${deprecated ? "Deprecated" : "Stale"} catalog item ${entry.id} has no lock hash \u2014 leaving in place: ${existing.join(", ")}`
2346
2378
  );
2347
2379
  continue;
2348
2380
  }
2349
2381
  const currentHash = await hashInstalledPaths(root, relPaths);
2350
2382
  if (currentHash !== entry.hash) {
2351
2383
  warn(
2352
- `Stale catalog item ${entry.id} was modified locally \u2014 leaving in place: ${existing.join(", ")}`
2384
+ `${deprecated ? "Deprecated" : "Stale"} catalog item ${entry.id} was modified locally \u2014 leaving in place: ${existing.join(", ")}`
2353
2385
  );
2354
2386
  continue;
2355
2387
  }
2356
2388
  for (const rel of existing) {
2357
2389
  const abs = path12.join(root, rel);
2358
2390
  if (dryRun) {
2359
- log(`[dry-run] would remove stale ${displayPath(root, abs)} (${entry.id})`);
2391
+ log(`[dry-run] would remove ${pruneReason} ${displayPath(root, abs)} (${entry.id})`);
2360
2392
  continue;
2361
2393
  }
2362
2394
  await fs12.remove(abs);
2363
2395
  await pruneEmptyDir(path12.dirname(abs));
2364
- log(`Removed stale ${displayPath(root, abs)} (${entry.id})`);
2396
+ log(`Removed ${pruneReason} ${displayPath(root, abs)} (${entry.id})`);
2365
2397
  }
2366
2398
  }
2367
2399
  }
@@ -2371,6 +2403,9 @@ async function cacheHasItems() {
2371
2403
  const data = await readJson(path13.join(getCacheDir(), "manifest.json"));
2372
2404
  return Array.isArray(data?.items) && data.items.length > 0;
2373
2405
  }
2406
+ function installableRecommendedItems(recommended) {
2407
+ return recommended.filter((item) => item.install !== false);
2408
+ }
2374
2409
  async function runApply(options) {
2375
2410
  if (!options.dryRun && !options.write) {
2376
2411
  log("Use --dry-run or --write");
@@ -2389,28 +2424,31 @@ async function runApply(options) {
2389
2424
  if (!rec) {
2390
2425
  log("No recommendation.json found \u2014 run `haus recommend` first. Writing core files only.");
2391
2426
  selectedIds = [];
2392
- } else if (rec.recommended.length === 0) {
2393
- log("Recommendation contains no catalog items. Writing core files only.");
2394
- selectedIds = [];
2395
2427
  } else {
2396
- const items = rec.recommended;
2397
- const choices = items.map((item) => ({
2398
- name: `${item.id} [${item.selectionMode}] \u2014 ${item.reason}`,
2399
- value: item.id,
2400
- checked: true
2401
- }));
2402
- const chosen = await checkbox({
2403
- message: "Select catalog items to apply (space to toggle, enter to confirm):",
2404
- choices,
2405
- pageSize: Math.min(20, items.length + 2)
2406
- });
2407
- selectedIds = chosen;
2408
- log(`Selected ${selectedIds.length} of ${items.length} catalog items.`);
2428
+ const items = installableRecommendedItems(rec.recommended);
2429
+ if (items.length === 0) {
2430
+ log("Recommendation contains no catalog items. Writing core files only.");
2431
+ selectedIds = [];
2432
+ } else {
2433
+ const choices = items.map((item) => ({
2434
+ name: `${item.id} [${item.selectionMode}] \u2014 ${item.reason}`,
2435
+ value: item.id,
2436
+ checked: true
2437
+ }));
2438
+ const chosen = await checkbox({
2439
+ message: "Select catalog items to apply (space to toggle, enter to confirm):",
2440
+ choices,
2441
+ pageSize: Math.min(20, items.length + 2)
2442
+ });
2443
+ selectedIds = chosen;
2444
+ log(`Selected ${selectedIds.length} of ${items.length} catalog items.`);
2445
+ }
2409
2446
  }
2410
2447
  }
2411
2448
  if (!options.allowEmptyCache && !process.env["HAUS_FIXTURE_CATALOG"]) {
2412
2449
  const rec = await readJson(hausPath(root, "recommendation.json"));
2413
- const catalogItemCount = selectedIds !== void 0 ? selectedIds.length : rec?.recommended.length ?? 0;
2450
+ const installableRecommended = installableRecommendedItems(rec?.recommended ?? []);
2451
+ const catalogItemCount = selectedIds !== void 0 ? selectedIds.length : installableRecommended.length;
2414
2452
  if (catalogItemCount > 0 && !await cacheHasItems()) {
2415
2453
  const message = "No catalog content found. Run `haus update` first.";
2416
2454
  if (isDryRun) {
@@ -2705,7 +2743,7 @@ var STACK_RULES = [
2705
2743
  { stack: ["tooling", "missing-eslint"], any: [depAbsent("eslint")] },
2706
2744
  {
2707
2745
  stack: ["tooling", "stripe"],
2708
- any: [dep("@stripe/stripe-js"), dep("@stripe/react-stripe-js")]
2746
+ any: [dep("stripe"), dep("@stripe/stripe-js"), dep("@stripe/react-stripe-js")]
2709
2747
  },
2710
2748
  { stack: ["tooling", "qliro"], any: [dep("@haus-tech/qliro-plugin")] },
2711
2749
  {
@@ -3543,15 +3581,6 @@ async function recommend(root, context) {
3543
3581
  skip(item.id, "unsupported-policy", "Unsupported stack policy");
3544
3582
  continue;
3545
3583
  }
3546
- if (item.type === "config") {
3547
- skip(
3548
- item.id,
3549
- "config-scaffold-only",
3550
- "Config item \u2014 install with `haus scaffold`",
3551
- "type:config"
3552
- );
3553
- continue;
3554
- }
3555
3584
  if (item.reviewStatus === "deprecated") {
3556
3585
  skip(item.id, "deprecated", "Catalog item is deprecated", "reviewStatus:deprecated");
3557
3586
  continue;
@@ -3608,6 +3637,19 @@ async function recommend(root, context) {
3608
3637
  );
3609
3638
  continue;
3610
3639
  }
3640
+ if (item.id === "haus.ecc-database-reviewer" && !roleSet.has("database")) {
3641
+ skip(item.id, "required-role-missing", "Required role missing: database", "role:database");
3642
+ continue;
3643
+ }
3644
+ 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"))) {
3645
+ skip(
3646
+ item.id,
3647
+ "co-install-react-reviewer",
3648
+ "Skip typescript-reviewer on React/Next stacks (use react-reviewer)",
3649
+ "stack:react|nextjs|role:react-app|next-app"
3650
+ );
3651
+ continue;
3652
+ }
3611
3653
  const isDefaultBaseline = item.default === true;
3612
3654
  const reasons = [];
3613
3655
  const push = (code, message, signal) => reasons.push({ code, message, signal });
@@ -3652,13 +3694,22 @@ async function recommend(root, context) {
3652
3694
  }
3653
3695
  const hasEvidence = reasons.some((r) => r.code !== "default-baseline");
3654
3696
  if (isDefaultBaseline || hasEvidence) {
3697
+ const isConfig = item.type === "config";
3698
+ const entryReasons = isConfig ? [
3699
+ ...reasons,
3700
+ {
3701
+ code: "config-scaffold",
3702
+ message: "Install with `haus scaffold`",
3703
+ signal: "type:config"
3704
+ }
3705
+ ] : reasons;
3655
3706
  recommended.push({
3656
3707
  id: item.id,
3657
3708
  type: item.type,
3658
- reason: reasons.length ? reasons.map((x) => x.message).join(", ") : "eligible",
3659
- reasons,
3709
+ reason: entryReasons.length ? entryReasons.map((x) => x.message).join(", ") : "eligible",
3710
+ reasons: entryReasons,
3660
3711
  selectionMode: isDefaultBaseline && !hasEvidence ? "baseline" : "matched",
3661
- install: true,
3712
+ install: !isConfig,
3662
3713
  tags: item.tags,
3663
3714
  ecosystem: item.ecosystem,
3664
3715
  tokenEstimate: item.tokenEstimate
@@ -3667,9 +3718,11 @@ async function recommend(root, context) {
3667
3718
  skip(item.id, "no-role-stack-match", "No role/stack match");
3668
3719
  }
3669
3720
  }
3721
+ applyCoInstallSuppression(recommended, skipped);
3670
3722
  recommended.sort((a, b) => a.id.localeCompare(b.id));
3671
3723
  skipped.sort((a, b) => a.id.localeCompare(b.id));
3672
- const selectedRules = recommended.length;
3724
+ const installableRecommended = recommended.filter((item) => item.install);
3725
+ const selectedRules = installableRecommended.length;
3673
3726
  const skippedRules = skipped.length;
3674
3727
  const estimatedContextTokens = estimateContextTokens(selectedRules);
3675
3728
  const estimatedTokenReductionPct = tokenReductionPct(selectedRules, skippedRules);
@@ -3690,6 +3743,78 @@ function buildStackSet(context) {
3690
3743
  )
3691
3744
  );
3692
3745
  }
3746
+ function applyCoInstallSuppression(recommended, skipped) {
3747
+ const recommendedIds = new Set(recommended.map((r) => r.id));
3748
+ const rules = [
3749
+ {
3750
+ suppress: "haus.sentry-sentry-workflow",
3751
+ whenAnyRecommended: [
3752
+ "haus.sentry-sentry-php-sdk",
3753
+ "haus.sentry-sentry-nextjs-sdk",
3754
+ "haus.sentry-sentry-node-sdk",
3755
+ "haus.sentry-sentry-nestjs-sdk"
3756
+ ],
3757
+ code: "co-install-sentry-sdk",
3758
+ message: "Skip sentry-workflow when a stack-specific Sentry SDK skill is selected",
3759
+ signal: "co-install:sentry-sdk"
3760
+ },
3761
+ {
3762
+ suppress: "haus.ecc-e2e-runner",
3763
+ whenAnyRecommended: ["haus.ecc-e2e-testing"],
3764
+ code: "co-install-e2e-skill",
3765
+ message: "Skip e2e-runner when e2e-testing skill covers patterns",
3766
+ signal: "co-install:e2e-testing"
3767
+ },
3768
+ {
3769
+ suppress: "haus.oh-my-claudecode-test-engineer",
3770
+ whenAnyRecommended: ["haus.ecc-e2e-testing", "haus.ecc-e2e-runner"],
3771
+ code: "co-install-e2e-coverage",
3772
+ message: "Skip oh-my test-engineer when ECC e2e skill or agent is selected",
3773
+ signal: "co-install:e2e-coverage"
3774
+ },
3775
+ {
3776
+ suppress: "haus.wshobson-js-testing-patterns",
3777
+ whenAnyRecommended: ["haus.ecc-react-testing"],
3778
+ code: "co-install-react-testing",
3779
+ message: "Skip js-testing-patterns when ecc-react-testing covers component tests",
3780
+ signal: "co-install:react-testing"
3781
+ },
3782
+ {
3783
+ suppress: "haus.superpowers-test-driven-development",
3784
+ whenAnyRecommended: ["haus.wshobson-js-testing-patterns", "haus.ecc-react-testing"],
3785
+ code: "co-install-stack-testing",
3786
+ message: "Skip TDD superpower when a stack testing skill is selected",
3787
+ signal: "co-install:stack-testing"
3788
+ },
3789
+ {
3790
+ suppress: "haus.superpowers-specifying-gates",
3791
+ whenAnyRecommended: ["haus.superpowers-checking-gates"],
3792
+ code: "co-install-gate-workflow",
3793
+ message: "Skip specifying-gates when checking-gates is selected",
3794
+ signal: "co-install:checking-gates"
3795
+ },
3796
+ {
3797
+ suppress: "haus.ecc-redis-patterns",
3798
+ whenAnyRecommended: ["haus.redis-redis-connections"],
3799
+ code: "co-install-redis-official",
3800
+ message: "Skip ecc-redis-patterns when official redis-connections skill is selected",
3801
+ signal: "co-install:redis-connections"
3802
+ }
3803
+ ];
3804
+ for (const rule of rules) {
3805
+ if (!recommendedIds.has(rule.suppress)) continue;
3806
+ if (!rule.whenAnyRecommended.some((id) => recommendedIds.has(id))) continue;
3807
+ const idx = recommended.findIndex((r) => r.id === rule.suppress);
3808
+ if (idx === -1) continue;
3809
+ const [removed] = recommended.splice(idx, 1);
3810
+ recommendedIds.delete(rule.suppress);
3811
+ skipped.push({
3812
+ id: removed.id,
3813
+ reason: rule.message,
3814
+ skipReasons: [{ code: rule.code, message: rule.message, signal: rule.signal }]
3815
+ });
3816
+ }
3817
+ }
3693
3818
 
3694
3819
  // src/commands/setup-core.ts
3695
3820
  async function runSetupCore(root, opts) {
@@ -4903,15 +5028,19 @@ function walkMd(dir, fn) {
4903
5028
  else if (entry.name.endsWith(".md")) fn(full);
4904
5029
  }
4905
5030
  }
4906
- function auditMarkdownContent(manifestDir) {
5031
+ function auditMarkdownContent(manifestDir, items) {
4907
5032
  const failures = [];
5033
+ const pathSourceMap = buildItemPathSourceMap(items);
4908
5034
  for (const dir of ["skills", "agents", "templates", "commands"]) {
4909
5035
  const abs = path27.join(manifestDir, dir);
4910
5036
  if (!fs21.existsSync(abs)) continue;
4911
- const checkNonTsxNpx = !NPX_TSX_ONLY_EXEMPT_TYPES.includes(DIR_ITEM_TYPE[dir] ?? "");
5037
+ const dirType = DIR_ITEM_TYPE[dir] ?? "";
4912
5038
  walkMd(abs, (file) => {
4913
5039
  const text = fs21.readFileSync(file, "utf8");
4914
5040
  const rel = path27.relative(manifestDir, file);
5041
+ const norm = rel.replace(/\\/g, "/");
5042
+ const source = resolveMarkdownItemSource(norm, pathSourceMap);
5043
+ const checkNonTsxNpx = !isNpxTsxOnlyExempt(dirType, source);
4915
5044
  const lines = text.split(/\r?\n/);
4916
5045
  for (let i = 0; i < lines.length; i++) {
4917
5046
  const line2 = lines[i] ?? "";
@@ -4921,7 +5050,7 @@ function auditMarkdownContent(manifestDir) {
4921
5050
  failures.push(`${rel}:${i + 1}: disallowed npx (only npx tsx allowed)`);
4922
5051
  }
4923
5052
  }
4924
- if (!rel.includes("/references/")) failures.push(...auditForbiddenTagsInText(text, rel));
5053
+ if (!norm.includes("/references/")) failures.push(...auditForbiddenTagsInText(text, rel));
4925
5054
  });
4926
5055
  }
4927
5056
  return failures;
@@ -4931,7 +5060,7 @@ function validateCatalogData(manifestDir, manifestVersion, items) {
4931
5060
  ...auditManifestStructure(manifestVersion, items),
4932
5061
  ...auditForbiddenStacks(items),
4933
5062
  ...auditShippedFiles(manifestDir, items),
4934
- ...auditMarkdownContent(manifestDir),
5063
+ ...auditMarkdownContent(manifestDir, items),
4935
5064
  ...auditDisallowedTags(items)
4936
5065
  ];
4937
5066
  return { ok: failures.length === 0, failures, items };