@soleur/opencode 0.0.0-stage → 1.0.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.
Files changed (812) hide show
  1. package/.claude-plugin/agents.manifest.json +476 -0
  2. package/.claude-plugin/plugin.json +42 -0
  3. package/README.md +44 -2
  4. package/agents/community/.gitkeep +0 -0
  5. package/agents/engineering/cto.md +87 -0
  6. package/agents/engineering/design/ddd-architect.md +95 -0
  7. package/agents/engineering/discovery/agent-finder.md +214 -0
  8. package/agents/engineering/discovery/functional-discovery.md +182 -0
  9. package/agents/engineering/infra/infra-security.md +151 -0
  10. package/agents/engineering/infra/platform-strategist.md +109 -0
  11. package/agents/engineering/infra/terraform-architect.md +171 -0
  12. package/agents/engineering/prompt-engineer.md +37 -0
  13. package/agents/engineering/research/best-practices-researcher.md +115 -0
  14. package/agents/engineering/research/framework-docs-researcher.md +93 -0
  15. package/agents/engineering/research/git-history-analyzer.md +53 -0
  16. package/agents/engineering/research/learnings-researcher.md +285 -0
  17. package/agents/engineering/research/repo-research-analyst.md +118 -0
  18. package/agents/engineering/review/agent-native-reviewer.md +279 -0
  19. package/agents/engineering/review/architecture-strategist.md +61 -0
  20. package/agents/engineering/review/code-quality-analyst.md +92 -0
  21. package/agents/engineering/review/code-simplicity-reviewer.md +109 -0
  22. package/agents/engineering/review/data-integrity-guardian.md +82 -0
  23. package/agents/engineering/review/data-migration-expert.md +98 -0
  24. package/agents/engineering/review/deployment-verification-agent.md +183 -0
  25. package/agents/engineering/review/dhh-rails-reviewer.md +46 -0
  26. package/agents/engineering/review/kieran-rails-reviewer.md +87 -0
  27. package/agents/engineering/review/legacy-code-expert.md +55 -0
  28. package/agents/engineering/review/observability-coverage-reviewer.md +147 -0
  29. package/agents/engineering/review/pattern-recognition-specialist.md +59 -0
  30. package/agents/engineering/review/performance-oracle.md +130 -0
  31. package/agents/engineering/review/security-sentinel.md +167 -0
  32. package/agents/engineering/review/semgrep-sast.md +36 -0
  33. package/agents/engineering/review/test-design-reviewer.md +150 -0
  34. package/agents/engineering/review/user-impact-reviewer.md +70 -0
  35. package/agents/engineering/workflow/pr-comment-resolver.md +69 -0
  36. package/agents/finance/budget-analyst.md +29 -0
  37. package/agents/finance/cfo.md +44 -0
  38. package/agents/finance/financial-reporter.md +30 -0
  39. package/agents/finance/revenue-analyst.md +30 -0
  40. package/agents/legal/clo.md +115 -0
  41. package/agents/legal/legal-compliance-auditor.md +110 -0
  42. package/agents/legal/legal-document-generator.md +134 -0
  43. package/agents/marketing/analytics-analyst.md +25 -0
  44. package/agents/marketing/brand-architect.md +181 -0
  45. package/agents/marketing/cmo.md +76 -0
  46. package/agents/marketing/conversion-optimizer.md +19 -0
  47. package/agents/marketing/copywriter.md +20 -0
  48. package/agents/marketing/fact-checker.md +70 -0
  49. package/agents/marketing/growth-strategist.md +145 -0
  50. package/agents/marketing/paid-media-strategist.md +28 -0
  51. package/agents/marketing/pricing-strategist.md +17 -0
  52. package/agents/marketing/programmatic-seo-specialist.md +25 -0
  53. package/agents/marketing/retention-strategist.md +23 -0
  54. package/agents/marketing/seo-aeo-analyst.md +164 -0
  55. package/agents/operations/coo.md +50 -0
  56. package/agents/operations/ops-advisor.md +78 -0
  57. package/agents/operations/ops-provisioner.md +101 -0
  58. package/agents/operations/ops-research.md +52 -0
  59. package/agents/operations/service-automator.md +278 -0
  60. package/agents/product/business-validator.md +206 -0
  61. package/agents/product/competitive-intelligence.md +89 -0
  62. package/agents/product/cpo.md +63 -0
  63. package/agents/product/design/ux-design-lead.md +209 -0
  64. package/agents/product/spec-flow-analyzer.md +122 -0
  65. package/agents/sales/cro.md +44 -0
  66. package/agents/sales/deal-architect.md +29 -0
  67. package/agents/sales/outbound-strategist.md +29 -0
  68. package/agents/sales/pipeline-analyst.md +29 -0
  69. package/agents/support/cco.md +48 -0
  70. package/agents/support/community-manager.md +570 -0
  71. package/agents/support/ticket-triage.md +70 -0
  72. package/bin/soleur-opencode.js +29 -0
  73. package/commands/go.md +659 -0
  74. package/commands/help.md +273 -0
  75. package/commands/sync.md +977 -0
  76. package/commands/version.md +103 -0
  77. package/hooks/antigravity-hooks.json +84 -0
  78. package/hooks/antigravity-session-start.sh +15 -0
  79. package/hooks/bare-root-cwd-guard.sh +131 -0
  80. package/hooks/browser-snapshot-credential-guard.sh +200 -0
  81. package/hooks/codex-session-start.sh +9 -0
  82. package/hooks/compaction-state.sh +525 -0
  83. package/hooks/destructive-command-guard.sh +1387 -0
  84. package/hooks/devin-session-start.sh +90 -0
  85. package/hooks/ext-filter-detect.sh +171 -0
  86. package/hooks/hooks.json +133 -0
  87. package/hooks/lib/hook-tool-kind.sh +24 -0
  88. package/hooks/lib/log-rotation.sh +176 -0
  89. package/hooks/lib/shell-argv.pl +576 -0
  90. package/hooks/memory-backstop.sh +1114 -0
  91. package/hooks/operator-stage-approval.sh +391 -0
  92. package/hooks/stop-hook.sh +367 -0
  93. package/hooks/unkept-promise-hook.sh +383 -0
  94. package/hooks/version-banner.sh +127 -0
  95. package/hooks/welcome-hook.sh +50 -0
  96. package/install.js +108 -0
  97. package/lib/agent-registry.ts +154 -0
  98. package/lib/c4-canonical-cli.mjs +34 -0
  99. package/lib/c4-canonical.mjs +46 -0
  100. package/lib/c4-from-components.ts +554 -0
  101. package/lib/go-routing.ts +81 -0
  102. package/lib/grok-inspect-contract.ts +311 -0
  103. package/lib/harness-model-map.ts +101 -0
  104. package/lib/harness-parity.ts +726 -0
  105. package/lib/harness-tool-map.ts +180 -0
  106. package/lib/harness.ts +716 -0
  107. package/lib/heartbeat-live-reconcile.ts +1153 -0
  108. package/lib/heartbeat-manifest.ts +342 -0
  109. package/lib/kb-coverage.ts +252 -0
  110. package/lib/pr-merge-poll.ts +103 -0
  111. package/lib/workflow-fidelity.ts +388 -0
  112. package/opencode/INSTRUCTIONS.md +152 -0
  113. package/opencode/agents/engineering/cto.md +10 -0
  114. package/opencode/agents/engineering/design/ddd-architect.md +10 -0
  115. package/opencode/agents/engineering/discovery/agent-finder.md +10 -0
  116. package/opencode/agents/engineering/discovery/functional-discovery.md +10 -0
  117. package/opencode/agents/engineering/infra/infra-security.md +10 -0
  118. package/opencode/agents/engineering/infra/platform-strategist.md +10 -0
  119. package/opencode/agents/engineering/infra/terraform-architect.md +10 -0
  120. package/opencode/agents/engineering/prompt-engineer.md +10 -0
  121. package/opencode/agents/engineering/research/best-practices-researcher.md +10 -0
  122. package/opencode/agents/engineering/research/framework-docs-researcher.md +10 -0
  123. package/opencode/agents/engineering/research/git-history-analyzer.md +10 -0
  124. package/opencode/agents/engineering/research/learnings-researcher.md +10 -0
  125. package/opencode/agents/engineering/research/repo-research-analyst.md +10 -0
  126. package/opencode/agents/engineering/review/agent-native-reviewer.md +10 -0
  127. package/opencode/agents/engineering/review/architecture-strategist.md +10 -0
  128. package/opencode/agents/engineering/review/code-quality-analyst.md +10 -0
  129. package/opencode/agents/engineering/review/code-simplicity-reviewer.md +10 -0
  130. package/opencode/agents/engineering/review/data-integrity-guardian.md +10 -0
  131. package/opencode/agents/engineering/review/data-migration-expert.md +10 -0
  132. package/opencode/agents/engineering/review/deployment-verification-agent.md +10 -0
  133. package/opencode/agents/engineering/review/dhh-rails-reviewer.md +10 -0
  134. package/opencode/agents/engineering/review/kieran-rails-reviewer.md +10 -0
  135. package/opencode/agents/engineering/review/legacy-code-expert.md +10 -0
  136. package/opencode/agents/engineering/review/observability-coverage-reviewer.md +10 -0
  137. package/opencode/agents/engineering/review/pattern-recognition-specialist.md +10 -0
  138. package/opencode/agents/engineering/review/performance-oracle.md +10 -0
  139. package/opencode/agents/engineering/review/security-sentinel.md +10 -0
  140. package/opencode/agents/engineering/review/semgrep-sast.md +10 -0
  141. package/opencode/agents/engineering/review/test-design-reviewer.md +10 -0
  142. package/opencode/agents/engineering/review/user-impact-reviewer.md +10 -0
  143. package/opencode/agents/engineering/workflow/pr-comment-resolver.md +10 -0
  144. package/opencode/agents/finance/budget-analyst.md +10 -0
  145. package/opencode/agents/finance/cfo.md +10 -0
  146. package/opencode/agents/finance/financial-reporter.md +10 -0
  147. package/opencode/agents/finance/revenue-analyst.md +10 -0
  148. package/opencode/agents/legal/clo.md +10 -0
  149. package/opencode/agents/legal/legal-compliance-auditor.md +10 -0
  150. package/opencode/agents/legal/legal-document-generator.md +10 -0
  151. package/opencode/agents/marketing/analytics-analyst.md +10 -0
  152. package/opencode/agents/marketing/brand-architect.md +10 -0
  153. package/opencode/agents/marketing/cmo.md +10 -0
  154. package/opencode/agents/marketing/conversion-optimizer.md +10 -0
  155. package/opencode/agents/marketing/copywriter.md +10 -0
  156. package/opencode/agents/marketing/fact-checker.md +10 -0
  157. package/opencode/agents/marketing/growth-strategist.md +10 -0
  158. package/opencode/agents/marketing/paid-media-strategist.md +10 -0
  159. package/opencode/agents/marketing/pricing-strategist.md +10 -0
  160. package/opencode/agents/marketing/programmatic-seo-specialist.md +10 -0
  161. package/opencode/agents/marketing/retention-strategist.md +10 -0
  162. package/opencode/agents/marketing/seo-aeo-analyst.md +10 -0
  163. package/opencode/agents/operations/coo.md +10 -0
  164. package/opencode/agents/operations/ops-advisor.md +10 -0
  165. package/opencode/agents/operations/ops-provisioner.md +10 -0
  166. package/opencode/agents/operations/ops-research.md +10 -0
  167. package/opencode/agents/operations/service-automator.md +10 -0
  168. package/opencode/agents/product/business-validator.md +10 -0
  169. package/opencode/agents/product/competitive-intelligence.md +10 -0
  170. package/opencode/agents/product/cpo.md +10 -0
  171. package/opencode/agents/product/design/ux-design-lead.md +10 -0
  172. package/opencode/agents/product/spec-flow-analyzer.md +10 -0
  173. package/opencode/agents/sales/cro.md +10 -0
  174. package/opencode/agents/sales/deal-architect.md +10 -0
  175. package/opencode/agents/sales/outbound-strategist.md +10 -0
  176. package/opencode/agents/sales/pipeline-analyst.md +10 -0
  177. package/opencode/agents/support/cco.md +10 -0
  178. package/opencode/agents/support/community-manager.md +10 -0
  179. package/opencode/agents/support/ticket-triage.md +10 -0
  180. package/opencode/commands/go.md +10 -0
  181. package/opencode/commands/help.md +10 -0
  182. package/opencode/commands/sync.md +10 -0
  183. package/opencode/commands/version.md +11 -0
  184. package/opencode/opencode.json +8 -0
  185. package/opencode/plugin.ts +493 -0
  186. package/package.json +36 -4
  187. package/scripts/admin-merge-ready.sh +545 -0
  188. package/scripts/admin-merge-ready.test.sh +753 -0
  189. package/scripts/affected-tests.sh +908 -0
  190. package/scripts/affected-tests.test.sh +1797 -0
  191. package/scripts/alpha-metrics.sh +78 -0
  192. package/scripts/alpha-metrics.test.sh +115 -0
  193. package/scripts/ancestor-signal-helpers.baseline.txt +16 -0
  194. package/scripts/audit-flag-flip.sh +107 -0
  195. package/scripts/check-red-on-main.sh +316 -0
  196. package/scripts/ci-head-verdict.sh +363 -0
  197. package/scripts/cloud-detect.sh +193 -0
  198. package/scripts/delegation-verify.sh +657 -0
  199. package/scripts/delegation-verify.test.sh +1424 -0
  200. package/scripts/deploy-arm.sh +475 -0
  201. package/scripts/domain-model-drift.sh +419 -0
  202. package/scripts/emit-decision.sh +144 -0
  203. package/scripts/emit-decision.test.sh +144 -0
  204. package/scripts/ext-filter-divergence-corpus.txt +15 -0
  205. package/scripts/ext-filter-divergence.sh +189 -0
  206. package/scripts/generate-c4-from-components.ts +442 -0
  207. package/scripts/grok-fidelity-gate.sh +68 -0
  208. package/scripts/grok-pre-push-gate.sh +232 -0
  209. package/scripts/harness-discovery-smoke.ts +906 -0
  210. package/scripts/harness-parity-census.ts +92 -0
  211. package/scripts/inngest-luks-alert-armed-probe.ts +29 -0
  212. package/scripts/install-opencode-adapter.ts +520 -0
  213. package/scripts/lib/affected-tests/common.sh +365 -0
  214. package/scripts/lib/affected-tests/go.sh +157 -0
  215. package/scripts/lib/affected-tests/js.sh +188 -0
  216. package/scripts/lib/affected-tests/nx-turbo.sh +98 -0
  217. package/scripts/lib/affected-tests/python.sh +185 -0
  218. package/scripts/lib/context-sanitize.sh +49 -0
  219. package/scripts/lib/domain-model-lib.sh +180 -0
  220. package/scripts/lib/ext-filter-grammar.tsv +34 -0
  221. package/scripts/lib/operator-script.sh +1236 -0
  222. package/scripts/lib/proc.sh +580 -0
  223. package/scripts/lib/session-state.sh +748 -0
  224. package/scripts/lib/tmp-classify.sh +843 -0
  225. package/scripts/lib/wrapper-boundary.sh +47 -0
  226. package/scripts/lint-ratchet.py +1547 -0
  227. package/scripts/monitor-pr-checks.sh +441 -0
  228. package/scripts/pipeline-tally.sh +756 -0
  229. package/scripts/pipeline-tally.test.sh +740 -0
  230. package/scripts/plugin-version.sh +528 -0
  231. package/scripts/precommit-guard.sh +113 -0
  232. package/scripts/precommit-guard.test.sh +254 -0
  233. package/scripts/reconcile-live-heartbeats.ts +705 -0
  234. package/scripts/render-c4-model.sh +217 -0
  235. package/scripts/resolve-git-root.sh +55 -0
  236. package/scripts/resolve-regenerable-conflicts.sh +363 -0
  237. package/scripts/resolve-regenerable-conflicts.test.sh +780 -0
  238. package/scripts/ruff-requirements.txt +22 -0
  239. package/scripts/run-in-pid-namespace.sh +98 -0
  240. package/scripts/run-in-pid-namespace.test.sh +290 -0
  241. package/scripts/scan-ancestor-signal-helpers.py +556 -0
  242. package/scripts/scan-ancestor-signal-helpers.test.sh +670 -0
  243. package/scripts/setup-ralph-loop.sh +238 -0
  244. package/scripts/sync-cursor-name-map.ts +316 -0
  245. package/scripts/sync-grok-agent-compat.ts +188 -0
  246. package/scripts/sync-npm-package.ts +178 -0
  247. package/scripts/sync-opencode-adapter.ts +479 -0
  248. package/scripts/sync-pr-behind.sh +534 -0
  249. package/scripts/sync-pr-behind.test.sh +252 -0
  250. package/scripts/taste-profile-update.sh +180 -0
  251. package/scripts/taste-profile-update.test.sh +167 -0
  252. package/scripts/write-budget-marker.sh +23 -0
  253. package/scripts/write-kb-coverage.ts +113 -0
  254. package/skills/admin-ip-refresh/SKILL.md +81 -0
  255. package/skills/admin-ip-refresh/references/admin-ip-refresh-procedure.md +201 -0
  256. package/skills/agent-browser/SKILL.md +698 -0
  257. package/skills/agent-browser/scripts/playwright-mcp-profile-slot.sh +304 -0
  258. package/skills/agent-browser/scripts/playwright-mcp-redact-proxy.py +964 -0
  259. package/skills/agent-browser/scripts/redact-a11y-snapshot.py +443 -0
  260. package/skills/agent-browser/test/fixtures/capture-playwright-mcp-fixtures.py +217 -0
  261. package/skills/agent-browser/test/fixtures/fake-passthrough-proxy.py +24 -0
  262. package/skills/agent-browser/test/fixtures/fake-playwright-mcp.py +131 -0
  263. package/skills/agent-browser/test/fixtures/make-proxy-mutant.py +48 -0
  264. package/skills/agent-browser/test/fixtures/odd-shapes/dialog-link-injection.json +10 -0
  265. package/skills/agent-browser/test/fixtures/odd-shapes/empty-content.json +5 -0
  266. package/skills/agent-browser/test/fixtures/odd-shapes/error-and-result.json +4 -0
  267. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-bool.json +6 -0
  268. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-string.json +6 -0
  269. package/skills/agent-browser/test/fixtures/odd-shapes/error-message-tree.json +6 -0
  270. package/skills/agent-browser/test/fixtures/odd-shapes/error-no-data.json +6 -0
  271. package/skills/agent-browser/test/fixtures/odd-shapes/error-string.json +4 -0
  272. package/skills/agent-browser/test/fixtures/odd-shapes/error-with-data.json +9 -0
  273. package/skills/agent-browser/test/fixtures/odd-shapes/evaluate-escaped-object.json +10 -0
  274. package/skills/agent-browser/test/fixtures/odd-shapes/invalid-utf8.json +4 -0
  275. package/skills/agent-browser/test/fixtures/odd-shapes/link-result.json +10 -0
  276. package/skills/agent-browser/test/fixtures/odd-shapes/list-two-results.json +6 -0
  277. package/skills/agent-browser/test/fixtures/odd-shapes/meta-in-result.json +13 -0
  278. package/skills/agent-browser/test/fixtures/odd-shapes/no-content.json +5 -0
  279. package/skills/agent-browser/test/fixtures/odd-shapes/quoted-key.json +10 -0
  280. package/skills/agent-browser/test/fixtures/odd-shapes/resource-block.json +18 -0
  281. package/skills/agent-browser/test/fixtures/odd-shapes/result-and-method.json +11 -0
  282. package/skills/agent-browser/test/fixtures/odd-shapes/result-string.json +3 -0
  283. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped-one-row.json +10 -0
  284. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped.json +10 -0
  285. package/skills/agent-browser/test/fixtures/odd-shapes/structured-content.json +13 -0
  286. package/skills/agent-browser/test/fixtures/odd-shapes/tools-list-no-tools.json +5 -0
  287. package/skills/agent-browser/test/fixtures/odd-shapes/tree-no-credential.json +10 -0
  288. package/skills/agent-browser/test/fixtures/odd-shapes/two-text-blocks.json +14 -0
  289. package/skills/agent-browser/test/fixtures/odd-shapes/unparsable-line.json +3 -0
  290. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-brace.json +10 -0
  291. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-link-substring.json +10 -0
  292. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/close.json +12 -0
  293. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/evaluate.json +12 -0
  294. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/find.json +12 -0
  295. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/framing.json +6 -0
  296. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/initialize.json +14 -0
  297. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate-link.json +12 -0
  298. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate.json +12 -0
  299. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/roots-list-request.json +5 -0
  300. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/screenshot.json +17 -0
  301. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-filename.json +12 -0
  302. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-json.json +12 -0
  303. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-raw.json +12 -0
  304. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot.json +12 -0
  305. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/tools-list.json +827 -0
  306. package/skills/agent-browser/test/fixtures/proxy-session-driver.py +138 -0
  307. package/skills/agent-browser/test/fixtures/session-facts.py +102 -0
  308. package/skills/agent-browser/test/playwright-mcp-lifetime.test.sh +1620 -0
  309. package/skills/agent-browser/test/playwright-mcp-redact-proxy.test.sh +1599 -0
  310. package/skills/agent-browser/test/redact-a11y-snapshot.test.sh +527 -0
  311. package/skills/agent-native-architecture/SKILL.md +479 -0
  312. package/skills/agent-native-architecture/references/action-parity-discipline.md +430 -0
  313. package/skills/agent-native-architecture/references/agent-execution-patterns.md +483 -0
  314. package/skills/agent-native-architecture/references/agent-native-testing.md +605 -0
  315. package/skills/agent-native-architecture/references/architecture-patterns.md +493 -0
  316. package/skills/agent-native-architecture/references/dynamic-context-injection.md +358 -0
  317. package/skills/agent-native-architecture/references/files-universal-interface.md +314 -0
  318. package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +381 -0
  319. package/skills/agent-native-architecture/references/mcp-tool-design.md +524 -0
  320. package/skills/agent-native-architecture/references/mobile-patterns.md +897 -0
  321. package/skills/agent-native-architecture/references/product-implications.md +478 -0
  322. package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +342 -0
  323. package/skills/agent-native-architecture/references/self-modification.md +294 -0
  324. package/skills/agent-native-architecture/references/shared-workspace-architecture.md +700 -0
  325. package/skills/agent-native-architecture/references/system-prompt-design.md +263 -0
  326. package/skills/agent-native-audit/SKILL.md +300 -0
  327. package/skills/agent-native-audit/workflows/agent-native-audit.workflow.js +423 -0
  328. package/skills/andrew-kane-gem-writer/SKILL.md +189 -0
  329. package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  330. package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  331. package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  332. package/skills/andrew-kane-gem-writer/references/resources.md +126 -0
  333. package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  334. package/skills/architecture/SKILL.md +436 -0
  335. package/skills/architecture/references/adr-template.md +154 -0
  336. package/skills/architecture/references/likec4-reference.md +136 -0
  337. package/skills/architecture/references/nfr-reference.md +126 -0
  338. package/skills/archive-kb/SKILL.md +86 -0
  339. package/skills/archive-kb/scripts/archive-kb.sh +300 -0
  340. package/skills/archive-kb/test/archive-kb-partial-run.test.sh +276 -0
  341. package/skills/atdd-developer/SKILL.md +65 -0
  342. package/skills/brainstorm/SKILL.md +701 -0
  343. package/skills/brainstorm/references/brainstorm-brand-workshop.md +153 -0
  344. package/skills/brainstorm/references/brainstorm-domain-config.md +63 -0
  345. package/skills/brainstorm/references/brainstorm-validation-workshop.md +65 -0
  346. package/skills/brainstorm/references/ui-surface-terms.md +42 -0
  347. package/skills/brainstorm-techniques/SKILL.md +231 -0
  348. package/skills/brainstorm-techniques/references/decision-principles.md +150 -0
  349. package/skills/brainstorm-techniques/references/phase-boundaries.md +103 -0
  350. package/skills/brainstorm-techniques/references/standing-delegation.md +407 -0
  351. package/skills/brainstorm-techniques/references/technical-ruling-delegation.md +174 -0
  352. package/skills/campaign-calendar/SKILL.md +145 -0
  353. package/skills/cf-token-scope/SKILL.md +164 -0
  354. package/skills/cf-token-scope/references/widen-playbook.md +114 -0
  355. package/skills/cf-token-scope/scripts/cf-token-scope.sh +253 -0
  356. package/skills/changelog/SKILL.md +142 -0
  357. package/skills/code-to-prd/SKILL.md +66 -0
  358. package/skills/code-to-prd/references/banner-template.md +12 -0
  359. package/skills/code-to-prd/references/prd-template.md +41 -0
  360. package/skills/code-to-prd/scripts/code-to-prd.sh +634 -0
  361. package/skills/code-to-prd/test/code-to-prd.test.sh +447 -0
  362. package/skills/code-to-prd/test/fixture/.env.example +7 -0
  363. package/skills/code-to-prd/test/fixture/app/about/page.tsx +11 -0
  364. package/skills/code-to-prd/test/fixture/app/api/health/route.ts +10 -0
  365. package/skills/code-to-prd/test/fixture/app/page.tsx +14 -0
  366. package/skills/code-to-prd/test/fixture/next.config.mjs +3 -0
  367. package/skills/code-to-prd/test/fixture/package.json +6 -0
  368. package/skills/cohort-status/SKILL.md +62 -0
  369. package/skills/community/SKILL.md +210 -0
  370. package/skills/community/scripts/bsky-community.sh +644 -0
  371. package/skills/community/scripts/bsky-setup.sh +631 -0
  372. package/skills/community/scripts/community-router.sh +93 -0
  373. package/skills/community/scripts/discord-community.sh +487 -0
  374. package/skills/community/scripts/discord-setup.sh +784 -0
  375. package/skills/community/scripts/github-community.sh +706 -0
  376. package/skills/community/scripts/hn-community.sh +233 -0
  377. package/skills/community/scripts/lib/hmac-sha1-b64.sh +126 -0
  378. package/skills/community/scripts/linkedin-community.sh +750 -0
  379. package/skills/community/scripts/linkedin-setup.sh +758 -0
  380. package/skills/community/scripts/x-community.sh +890 -0
  381. package/skills/community/scripts/x-setup.sh +713 -0
  382. package/skills/community/test/community-argv.test.sh +2922 -0
  383. package/skills/community/test/github-community.test.sh +981 -0
  384. package/skills/competitive-analysis/SKILL.md +69 -0
  385. package/skills/competitive-analysis/references/peer-plugin-audit.md +218 -0
  386. package/skills/compound/SKILL.md +673 -0
  387. package/skills/compound/scripts/token-efficiency-report.sh +334 -0
  388. package/skills/compound/test/phase-16.test.sh +379 -0
  389. package/skills/compound-capture/SKILL.md +791 -0
  390. package/skills/compound-capture/assets/critical-pattern-template.md +37 -0
  391. package/skills/compound-capture/assets/resolution-template.md +116 -0
  392. package/skills/compound-capture/references/yaml-schema.md +67 -0
  393. package/skills/compound-capture/schema.yaml +181 -0
  394. package/skills/constraint-scaffold/SKILL.md +175 -0
  395. package/skills/constraint-scaffold/references/boundary-readme.template +35 -0
  396. package/skills/constraint-scaffold/references/constraint-gates-workflow.template +69 -0
  397. package/skills/constraint-scaffold/references/depcruise-config.template +230 -0
  398. package/skills/constraint-scaffold/references/fix-constraints-stage-a.template +240 -0
  399. package/skills/constraint-scaffold/references/fix-constraints-stage-b.template +313 -0
  400. package/skills/constraint-scaffold/references/shared-runner.template +135 -0
  401. package/skills/constraint-scaffold/scripts/constraint-scaffold.sh +589 -0
  402. package/skills/constraint-scaffold/test/bite-proof.test.sh +1338 -0
  403. package/skills/constraint-scaffold/test/boundary.test.sh +734 -0
  404. package/skills/constraint-scaffold/test/emit-fix-constraints.test.sh +193 -0
  405. package/skills/constraint-scaffold/test/generator.test.sh +93 -0
  406. package/skills/constraint-scaffold/test/parity.test.sh +285 -0
  407. package/skills/content-writer/SKILL.md +242 -0
  408. package/skills/content-writer/scripts/blog-jargon-scan.sh +42 -0
  409. package/skills/cron-delete/SKILL.md +56 -0
  410. package/skills/cron-list/SKILL.md +54 -0
  411. package/skills/deepen-plan/SKILL.md +937 -0
  412. package/skills/deepen-plan/workflows/deepen-plan.workflow.js +488 -0
  413. package/skills/deploy/SKILL.md +115 -0
  414. package/skills/deploy/references/hetzner-setup.md +65 -0
  415. package/skills/deploy/scripts/deploy.sh +37 -0
  416. package/skills/deploy-docs/SKILL.md +95 -0
  417. package/skills/dhh-rails-style/SKILL.md +203 -0
  418. package/skills/dhh-rails-style/references/architecture.md +717 -0
  419. package/skills/dhh-rails-style/references/controllers.md +323 -0
  420. package/skills/dhh-rails-style/references/frontend.md +556 -0
  421. package/skills/dhh-rails-style/references/gems.md +316 -0
  422. package/skills/dhh-rails-style/references/models.md +388 -0
  423. package/skills/dhh-rails-style/references/testing.md +354 -0
  424. package/skills/discord-content/SKILL.md +126 -0
  425. package/skills/docs-site/SKILL.md +214 -0
  426. package/skills/drain-labeled-backlog/SKILL.md +203 -0
  427. package/skills/drain-labeled-backlog/scripts/group-by-area.sh +208 -0
  428. package/skills/drain-labeled-backlog/workflows/drain-labeled-backlog.workflow.js +433 -0
  429. package/skills/drain-prs/SKILL.md +142 -0
  430. package/skills/drain-prs/scripts/triage-prs.sh +192 -0
  431. package/skills/drain-prs/test/argv-ceiling.test.sh +183 -0
  432. package/skills/dspy-ruby/SKILL.md +634 -0
  433. package/skills/dspy-ruby/assets/config-template.rb +359 -0
  434. package/skills/dspy-ruby/assets/module-template.rb +326 -0
  435. package/skills/dspy-ruby/assets/signature-template.rb +143 -0
  436. package/skills/dspy-ruby/references/core-concepts.md +268 -0
  437. package/skills/dspy-ruby/references/optimization.md +623 -0
  438. package/skills/dspy-ruby/references/providers.md +335 -0
  439. package/skills/eval-harness/README.md +194 -0
  440. package/skills/eval-harness/SKILL.md +157 -0
  441. package/skills/eval-harness/enums/go-routes.json +13 -0
  442. package/skills/eval-harness/enums/incident-threshold.json +5 -0
  443. package/skills/eval-harness/enums/lane.json +5 -0
  444. package/skills/eval-harness/enums/tool-selection.json +20 -0
  445. package/skills/eval-harness/enums/triage-levels.json +5 -0
  446. package/skills/eval-harness/gated-skills.json +34 -0
  447. package/skills/eval-harness/models.generated.json +5 -0
  448. package/skills/eval-harness/promptfooconfig-go-routing.yaml +35 -0
  449. package/skills/eval-harness/promptfooconfig-incident-threshold.yaml +32 -0
  450. package/skills/eval-harness/promptfooconfig-lane-inference.yaml +33 -0
  451. package/skills/eval-harness/promptfooconfig-ticket-triage.yaml +34 -0
  452. package/skills/eval-harness/promptfooconfig-tool-selection.yaml +33 -0
  453. package/skills/eval-harness/prompts/go-baseline.txt +6 -0
  454. package/skills/eval-harness/prompts/go-skill.txt +21 -0
  455. package/skills/eval-harness/prompts/incident-threshold-baseline.txt +6 -0
  456. package/skills/eval-harness/prompts/incident-threshold-skill.txt +12 -0
  457. package/skills/eval-harness/prompts/lane-inference-baseline.txt +6 -0
  458. package/skills/eval-harness/prompts/lane-inference-skill.txt +15 -0
  459. package/skills/eval-harness/prompts/tool-selection-baseline.txt +19 -0
  460. package/skills/eval-harness/prompts/tool-selection-skill.txt +22 -0
  461. package/skills/eval-harness/prompts/triage-baseline.txt +7 -0
  462. package/skills/eval-harness/prompts/triage-skill.txt +10 -0
  463. package/skills/eval-harness/scripts/eval-gate.cjs +318 -0
  464. package/skills/eval-harness/scripts/extract-block.cjs +87 -0
  465. package/skills/eval-harness/scripts/gate-classification.cjs +28 -0
  466. package/skills/eval-harness/scripts/gen-models.sh +41 -0
  467. package/skills/eval-harness/scripts/gen-skill-prompt.cjs +177 -0
  468. package/skills/eval-harness/scripts/measure-classification.cjs +30 -0
  469. package/skills/eval-harness/scripts/parse-label.cjs +93 -0
  470. package/skills/eval-harness/scripts/verdict.cjs +132 -0
  471. package/skills/eval-harness/tasks/go-routing.jsonl +8 -0
  472. package/skills/eval-harness/tasks/incident-threshold.jsonl +7 -0
  473. package/skills/eval-harness/tasks/lane-inference.jsonl +7 -0
  474. package/skills/eval-harness/tasks/ticket-triage.jsonl +6 -0
  475. package/skills/eval-harness/tasks/tool-selection.jsonl +15 -0
  476. package/skills/eval-harness/test/eval-gate.test.sh +132 -0
  477. package/skills/eval-harness/test/extract-block.test.sh +87 -0
  478. package/skills/eval-harness/test/gate-classification.test.sh +77 -0
  479. package/skills/eval-harness/test/gen-models.test.sh +75 -0
  480. package/skills/eval-harness/test/measure-classification.test.sh +74 -0
  481. package/skills/eval-harness/test/parse-label.test.sh +129 -0
  482. package/skills/eval-harness/test/registry-completeness.test.sh +109 -0
  483. package/skills/eval-harness/test/verdict.test.sh +123 -0
  484. package/skills/every-style-editor/SKILL.md +142 -0
  485. package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  486. package/skills/feature-tweet/SKILL.md +199 -0
  487. package/skills/feature-video/SKILL.md +412 -0
  488. package/skills/feature-video/scripts/check_deps.sh +281 -0
  489. package/skills/file-todos/SKILL.md +267 -0
  490. package/skills/file-todos/assets/todo-template.md +166 -0
  491. package/skills/fix-issue/SKILL.md +278 -0
  492. package/skills/fix-issue/references/agent-authored-exclusion.md +118 -0
  493. package/skills/fix-issue/references/exclude-label-jq-snippet.md +96 -0
  494. package/skills/flag-bootstrap/SETUP.md +142 -0
  495. package/skills/flag-create/SKILL.md +145 -0
  496. package/skills/flag-create/scripts/create.sh +273 -0
  497. package/skills/flag-delete/SKILL.md +167 -0
  498. package/skills/flag-delete/scripts/delete.sh +288 -0
  499. package/skills/flag-list/SKILL.md +99 -0
  500. package/skills/flag-list/scripts/list.sh +275 -0
  501. package/skills/flag-set-role/SKILL.md +229 -0
  502. package/skills/flag-set-role/scripts/flip.sh +774 -0
  503. package/skills/frontend-anti-slop/SKILL.md +106 -0
  504. package/skills/frontend-anti-slop/references/anti-patterns.md +199 -0
  505. package/skills/frontend-anti-slop/references/slop-rules.md +65 -0
  506. package/skills/frontend-anti-slop/scripts/tier1-scan.ts +525 -0
  507. package/skills/frontend-design/SKILL.md +77 -0
  508. package/skills/gdpr-gate/NOTICE +136 -0
  509. package/skills/gdpr-gate/SKILL.md +341 -0
  510. package/skills/gdpr-gate/references/fields.md +160 -0
  511. package/skills/gdpr-gate/references/layers/api-layer.md +355 -0
  512. package/skills/gdpr-gate/references/layers/auth-sessions.md +290 -0
  513. package/skills/gdpr-gate/references/layers/data-in-transit.md +331 -0
  514. package/skills/gdpr-gate/references/layers/data-lifecycle.md +341 -0
  515. package/skills/gdpr-gate/references/layers/frontend.md +249 -0
  516. package/skills/gdpr-gate/references/layers/testing-seeding.md +256 -0
  517. package/skills/gdpr-gate/references/leakage-vectors.md +248 -0
  518. package/skills/gdpr-gate/references/legacy/legal-consent-v1-prose.md +48 -0
  519. package/skills/gdpr-gate/references/legal-consent.md +140 -0
  520. package/skills/gdpr-gate/references/non-negotiables.md +60 -0
  521. package/skills/gdpr-gate/scripts/gdpr-gate.sh +271 -0
  522. package/skills/gdpr-gate/scripts/notice-frontmatter.sh +337 -0
  523. package/skills/gdpr-gate/scripts/path-denylist.txt +39 -0
  524. package/skills/gdpr-gate/scripts/repo-scan.sh +158 -0
  525. package/skills/gdpr-gate/scripts/vendor-drift-classify.sh +120 -0
  526. package/skills/gdpr-gate/scripts/vendor-pin-integrity.sh +237 -0
  527. package/skills/gemini-imagegen/SKILL.md +283 -0
  528. package/skills/gemini-imagegen/requirements.txt +5 -0
  529. package/skills/gemini-imagegen/scripts/_error_handling.py +146 -0
  530. package/skills/gemini-imagegen/scripts/compose_images.py +150 -0
  531. package/skills/gemini-imagegen/scripts/edit_image.py +137 -0
  532. package/skills/gemini-imagegen/scripts/gemini_images.py +256 -0
  533. package/skills/gemini-imagegen/scripts/generate_image.py +147 -0
  534. package/skills/gemini-imagegen/scripts/multi_turn_chat.py +223 -0
  535. package/skills/gemini-imagegen/scripts/test_error_handling.py +179 -0
  536. package/skills/git-worktree/SKILL.md +534 -0
  537. package/skills/git-worktree/scripts/git-repo-readiness-diag.sh +98 -0
  538. package/skills/git-worktree/scripts/worktree-manager.sh +4773 -0
  539. package/skills/git-worktree/test/create-from-origin-main.test.sh +116 -0
  540. package/skills/git-worktree/test/lease-protects-active.test.sh +1056 -0
  541. package/skills/git-worktree/test/no-repo-fail-loud.test.sh +88 -0
  542. package/skills/git-worktree/test/orphan-reaper-honest-count.test.sh +379 -0
  543. package/skills/git-worktree/test/reap-archive-persistence.test.sh +725 -0
  544. package/skills/git-worktree/test/stale-lock-sweep.test.sh +176 -0
  545. package/skills/go/SKILL.md +54 -0
  546. package/skills/growth/SKILL.md +164 -0
  547. package/skills/harvest-debt/SKILL.md +78 -0
  548. package/skills/harvest-debt/scripts/harvest-debt.sh +114 -0
  549. package/skills/heal-skill/SKILL.md +173 -0
  550. package/skills/help/SKILL.md +46 -0
  551. package/skills/incident/NOTICE +39 -0
  552. package/skills/incident/SKILL.md +395 -0
  553. package/skills/incident/scripts/dry-run.sh +448 -0
  554. package/skills/incident/scripts/redact-engine.py +412 -0
  555. package/skills/incident/scripts/redact-sentinel.sh +44 -0
  556. package/skills/incident/templates/pir.md +139 -0
  557. package/skills/incident/test/fixtures/dry-run-incident.json +26 -0
  558. package/skills/incident/test/fixtures/dry-run-secret-leak.json +26 -0
  559. package/skills/incident/test/fixtures/legacy-bash-scanner.sh +82 -0
  560. package/skills/incident/test/fixtures/positive-corpus.md +87 -0
  561. package/skills/incident/test/redact-sentinel.test.sh +1420 -0
  562. package/skills/invoice/SKILL.md +206 -0
  563. package/skills/kb-glossary/SKILL.md +71 -0
  564. package/skills/kb-glossary/references/glossary-format.md +111 -0
  565. package/skills/kb-glossary/references/rejected-request-register.md +115 -0
  566. package/skills/kb-search/SKILL.md +276 -0
  567. package/skills/kb-search/scripts/kb-search-cache.sh +123 -0
  568. package/skills/legal-audit/SKILL.md +161 -0
  569. package/skills/legal-generate/NOTICE +242 -0
  570. package/skills/legal-generate/SKILL.md +244 -0
  571. package/skills/legal-generate/references/templates/advisor-agreement/template.md +12 -0
  572. package/skills/legal-generate/references/templates/business-associate-agreement/template.md +112 -0
  573. package/skills/legal-generate/references/templates/cookie-notice/template.md +82 -0
  574. package/skills/legal-generate/references/templates/dpa-global/template.md +439 -0
  575. package/skills/legal-generate/references/templates/dpa-us/template.md +222 -0
  576. package/skills/legal-generate/references/templates/employee-offer-letter/template.md +75 -0
  577. package/skills/legal-generate/references/templates/master-services-agreement/template.md +252 -0
  578. package/skills/legal-generate/references/templates/mutual-nda/template.md +66 -0
  579. package/skills/legal-generate/references/templates/one-way-nda/template.md +38 -0
  580. package/skills/legal-generate/references/templates/privacy-policy-gdpr/template.md +427 -0
  581. package/skills/legal-generate/references/templates/privacy-policy-us/template.md +313 -0
  582. package/skills/legal-generate/references/templates/terms-of-use/template.md +75 -0
  583. package/skills/legal-generate/scripts/strip-vendor-credit.sh +96 -0
  584. package/skills/linear-fetch/SKILL.md +247 -0
  585. package/skills/linear-fetch/scripts/assert-no-linear-telemetry.sh +66 -0
  586. package/skills/linear-fetch/scripts/redact-linear-urls.sh +51 -0
  587. package/skills/linear-fetch/scripts/render-caller-template.sh +36 -0
  588. package/skills/linear-fetch/test/assert-no-linear-telemetry.test.sh +113 -0
  589. package/skills/linear-fetch/test/parity.test.sh +112 -0
  590. package/skills/linear-fetch/test/persist-safe-integration.test.sh +191 -0
  591. package/skills/linear-fetch/test/redact-linear-urls.test.sh +226 -0
  592. package/skills/merge-pr/SKILL.md +668 -0
  593. package/skills/model-launch-review/SKILL.md +255 -0
  594. package/skills/model-launch-review/scripts/audit-models.sh +501 -0
  595. package/skills/one-shot/SKILL.md +316 -0
  596. package/skills/one-shot/references/token-discipline.md +74 -0
  597. package/skills/operator-bootstrap/SKILL.md +412 -0
  598. package/skills/operator-bootstrap/template.sh +439 -0
  599. package/skills/operator-digest/SKILL.md +287 -0
  600. package/skills/operator-digest/assets/operator-digest.workflow.yml +175 -0
  601. package/skills/operator-digest/scripts/digest-scrub.sh +119 -0
  602. package/skills/operator-digest/scripts/provision-operator-digest-repo.sh +147 -0
  603. package/skills/operator-rephrase/SKILL.md +98 -0
  604. package/skills/pencil-setup/SKILL.md +201 -0
  605. package/skills/pencil-setup/scripts/check_deps.sh +556 -0
  606. package/skills/pencil-setup/scripts/copy_adapter.sh +80 -0
  607. package/skills/pencil-setup/scripts/package-lock.json +1172 -0
  608. package/skills/pencil-setup/scripts/package.json +14 -0
  609. package/skills/pencil-setup/scripts/pencil-error-enrichment.mjs +21 -0
  610. package/skills/pencil-setup/scripts/pencil-mcp-adapter.mjs +733 -0
  611. package/skills/pencil-setup/scripts/pencil-response-classify.mjs +38 -0
  612. package/skills/pencil-setup/scripts/pencil-save-gate.mjs +26 -0
  613. package/skills/pencil-setup/scripts/sanitize-filename.mjs +25 -0
  614. package/skills/plan/SKILL.md +1140 -0
  615. package/skills/plan/references/differential-migration-testing.md +185 -0
  616. package/skills/plan/references/plan-community-discovery.md +42 -0
  617. package/skills/plan/references/plan-founder-check.md +137 -0
  618. package/skills/plan/references/plan-functional-overlap.md +25 -0
  619. package/skills/plan/references/plan-host-fanout.md +51 -0
  620. package/skills/plan/references/plan-issue-templates.md +779 -0
  621. package/skills/plan/references/plan-network-outage-checklist.md +131 -0
  622. package/skills/plan/references/plan-scope-check.md +126 -0
  623. package/skills/plan/references/plan-sharp-edges.md +325 -0
  624. package/skills/plan/references/plan-signoff-lifecycle.md +12 -0
  625. package/skills/plan/references/webapp-cwv-observability.md +143 -0
  626. package/skills/plan-review/SKILL.md +91 -0
  627. package/skills/plan-review/lib/named-panel.mjs +58 -0
  628. package/skills/plan-review/workflows/plan-review.workflow.js +477 -0
  629. package/skills/postmerge/SKILL.md +642 -0
  630. package/skills/postmerge/references/deploy-status-debugging.md +138 -0
  631. package/skills/postmerge/references/graceful-degradation.md +20 -0
  632. package/skills/postmerge/references/sentry-error-count-delta.md +137 -0
  633. package/skills/preflight/SKILL.md +1553 -0
  634. package/skills/preflight/references/check-13-founder-check.md +286 -0
  635. package/skills/preflight/scripts/founder-check.py +1640 -0
  636. package/skills/preflight/scripts/parse-form-a.awk +107 -0
  637. package/skills/preflight/scripts/probe-verb-gate.sh +108 -0
  638. package/skills/product-roadmap/SKILL.md +357 -0
  639. package/skills/product-roadmap/scripts/roadmap-reconcile.sh +327 -0
  640. package/skills/provision-cloudflare/SKILL.md +67 -0
  641. package/skills/provision-cloudflare/scripts/provision-cloudflare.sh +271 -0
  642. package/skills/provision-doppler/SKILL.md +58 -0
  643. package/skills/provision-doppler/scripts/provision-doppler.sh +358 -0
  644. package/skills/provision-github/SKILL.md +59 -0
  645. package/skills/provision-github/scripts/provision-github.sh +300 -0
  646. package/skills/provision-hetzner/SKILL.md +76 -0
  647. package/skills/provision-hetzner/scripts/provision-hetzner.sh +262 -0
  648. package/skills/provision-hetzner/test/fixture/knowledge-base/legal/tenant-dpa-register.md +26 -0
  649. package/skills/provision-hetzner/test/fixture/provision-hetzner-dry-run.golden +34 -0
  650. package/skills/provision-hetzner/test/provision-hetzner-characterization.test.sh +227 -0
  651. package/skills/qa/SKILL.md +263 -0
  652. package/skills/qa/references/browser-preflight.md +22 -0
  653. package/skills/questionnaire-generate/SKILL.md +328 -0
  654. package/skills/questionnaire-generate/references/questionnaire.template +60 -0
  655. package/skills/rclone/SKILL.md +162 -0
  656. package/skills/rclone/scripts/check_setup.sh +63 -0
  657. package/skills/release-announce/SKILL.md +65 -0
  658. package/skills/release-docs/SKILL.md +92 -0
  659. package/skills/reproduce-bug/SKILL.md +358 -0
  660. package/skills/resolve-debt/SKILL.md +85 -0
  661. package/skills/resolve-debt/scripts/resolve-debt.py +584 -0
  662. package/skills/resolve-parallel/SKILL.md +45 -0
  663. package/skills/resolve-parallel/workflows/resolve-parallel.workflow.js +415 -0
  664. package/skills/resolve-pr-parallel/SKILL.md +68 -0
  665. package/skills/resolve-pr-parallel/workflows/resolve-pr-parallel.workflow.js +419 -0
  666. package/skills/resolve-todo-parallel/SKILL.md +57 -0
  667. package/skills/resolve-todo-parallel/workflows/resolve-todo-parallel.workflow.js +398 -0
  668. package/skills/review/SKILL.md +1621 -0
  669. package/skills/review/references/anti-slop-scanner-hook.md +40 -0
  670. package/skills/review/references/defect-classes-migrations.md +9 -0
  671. package/skills/review/references/generated-input-oracle-surfaces.md +15 -0
  672. package/skills/review/references/review-e2e-testing.md +116 -0
  673. package/skills/review/references/review-todo-structure.md +165 -0
  674. package/skills/review/references/risk-tier-and-fix-rounds.md +95 -0
  675. package/skills/review/references/semgrep-custom-rules.yaml +174 -0
  676. package/skills/review/references/ultrathink-phases.md +98 -0
  677. package/skills/review/references/wfs.md +6 -0
  678. package/skills/review/scripts/emit-review-trailer.sh +499 -0
  679. package/skills/review/scripts/ensure-semgrep.sh +99 -0
  680. package/skills/review/scripts/fix-round-seats.sh +197 -0
  681. package/skills/review/test/emit-review-trailer.test.sh +387 -0
  682. package/skills/review/test/ensure-semgrep.test.sh +111 -0
  683. package/skills/review/test/fix-round-seats.test.sh +237 -0
  684. package/skills/review/workflows/README.md +147 -0
  685. package/skills/review/workflows/review.workflow.js +667 -0
  686. package/skills/schedule/SKILL.md +796 -0
  687. package/skills/seo-aeo/SKILL.md +139 -0
  688. package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
  689. package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
  690. package/skills/ship/SKILL.md +3022 -0
  691. package/skills/ship/references/adr-ordinal-collision.md +47 -0
  692. package/skills/ship/references/affected-gate-contract.md +115 -0
  693. package/skills/ship/references/ci-run-diagnosis.md +29 -0
  694. package/skills/ship/references/ci-workflow-authoring.md +68 -0
  695. package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
  696. package/skills/ship/references/cto-ruling-armed-push.md +5 -0
  697. package/skills/ship/references/decision-challenges-render.md +71 -0
  698. package/skills/ship/references/detached-battery-verdict.md +13 -0
  699. package/skills/ship/references/feature-tweet-draft.md +42 -0
  700. package/skills/ship/references/followthrough-issue-body-template.md +47 -0
  701. package/skills/ship/references/followthrough-stub-template.sh +92 -0
  702. package/skills/ship/references/merge-queue-dequeue.md +39 -0
  703. package/skills/ship/references/operator-holds.md +149 -0
  704. package/skills/ship/references/pipeline-tally-render.md +43 -0
  705. package/skills/ship/references/pr-overlap.md +153 -0
  706. package/skills/ship/references/queue-mode.md +7 -0
  707. package/skills/ship/references/ready-run-wait.md +36 -0
  708. package/skills/ship/references/red-on-main-quarantine.md +110 -0
  709. package/skills/ship/references/register-update-pr-pattern.md +88 -0
  710. package/skills/ship/references/settle-then-admin-merge.md +130 -0
  711. package/skills/ship/references/supabase-column-probe.md +13 -0
  712. package/skills/ship/scripts/auto-close-scan.sh +76 -0
  713. package/skills/ship/scripts/battery-owed.sh +403 -0
  714. package/skills/ship/scripts/hold-gate-eval.sh +410 -0
  715. package/skills/ship/scripts/net-issue-flow.sh +665 -0
  716. package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
  717. package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
  718. package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
  719. package/skills/skill-creator/SKILL.md +299 -0
  720. package/skills/skill-creator/references/api-security.md +248 -0
  721. package/skills/skill-creator/references/authoring-levers.md +110 -0
  722. package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
  723. package/skills/skill-creator/references/best-practices.md +431 -0
  724. package/skills/skill-creator/references/common-patterns.md +639 -0
  725. package/skills/skill-creator/references/core-principles.md +193 -0
  726. package/skills/skill-creator/references/executable-code.md +188 -0
  727. package/skills/skill-creator/references/iteration-and-testing.md +509 -0
  728. package/skills/skill-creator/references/official-spec.md +193 -0
  729. package/skills/skill-creator/references/recommended-structure.md +180 -0
  730. package/skills/skill-creator/references/skill-structure.md +396 -0
  731. package/skills/skill-creator/references/using-scripts.md +122 -0
  732. package/skills/skill-creator/references/using-templates.md +120 -0
  733. package/skills/skill-creator/references/workflows-and-validation.md +541 -0
  734. package/skills/skill-creator/scripts/init_skill.py +322 -0
  735. package/skills/skill-creator/scripts/package_skill.py +110 -0
  736. package/skills/skill-creator/scripts/quick_validate.py +64 -0
  737. package/skills/skill-creator/templates/router-skill.md +73 -0
  738. package/skills/skill-creator/templates/simple-skill.md +34 -0
  739. package/skills/skill-creator/workflows/add-reference.md +96 -0
  740. package/skills/skill-creator/workflows/add-script.md +104 -0
  741. package/skills/skill-creator/workflows/add-template.md +83 -0
  742. package/skills/skill-creator/workflows/add-workflow.md +128 -0
  743. package/skills/skill-creator/workflows/audit-skill.md +159 -0
  744. package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
  745. package/skills/skill-creator/workflows/create-new-skill.md +207 -0
  746. package/skills/skill-creator/workflows/get-guidance.md +134 -0
  747. package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
  748. package/skills/skill-creator/workflows/verify-skill.md +226 -0
  749. package/skills/skill-security-scan/SKILL.md +145 -0
  750. package/skills/skill-security-scan/references/disclaimer.md +21 -0
  751. package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
  752. package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
  753. package/skills/skill-security-scan/references/override-mechanism.md +171 -0
  754. package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
  755. package/skills/skill-security-scan/references/regex-patterns.md +136 -0
  756. package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
  757. package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
  758. package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
  759. package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
  760. package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
  761. package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
  762. package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
  763. package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
  764. package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
  765. package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
  766. package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
  767. package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
  768. package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
  769. package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
  770. package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
  771. package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
  772. package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
  773. package/skills/skill-security-scan/scripts/lib.sh +197 -0
  774. package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
  775. package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
  776. package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
  777. package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
  778. package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
  779. package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
  780. package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
  781. package/skills/social-distribute/SKILL.md +485 -0
  782. package/skills/spec-templates/SKILL.md +274 -0
  783. package/skills/sync/SKILL.md +49 -0
  784. package/skills/test-browser/SKILL.md +464 -0
  785. package/skills/test-browser/references/feature-map.md +161 -0
  786. package/skills/test-fix-loop/SKILL.md +159 -0
  787. package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
  788. package/skills/triage/SKILL.md +402 -0
  789. package/skills/trigger-cron/SKILL.md +136 -0
  790. package/skills/trigger-cron/scripts/trigger.sh +198 -0
  791. package/skills/user-set-role/SKILL.md +147 -0
  792. package/skills/user-set-role/scripts/set-role.sh +218 -0
  793. package/skills/user-story-writer/SKILL.md +77 -0
  794. package/skills/ux-audit/SKILL.md +251 -0
  795. package/skills/ux-audit/references/finding.schema.json +62 -0
  796. package/skills/ux-audit/references/route-list.yaml +95 -0
  797. package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
  798. package/skills/ux-audit/scripts/bot-signin.ts +135 -0
  799. package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
  800. package/skills/work/SKILL.md +1479 -0
  801. package/skills/work/references/end-of-work-resume.md +7 -0
  802. package/skills/work/references/hook-test-main-cwd.md +24 -0
  803. package/skills/work/references/key-principles.md +83 -0
  804. package/skills/work/references/property-testing.md +122 -0
  805. package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
  806. package/skills/work/references/work-agent-teams.md +97 -0
  807. package/skills/work/references/work-lifecycle-parallel.md +147 -0
  808. package/skills/work/references/work-lockfile-bumps.md +97 -0
  809. package/skills/work/references/work-scratch-sandboxes.md +212 -0
  810. package/skills/work/references/work-subagent-fanout.md +61 -0
  811. package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
  812. package/skills/xcode-test/SKILL.md +360 -0
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: seo-aeo
3
+ description: "This skill should be used when auditing, fixing, or validating SEO and AEO (AI Engine Optimization) for Eleventy documentation sites. It provides sub-commands for running audits, applying fixes, and validating build output."
4
+ ---
5
+
6
+ <!-- soleur-cloud-mode:start -->
7
+ **Cloud Mode (Devin):** before pipeline work run `bash "${CLAUDE_PLUGIN_ROOT}/scripts/cloud-detect.sh"`. If `CLAUDE_PLUGIN_ROOT` is unset (cloud exec shells do not export it), resolve the root by IDENTITY, never by script basename: for `d` in `"$HOME/.local/share/devin/cli/plugins/cache"` and `/opt/.devin/plugins`, skip unless `[ -d "$d" ]`, then `MANIFEST="$(find "$d" -path '*/.claude-plugin/plugin.json' -exec grep -l '"name"[[:space:]]*:[[:space:]]*"soleur"' {} + 2>/dev/null | head -1)"`, `ROOT="${MANIFEST%/.claude-plugin/plugin.json}"` — one resolution, two consumers: `$ROOT/scripts/cloud-detect.sh` and `$ROOT/scripts/precommit-guard.sh`. (Shape check, not authentication: a planted `{"name":"soleur"}` dir passes — ADR-179 A11.) `local` or `not-local:no-devin-env` proceeds normally; any other `not-local:<reason>` applies `<plugin-root>/devin/INSTRUCTIONS.md` §Cloud Mode: emit the `--banner`, fan out sequentially with `Reviewed-Coverage: sequential-fallback` disclosure (never claim an independent review ran), `message_user` ack before any secrets read or production mutation, and run `precommit-guard.sh` before any `git commit` — hooks do not fire in cloud.
8
+ <!-- soleur-cloud-mode:end -->
9
+
10
+ <!-- grok-harness-invoke:start -->
11
+ **Grok Build (`plugins/soleur/lib/harness.ts` `invokeSkill()`):** Read this SKILL.md in this process and run it to completion. A one-segment `soleur:<name>` in this document names a SKILL — on Grok Build, Read `plugins/soleur/skills/<name>/SKILL.md` in this process; it is not a nested tool_use. A multi-segment id such as `soleur:<domain>:<name>` names an AGENT: spawn it, never Read it, and on Grok Build spawn_subagent takes the id with its colons replaced by hyphens (`agentIdToGrokSubagentType`). **Claude Code:** Skill tool for a skill (`soleur:<name>`), Task tool with `subagent_type` for an agent. Forbidden is executing a subset, not the Read.
12
+ <!-- grok-harness-invoke:end -->
13
+
14
+ # SEO & AEO for Eleventy Docs
15
+
16
+ Audit, fix, and validate SEO and AEO (AI Engine Optimization) for Eleventy documentation sites. This skill routes to sub-commands for analysis, remediation, and CI validation.
17
+
18
+ ## Sub-commands
19
+
20
+ | Command | Description |
21
+ |---------|-------------|
22
+ | `seo-aeo audit` | Audit the site for SEO/AEO issues and produce a report |
23
+ | `seo-aeo fix` | Analyze gaps and apply fixes to source files |
24
+ | `seo-aeo validate` | Run the validation script against built output |
25
+
26
+ If no sub-command is provided, display the table above and ask which sub-command to run.
27
+
28
+ ---
29
+
30
+ ## Phase 0: Prerequisites
31
+
32
+ <critical_sequence>
33
+
34
+ Before executing any sub-command, verify the Eleventy site structure exists.
35
+
36
+ **Required: Eleventy config**
37
+
38
+ ```bash
39
+ if [[ ! -f "eleventy.config.js" ]] && [[ ! -f ".eleventy.js" ]]; then
40
+ echo "No Eleventy config found. This skill works with Eleventy documentation sites."
41
+ # Stop execution
42
+ fi
43
+ ```
44
+
45
+ **Required for validate: Built output**
46
+
47
+ The `validate` sub-command requires a built `_site/` directory. If missing:
48
+
49
+ > No `_site/` directory found. Run `npx @11ty/eleventy` first, then re-run validate.
50
+
51
+ </critical_sequence>
52
+
53
+ ---
54
+
55
+ ## Sub-command: audit
56
+
57
+ Run a comprehensive SEO/AEO audit using the soleur:marketing:seo-aeo-analyst agent.
58
+
59
+ ### Steps
60
+
61
+ 1. Launch the soleur:marketing:seo-aeo-analyst agent via the Task tool:
62
+
63
+ ```
64
+ Task soleur:marketing:seo-aeo-analyst: "Audit this Eleventy documentation site for SEO and AEO issues.
65
+ Read the site configuration, templates, and data files. Produce a structured report
66
+ with critical issues, warnings, and passed checks. Do NOT make any changes."
67
+ ```
68
+
69
+ 2. Present the agent's report to the user
70
+ 3. Offer next steps: "Run `seo-aeo fix` to apply recommended fixes, or `seo-aeo validate` to check the built output."
71
+
72
+ ---
73
+
74
+ ## Sub-command: fix
75
+
76
+ Analyze gaps and apply targeted fixes to source files.
77
+
78
+ ### Steps
79
+
80
+ 1. Launch the soleur:marketing:seo-aeo-analyst agent via the Task tool with fix instructions:
81
+
82
+ ```
83
+ Task soleur:marketing:seo-aeo-analyst: "Audit this Eleventy documentation site for SEO and AEO issues.
84
+ For each issue found, apply a fix to the source files. Read each file before editing.
85
+ After all fixes, build the site with `npx @11ty/eleventy` and run
86
+ `bash "${CLAUDE_PLUGIN_ROOT}/skills/seo-aeo/scripts/validate-seo.sh" _site` and
87
+ `bash "${CLAUDE_PLUGIN_ROOT}/skills/seo-aeo/scripts/validate-csp.sh" _site` to verify.
88
+ Report what was changed and whether validation passed."
89
+ ```
90
+
91
+ 2. Present the agent's report showing changes made
92
+ 3. If validation failed, show the failing checks and offer to re-run fix
93
+
94
+ ---
95
+
96
+ ## Sub-command: validate
97
+
98
+ Run the standalone validation script against built output.
99
+
100
+ ### Steps
101
+
102
+ 1. Verify `_site/` exists. If not, build first:
103
+
104
+ ```bash
105
+ npx @11ty/eleventy
106
+ ```
107
+
108
+ 2. Run the validation scripts:
109
+
110
+ ```bash
111
+ bash "${CLAUDE_PLUGIN_ROOT}/skills/seo-aeo/scripts/validate-seo.sh" _site
112
+ bash "${CLAUDE_PLUGIN_ROOT}/skills/seo-aeo/scripts/validate-csp.sh" _site
113
+ ```
114
+
115
+ 3. Report results:
116
+ - **Exit 0 on both:** All SEO and CSP checks passed
117
+ - **Exit 1:** Show failing checks and recommend running `seo-aeo fix`
118
+
119
+ Validation scripts:
120
+
121
+ - [validate-seo.sh](./scripts/validate-seo.sh) -- SEO/AEO element checks
122
+ - [validate-csp.sh](./scripts/validate-csp.sh) -- Content-Security-Policy hash integrity checks
123
+
124
+ **Known limitations:**
125
+
126
+ - validate-seo.sh only checks named AI bot entries -- wildcard `User-agent: *` blocks are not detected. A site blocking all bots via wildcard will pass validation.
127
+ - validate-csp.sh requires Python 3 for reliable multi-line HTML parsing and SHA-256 hash computation.
128
+
129
+ ## Sharp edges
130
+
131
+ - **Bare-relative href sweep target list.** When dropping `<base href="/">` (or any change that defeats relative-href resolution), the sweep file list is `pages/**/*.njk`, `_includes/**/*.njk`, `index.njk`, **`404.njk`**, `llms.txt.njk`, `sitemap.njk`, and `blog/**/*.md` (`page-redirects.njk` was deleted in #3328 — the meta-refresh redirect machinery now lives entirely at the Cloudflare edge). Siblings of `index.njk` at the docs root are easy to miss because they don't live under `pages/`. Verify post-sweep: `grep -rnE 'href="[a-z][a-z-]*/' plugins/soleur/docs/ | grep -vE 'https?:|mailto:|//'` must be empty. Inline `@font-face` `url('fonts/...')` and preload `href="css/..."` resolve against the document URL too — convert those to absolute in the same edit. See PR #2973.
132
+ - **Bulk URL change in `_data/site.json` requires Nunjucks consumer sweep.** Root-slashing nav URLs breaks any template predicate that concatenates `'/' + item.url` (silent — `aria-current` simply stops activating). Grep `_includes/**/*.njk` for every place the field is consumed (`item.url`, `link.url`) and verify each comparison still computes against the new shape. Same class as the sweep rules in AGENTS.md (`cq-raf-batching-sweep-test-helpers` et al). See PR #2973.
133
+ - **Eleventy custom date filters must guard falsy input.** `new Date(undefined).toISOString()` throws `RangeError`. Any custom Eleventy filter that delegates to the Date constructor must return null on falsy input AND the calling template must wrap its JSON-LD line in `{% if page.date %}` so a null result does not emit invalid schema.org. Example: `dateToRfc3339` in `eleventy.config.js`.
134
+ - **`validate-seo.sh` per-page substitutions need `|| true`.** Inside the per-page loop under `set -euo pipefail`, `count=$(grep -oE 'X' "$f" | wc -l)` aborts the script on zero matches because pipefail propagates grep's exit 1. Use `count=$(grep -cE 'X' "$f" || true)` — `grep -c` returns the count (0 on no match) and the rescue keeps the script alive.
135
+ - **A GSC "Page with redirect" cluster is usually Google's historical memory, not a live sitemap leak.** The affected-URL list is dominated by URLs Google learned before a canonicalization (www→apex, `.html`→clean, `?ref=` query forms) that now 3xx **correctly** — it self-heals server-side as Google re-crawls; there is no repo defect to chase. The only actionable repo condition is a *redirecting* URL present in the *built `sitemap.xml`*. Verify THAT specific condition against a fresh `npx @11ty/eleventy` build (`grep -oE '<loc>[^<]+</loc>' _site/sitemap.xml | grep -E '(\.html$|/pages/)'` must be empty); do not treat the GSC URL list as a bug list. The redirect-stub gate in `validate-seo.sh` enforces it at deploy/CI time. See `knowledge-base/project/learnings/2026-06-01-gsc-page-with-redirect-is-historical-memory-verify-against-build.md`.
136
+ - **A GSC "Duplicate, Google chose different canonical than user" report on a `www` (or other non-canonical-variant) URL is the same benign class** — Google followed the variant's 301 to the apex, read the apex page's correct self-canonical, and consolidated the variant onto it (working correctly). Verify via live `curl`: variant → `301` → apex, apex → `200`, apex canonical self-referential. The fix is operator **VALIDATE FIX** in GSC + wait ~2-4 weeks, NOT a code change — and never "correct" `site.url` to `www` (the inverse of the #4573 apex flip; would emit a redirecting canonical site-wide). The per-page canonical-host gate in `validate-seo.sh` asserts each page's canonical host matches the sitemap host (catches a template regression; a uniform `site.url` flip is covered by NEITHER uptime monitor, and saying otherwise is the defect #7798 exists to fix. A flip changes the canonical tags the site EMITS; it does not touch the Cloudflare Bulk Redirect, so `betteruptime_monitor.soleur_www_redirect` still sees www 301 to the apex and stays green, as does the Sentry reachability probe. The predecessor claim named `sentry_uptime_monitor.soleur_www`, which additionally could never pass at all. A uniform flip is caught at review, not by an alarm). See `knowledge-base/project/learnings/2026-06-12-gsc-duplicate-canonical-on-www-variant-is-benign-consolidation.md`.
137
+ - **Cloudflare Bulk Redirects match `http.request.full_uri` exactly — enumerate every client-reachable shape, not the shapes the replaced machinery emits.** Migrating an origin redirect to `seo-bulk-redirects.tf` needs one item per form: `/<path>/`, `/<path>/index.html`, AND the bare `/<path>` (no trailing slash). The origin's trailing-slash normalization masks a missing bare item today; deleting the origin stub removes the mask and that form 404s. See `knowledge-base/project/learnings/2026-09-18-exact-match-bulk-redirects-need-the-bare-shape-too.md`.
138
+ - **Deleting a build input does not delete its output — verify stub/template removal on a clean `_site`, and fence the emitted signature, not the file size.** Eleventy incremental builds never prune removed templates, so a deleted `articles.njk` still served `_site/articles/index.html` until `rm -rf _site && bunx @11ty/eleventy`. The oracle for "no stubs remain" is a semantic grep over a fresh build (`grep -rl 'http-equiv="refresh"' _site` must be empty) at any size — a byte-threshold predicate lets a layout-wrapped meta refresh escape, and a plan's enumerated file list missed a fifth stub the grep found. See `knowledge-base/project/learnings/workflow-issues/guard-content-not-mechanism-stub-deletion-20260919.md`.
139
+ - **Run every suite that asserts on a marketing surface BEFORE shipping its edit — a plan's verification list is not the gate set.** `plugins/soleur/test/seo-aeo-drift-guard.test.ts` pins marketing prose by *literal*: the `#4410`/`#4754` AEO contract needs a `docs.claude.com` href **inside `.page-citations`**; `#9584` Guard 1 flags any marketing sentence with Claude-plan wording that is not both self-host-scoped and free of a hosted token; `#9582`/`#9579` pin the hero line to open `Self-hosted runs inside your own Claude Code` (committed wireframe `.pen` + counsel audit). A de-monopolizing rewrite can satisfy a sentence's *intent* and still red all three. `grep -rl '<filename>' plugins/soleur/test/` before editing, run those suites, and repair the CONTENT — never the assertion. See `knowledge-base/project/learnings/2026-10-20-docs-edits-regressed-a-guarded-prose-gate.md`.
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env bash
2
+ # validate-csp.sh -- Verify inline script SHA-256 hashes match the CSP meta tag.
3
+ # Usage: bash validate-csp.sh <site-dir>
4
+ # Exit 0 = all hashes match, Exit 1 = mismatch or missing CSP.
5
+ #
6
+ # Scans all HTML pages in the site directory. Each page with a CSP meta tag
7
+ # has its inline scripts extracted and hashed, then compared against the
8
+ # hashes declared in the CSP script-src directive.
9
+ #
10
+ # Depends on: python3 (for reliable multi-line HTML parsing and SHA-256 hashing)
11
+
12
+ set -euo pipefail
13
+
14
+ SITE_DIR="${1:?Usage: validate-csp.sh <site-dir>}"
15
+ FAILURES=0
16
+
17
+ fail() {
18
+ echo "FAIL: $1"
19
+ FAILURES=$((FAILURES + 1))
20
+ }
21
+
22
+ pass() {
23
+ echo "PASS: $1"
24
+ }
25
+
26
+ # ── Collect HTML files ───────────────────────────────────────────────────────
27
+
28
+ html_files=()
29
+ while IFS= read -r -d '' f; do
30
+ html_files+=("$f")
31
+ done < <(find "$SITE_DIR" -name '*.html' -not -name '404.html' -print0)
32
+
33
+ if [[ ${#html_files[@]} -eq 0 ]]; then
34
+ fail "no HTML files found in $SITE_DIR"
35
+ exit 1
36
+ fi
37
+
38
+ pass "found ${#html_files[@]} HTML page(s)"
39
+
40
+ # ── Validate each page ──────────────────────────────────────────────────────
41
+
42
+ PAGES_WITH_CSP=0
43
+
44
+ for html_file in "${html_files[@]}"; do
45
+ page="${html_file#"$SITE_DIR"/}"
46
+
47
+ # NOTE: no meta-refresh skip here — the instant-refresh exemption was the
48
+ # evasion class deleted in #3328 PR-B (edge 301s serve every legacy URL now;
49
+ # a reintroduced stub must fail CSP validation like every other page).
50
+
51
+ # Use Python for all HTML parsing: extract CSP hashes, inline script hashes, AND
52
+ # detect inline event-handler attributes (`onload=`, `onclick=`, etc.) that the
53
+ # CSP would block but no static gate previously caught — see PR #2966 / learning
54
+ # at knowledge-base/project/learnings/best-practices/ for the silent-failure class.
55
+ # Passing the file path via sys.argv[1] avoids shell injection.
56
+ RESULT=$(python3 -c "
57
+ import hashlib, base64, re, sys
58
+ from html.parser import HTMLParser
59
+
60
+ with open(sys.argv[1], 'r') as f:
61
+ content = f.read()
62
+
63
+ # Extract CSP meta tag
64
+ csp_match = re.search(
65
+ r'<meta\s+http-equiv=\"Content-Security-Policy\"\s+content=\"([^\"]*)\"',
66
+ content
67
+ )
68
+ if not csp_match:
69
+ print('NO_CSP')
70
+ sys.exit(0)
71
+
72
+ csp_content = csp_match.group(1)
73
+
74
+ # Extract script-src directive
75
+ src_match = re.search(r'script-src\s+([^;]+)', csp_content)
76
+ if not src_match:
77
+ print('NO_SCRIPT_SRC')
78
+ sys.exit(0)
79
+
80
+ script_src = src_match.group(1)
81
+
82
+ # Extract sha256 hashes from CSP
83
+ csp_hashes = set(re.findall(r\"'(sha256-[A-Za-z0-9+/=]+)'\", script_src))
84
+
85
+ # Detect whether the CSP allows inline event handlers.
86
+ allows_unsafe_inline = bool(re.search(r\"'unsafe-inline'\", script_src))
87
+ allows_unsafe_hashes = bool(re.search(r\"'unsafe-hashes'\", script_src))
88
+
89
+ # Extract inline scripts (excluding ld+json and src= scripts)
90
+ scripts = re.findall(
91
+ r'<script(?![^>]*(?:type=\"application/ld\+json\"|src=))[^>]*>(.*?)</script>',
92
+ content,
93
+ re.DOTALL
94
+ )
95
+
96
+ # Compute hashes
97
+ computed_hashes = set()
98
+ for script in scripts:
99
+ h = hashlib.sha256(script.encode('utf-8')).digest()
100
+ b64 = base64.b64encode(h).decode()
101
+ computed_hashes.add(f'sha256-{b64}')
102
+
103
+ # Output: CSP_HASHES|COMPUTED_HASHES (pipe-separated sets, comma-separated within)
104
+ print(','.join(sorted(csp_hashes)) + '|' + ','.join(sorted(computed_hashes)))
105
+
106
+ # Check for prohibited directives in meta tags
107
+ prohibited = ['strict-dynamic', 'report-uri', 'report-to', 'frame-ancestors', 'sandbox']
108
+ found_prohibited = [d for d in prohibited if re.search(r'(^|;\s*)' + d + r'(\s|;|$)', csp_content)]
109
+ if found_prohibited:
110
+ print('PROHIBITED:' + ','.join(found_prohibited))
111
+
112
+ # Inline event-handler scan. Use html.parser so we naturally skip <script>
113
+ # content and HTML comments — both contained false-positives in earlier regex
114
+ # attempts (e.g., 'window.onload =' inside a <script>, or 'onload=' inside a
115
+ # <!-- comment -->).
116
+ class EventHandlerFinder(HTMLParser):
117
+ def __init__(self):
118
+ super().__init__()
119
+ self.violations = []
120
+ def handle_starttag(self, tag, attrs):
121
+ for name, value in attrs:
122
+ if name and name.lower().startswith('on') and len(name) > 2 and name[2:].isalpha():
123
+ line, col = self.getpos()
124
+ self.violations.append((line, tag, name, (value or '')[:80]))
125
+ handle_startendtag = handle_starttag
126
+
127
+ if not (allows_unsafe_inline or allows_unsafe_hashes):
128
+ finder = EventHandlerFinder()
129
+ try:
130
+ finder.feed(content)
131
+ except Exception as e:
132
+ print(f'INLINE_HANDLER_PARSE_ERROR:{e}')
133
+ for line, tag, attr, value in finder.violations:
134
+ # One violation per line. Bash splits on newlines.
135
+ print(f'INLINE_HANDLER:{line}:{tag}:{attr}={value}')
136
+ " "$html_file" || true)
137
+
138
+ # Skip pages without CSP
139
+ if [[ "$RESULT" == "NO_CSP" ]]; then
140
+ continue
141
+ fi
142
+
143
+ if [[ "$RESULT" == "NO_SCRIPT_SRC" ]]; then
144
+ fail "$page: CSP meta tag found but no script-src directive"
145
+ continue
146
+ fi
147
+
148
+ PAGES_WITH_CSP=$((PAGES_WITH_CSP + 1))
149
+
150
+ # Parse Python output
151
+ HASH_LINE=$(echo "$RESULT" | head -1)
152
+ CSP_HASHES_STR="${HASH_LINE%%|*}"
153
+ COMPUTED_HASHES_STR="${HASH_LINE#*|}"
154
+
155
+ # Check for prohibited directives
156
+ PROHIBITED_LINE=$(echo "$RESULT" | grep '^PROHIBITED:' || true)
157
+ if [[ -n "$PROHIBITED_LINE" ]]; then
158
+ directives="${PROHIBITED_LINE#PROHIBITED:}"
159
+ fail "$page: CSP contains unsupported meta tag directive(s): $directives"
160
+ fi
161
+
162
+ # Check for inline event-handler attributes that CSP would silently block.
163
+ # See PR #2966 — `<link onload="...">` was blocked by `script-src` lacking
164
+ # `'unsafe-inline'`/`'unsafe-hashes'`, and the swap never fired in production.
165
+ while IFS= read -r handler_line; do
166
+ [[ -z "$handler_line" ]] && continue
167
+ detail="${handler_line#INLINE_HANDLER:}"
168
+ fail "$page: inline event-handler attribute '$detail' is silently blocked by script-src (no 'unsafe-inline'/'unsafe-hashes'). Move the handler into a hashed <script> block, or add 'unsafe-hashes' + a hash."
169
+ done < <(echo "$RESULT" | grep '^INLINE_HANDLER:' || true)
170
+
171
+ PARSE_ERR=$(echo "$RESULT" | grep '^INLINE_HANDLER_PARSE_ERROR:' || true)
172
+ if [[ -n "$PARSE_ERR" ]]; then
173
+ fail "$page: inline-handler parse error: ${PARSE_ERR#INLINE_HANDLER_PARSE_ERROR:}"
174
+ fi
175
+
176
+ # Convert to arrays
177
+ IFS=',' read -ra csp_hashes <<< "$CSP_HASHES_STR"
178
+ IFS=',' read -ra computed_hashes <<< "$COMPUTED_HASHES_STR"
179
+
180
+ # Skip if no inline scripts on this page
181
+ if [[ ${#computed_hashes[@]} -eq 0 ]] || [[ -z "${computed_hashes[0]}" ]]; then
182
+ continue
183
+ fi
184
+
185
+ # Verify every inline script hash is in the CSP
186
+ for computed in "${computed_hashes[@]}"; do
187
+ [[ -z "$computed" ]] && continue
188
+ found=false
189
+ for csp_hash in "${csp_hashes[@]}"; do
190
+ if [[ "$computed" == "$csp_hash" ]]; then
191
+ found=true
192
+ break
193
+ fi
194
+ done
195
+ if $found; then
196
+ pass "$page: inline script hash $computed matches CSP"
197
+ else
198
+ fail "$page: inline script hash $computed NOT in CSP -- add '$computed' to script-src"
199
+ fi
200
+ done
201
+
202
+ # Verify no orphan hashes in CSP
203
+ for csp_hash in "${csp_hashes[@]}"; do
204
+ [[ -z "$csp_hash" ]] && continue
205
+ found=false
206
+ for computed in "${computed_hashes[@]}"; do
207
+ if [[ "$csp_hash" == "$computed" ]]; then
208
+ found=true
209
+ break
210
+ fi
211
+ done
212
+ if ! $found; then
213
+ fail "$page: orphan CSP hash $csp_hash matches no inline script -- remove from script-src"
214
+ fi
215
+ done
216
+ done
217
+
218
+ if [[ $PAGES_WITH_CSP -eq 0 ]]; then
219
+ fail "no pages with CSP meta tag found in $SITE_DIR"
220
+ else
221
+ pass "validated CSP on $PAGES_WITH_CSP page(s)"
222
+ fi
223
+
224
+ # ── Summary ──────────────────────────────────────────────────────────────────
225
+
226
+ echo ""
227
+ if [[ $FAILURES -eq 0 ]]; then
228
+ echo "All CSP checks passed."
229
+ exit 0
230
+ else
231
+ echo "$FAILURES CSP check(s) failed."
232
+ exit 1
233
+ fi
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # validate-seo.sh -- Check built Eleventy output for required SEO/AEO elements.
3
+ # Usage: bash validate-seo.sh <site-dir>
4
+ # Exit 0 = all checks pass, Exit 1 = one or more checks failed.
5
+
6
+ set -euo pipefail
7
+
8
+ SITE_DIR="${1:?Usage: validate-seo.sh <site-dir>}"
9
+ FAILURES=0
10
+ # Bare host (no scheme) the per-page canonical-href gate expects. DERIVED from the
11
+ # sitemap's single <loc> host once it is validated below — there is no second literal
12
+ # pin to drift from _data/site.json. Empty until the sitemap host is established (or
13
+ # if the sitemap is missing/multi-host, in which case the per-page host check is
14
+ # skipped — those failures are already reported by the sitemap gates).
15
+ CANONICAL_HOST_EXPECTED=""
16
+
17
+ fail() {
18
+ echo "FAIL: $1"
19
+ FAILURES=$((FAILURES + 1))
20
+ }
21
+
22
+ pass() {
23
+ echo "PASS: $1"
24
+ }
25
+
26
+ # ── llms.txt ──────────────────────────────────────────────────────────────────
27
+
28
+ if [[ -f "$SITE_DIR/llms.txt" ]]; then
29
+ pass "llms.txt exists"
30
+ else
31
+ fail "llms.txt missing"
32
+ fi
33
+
34
+ # ── robots.txt AI bot access ──────────────────────────────────────────────────
35
+ # Limitation: checks only the line immediately after User-agent for Disallow.
36
+ # Multi-line stanzas with comments between directives may be misdetected.
37
+
38
+ if [[ -f "$SITE_DIR/robots.txt" ]]; then
39
+ pass "robots.txt exists"
40
+ for bot in GPTBot PerplexityBot ClaudeBot Google-Extended; do
41
+ if grep -qi "User-agent: $bot" "$SITE_DIR/robots.txt" && \
42
+ grep -qiE "Disallow: /\s*$" < <(grep -A1 -i "User-agent: $bot" "$SITE_DIR/robots.txt"); then
43
+ fail "robots.txt blocks $bot"
44
+ else
45
+ pass "robots.txt does not block $bot"
46
+ fi
47
+ done
48
+ else
49
+ fail "robots.txt missing"
50
+ fi
51
+
52
+ # ── sitemap.xml ───────────────────────────────────────────────────────────────
53
+
54
+ if [[ -f "$SITE_DIR/sitemap.xml" ]]; then
55
+ pass "sitemap.xml exists"
56
+ if grep -q '<lastmod>' "$SITE_DIR/sitemap.xml"; then
57
+ pass "sitemap.xml contains lastmod dates"
58
+ else
59
+ fail "sitemap.xml missing lastmod dates"
60
+ fi
61
+ # NON_HTML answers "is each <loc> a page URL at all?" (allows trailing-slash OR
62
+ # .html). This is a DIFFERENT axis from the redirect-stub gate below, which forbids
63
+ # the redirecting .html / /pages/ shape. Leave the `.html` allowance here intact.
64
+ NON_HTML=$(grep -oP '(?<=<loc>)[^<]+' "$SITE_DIR/sitemap.xml" | grep -vE '(/|\.html)$' || true)
65
+ if [[ -n "$NON_HTML" ]]; then
66
+ fail "sitemap.xml contains non-HTML entries: $NON_HTML"
67
+ else
68
+ pass "sitemap.xml contains only HTML entries"
69
+ fi
70
+
71
+ # Redirect-stub gate: redirect stubs used to live at /pages/*.html (meta-refresh
72
+ # + canonical-to-clean-URL, generated by page-redirects.njk — machinery deleted
73
+ # in #3328; every legacy URL is now served by a Cloudflare edge 301). The stub
74
+ # shape MUST NEVER appear here — Google indexes them as "Page with redirect" (the
75
+ # GSC cluster this gate guards on the PATH axis; the canonical-host gate below
76
+ # guards the HOST axis). Canonical pages all use trailing-slash directory URLs;
77
+ # no canonical page ends in .html. See the 2026-06-01 GSC sitemap-redirect-leak plan.
78
+ # `|| true` absorbs grep's exit-1 (no matches = the PASS case) under pipefail.
79
+ REDIRECTING=$(grep -oP '(?<=<loc>)[^<]+' "$SITE_DIR/sitemap.xml" \
80
+ | grep -E '(\.html$|/pages/)' || true)
81
+ if [[ -n "$REDIRECTING" ]]; then
82
+ fail "sitemap.xml contains redirecting URLs (must be canonical clean URLs only): $(echo "$REDIRECTING" | tr '\n' ' ')"
83
+ else
84
+ pass "sitemap.xml contains no redirecting URLs (.html / /pages/)"
85
+ fi
86
+
87
+ # Canonical-host gate (#3297): every <loc> must use a single canonical host,
88
+ # and that host must match the one declared in robots.txt's `Sitemap:` line
89
+ # (only if robots.txt actually has a Sitemap line — fixtures may omit it).
90
+ # This catches the GSC `Page with redirect` cluster cause: sitemap on www
91
+ # while live infra 301s www→apex (the canonical host is the bare apex
92
+ # https://soleur.ai; GitHub Pages enforces the CNAME apex). See the GSC
93
+ # indexing-fixes brainstorm and the 2026-05-29 host flip.
94
+ # `|| true` absorbs grep's exit-1 when sitemap has zero <loc> entries —
95
+ # pipefail would otherwise short-circuit before the empty-guard below.
96
+ SITEMAP_HOSTS=$(grep -oP '(?<=<loc>)[^<]+' "$SITE_DIR/sitemap.xml" \
97
+ | sed -E 's|^(https?://[^/]+).*|\1|' | sort -u || true)
98
+ HOST_COUNT=$(echo "$SITEMAP_HOSTS" | grep -c . || true)
99
+ if [[ -z "$SITEMAP_HOSTS" ]]; then
100
+ fail "sitemap.xml has no <loc> entries"
101
+ elif [[ "$HOST_COUNT" -gt 1 ]]; then
102
+ fail "sitemap.xml mixes multiple hosts: $(echo "$SITEMAP_HOSTS" | tr '\n' ' ')"
103
+ else
104
+ pass "sitemap.xml uses a single canonical host: $SITEMAP_HOSTS"
105
+ # Establish the per-page canonical-href host expectation from this single,
106
+ # validated sitemap host (strip the scheme to a bare host for comparison).
107
+ # $SITEMAP_HOSTS is already a single sort -u'd line in this HOST_COUNT==1 branch.
108
+ CANONICAL_HOST_EXPECTED=$(echo "$SITEMAP_HOSTS" | sed -E 's|^https?://||')
109
+ # Closing branch is below this if-fi block — the host comparison only
110
+ # runs when robots.txt exists AND has a `Sitemap:` line. Fixtures that
111
+ # omit the Sitemap line still pass the sitemap-internal consistency
112
+ # check above; production robots.txt always has the line.
113
+ if [[ -f "$SITE_DIR/robots.txt" ]] && grep -qiE '^Sitemap:' "$SITE_DIR/robots.txt"; then
114
+ ROBOTS_HOST=$(grep -iE '^Sitemap:' "$SITE_DIR/robots.txt" \
115
+ | sed -E 's|^[Ss]itemap:[[:space:]]*(https?://[^/]+).*|\1|' | head -1)
116
+ if [[ "$SITEMAP_HOSTS" != "$ROBOTS_HOST" ]]; then
117
+ fail "sitemap host ($SITEMAP_HOSTS) does not match robots.txt Sitemap line ($ROBOTS_HOST)"
118
+ else
119
+ pass "sitemap host matches robots.txt Sitemap line"
120
+ fi
121
+ fi
122
+ fi
123
+ else
124
+ fail "sitemap.xml missing"
125
+ fi
126
+
127
+ # ── HTML pages ────────────────────────────────────────────────────────────────
128
+
129
+ html_files=()
130
+ while IFS= read -r -d '' f; do
131
+ html_files+=("$f")
132
+ done < <(find "$SITE_DIR" -name '*.html' -not -name '404.html' -print0)
133
+
134
+ if [[ ${#html_files[@]} -eq 0 ]]; then
135
+ fail "no HTML files found in $SITE_DIR"
136
+ else
137
+ pass "found ${#html_files[@]} HTML page(s)"
138
+ fi
139
+
140
+ for f in "${html_files[@]}"; do
141
+ page="${f#"$SITE_DIR"/}"
142
+
143
+ # Canonical URL
144
+ if grep -q 'rel="canonical"' "$f"; then
145
+ pass "$page has canonical URL"
146
+ else
147
+ fail "$page missing canonical URL"
148
+ fi
149
+
150
+ # Canonical-href host gate (2026-06-12 GSC duplicate-canonical plan): EVERY page
151
+ # <link rel="canonical"> absolute-host MUST match the sitemap's single canonical host
152
+ # (CANONICAL_HOST_EXPECTED, derived above). A page emitting a different host (e.g. a
153
+ # www variant while the sitemap/apex is canonical) 301-redirects in prod and induces
154
+ # the GSC "Duplicate, Google chose different canonical than user" cluster — the
155
+ # per-page sibling of the sitemap host-axis gate above. ALL canonical tags are
156
+ # checked (a rogue second tag with a different host is the exact failure mode), not
157
+ # just the first. Only ABSOLUTE-host canonicals are extracted; a relative href leaves
158
+ # the host list empty and is skipped (the presence check above covers absence). The
159
+ # `|| true` absorbs grep's exit-1 on no-match under `set -euo pipefail`, mirroring the
160
+ # sitemap gates above (lines ~80/~96).
161
+ if [[ -n "$CANONICAL_HOST_EXPECTED" ]]; then
162
+ canon_hosts=$(grep -oiE '<link[^>]+rel="canonical"[^>]*>' "$f" \
163
+ | grep -oiE 'href="https?://[^/"]+' | sed -E 's|.*href="https?://||' || true)
164
+ if [[ -n "$canon_hosts" ]]; then
165
+ bad_host=$(printf '%s\n' "$canon_hosts" | grep -vxF "$CANONICAL_HOST_EXPECTED" | head -1 || true)
166
+ if [[ -n "$bad_host" ]]; then
167
+ fail "$page canonical host '$bad_host' differs from sitemap canonical host '$CANONICAL_HOST_EXPECTED' — a redirecting/other-host canonical induces GSC 'Google chose different canonical than user'"
168
+ else
169
+ pass "$page canonical host matches sitemap host ($CANONICAL_HOST_EXPECTED)"
170
+ fi
171
+ fi
172
+ fi
173
+
174
+ # JSON-LD
175
+ if grep -q 'application/ld+json' "$f"; then
176
+ pass "$page has JSON-LD"
177
+ else
178
+ fail "$page missing JSON-LD structured data"
179
+ fi
180
+
181
+ # OG tags
182
+ if grep -q 'property="og:title"' "$f"; then
183
+ pass "$page has og:title"
184
+ else
185
+ fail "$page missing og:title"
186
+ fi
187
+
188
+ # Twitter card
189
+ if grep -q 'name="twitter:card"' "$f"; then
190
+ pass "$page has Twitter card"
191
+ else
192
+ fail "$page missing Twitter card meta tag"
193
+ fi
194
+
195
+ # No <base> tag (per #2945) — root-domain site uses absolute root-relative paths
196
+ # Anchor on tag boundary to avoid false positives in prose / code blocks
197
+ if grep -qE '<base[[:space:]>]' "$f"; then
198
+ fail "$page contains <base> tag (must be removed for root-domain site)"
199
+ else
200
+ pass "$page has no <base> tag"
201
+ fi
202
+
203
+ # Exactly one <h1> per page (per #2943)
204
+ # grep -c exits 1 on zero matches under pipefail; `|| true` keeps the script alive
205
+ h1_count=$(grep -cE '<h1[ >]' "$f" || true)
206
+ if [[ "$h1_count" -ne 1 ]]; then
207
+ fail "$page has $h1_count <h1> tags (expected exactly 1)"
208
+ else
209
+ pass "$page has exactly 1 <h1>"
210
+ fi
211
+
212
+ # Non-empty meta description (per #2942)
213
+ if grep -q 'name="description" content=""' "$f"; then
214
+ fail "$page has empty meta description"
215
+ else
216
+ pass "$page has non-empty meta description"
217
+ fi
218
+
219
+ # FAQPage existence when visible FAQ markup is rendered (per #2948).
220
+ # This check verifies the JSON-LD block is PRESENT alongside the visible
221
+ # FAQ, not that every Q/A pair has parity — see the FAQPage parity learning
222
+ # for the codepoint-exact check (run via /soleur:review's data-integrity
223
+ # agent, not this CI gate).
224
+ if grep -qE 'class="faq-(item|question|answer|list)\b' "$f"; then
225
+ if grep -q '"@type": "FAQPage"' "$f"; then
226
+ pass "$page has FAQPage JSON-LD alongside visible FAQ"
227
+ else
228
+ fail "$page renders faq- class but lacks FAQPage JSON-LD"
229
+ fi
230
+ fi
231
+ done
232
+
233
+ # ── Homepage-specific: SoftwareApplication ────────────────────────────────────
234
+
235
+ if [[ -f "$SITE_DIR/index.html" ]]; then
236
+ if grep -q '"SoftwareApplication"' "$SITE_DIR/index.html"; then
237
+ pass "homepage has SoftwareApplication JSON-LD"
238
+ else
239
+ fail "homepage missing SoftwareApplication JSON-LD"
240
+ fi
241
+ fi
242
+
243
+ # ── Changelog: build-time content ─────────────────────────────────────────────
244
+
245
+ if [[ -f "$SITE_DIR/pages/changelog.html" ]]; then
246
+ if grep -q 'Loading changelog' "$SITE_DIR/pages/changelog.html"; then
247
+ fail "changelog page still has client-side loading placeholder"
248
+ else
249
+ pass "changelog page has build-time content"
250
+ fi
251
+ fi
252
+
253
+ # ── Summary ───────────────────────────────────────────────────────────────────
254
+
255
+ echo ""
256
+ if [[ $FAILURES -eq 0 ]]; then
257
+ echo "All SEO checks passed."
258
+ exit 0
259
+ else
260
+ echo "$FAILURES check(s) failed."
261
+ exit 1
262
+ fi