@soleur/opencode 0.0.0-stage → 3.354.2
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/.claude-plugin/agents.manifest.json +476 -0
- package/.claude-plugin/plugin.json +42 -0
- package/README.md +44 -2
- package/agents/community/.gitkeep +0 -0
- package/agents/engineering/cto.md +87 -0
- package/agents/engineering/design/ddd-architect.md +95 -0
- package/agents/engineering/discovery/agent-finder.md +214 -0
- package/agents/engineering/discovery/functional-discovery.md +182 -0
- package/agents/engineering/infra/infra-security.md +151 -0
- package/agents/engineering/infra/platform-strategist.md +109 -0
- package/agents/engineering/infra/terraform-architect.md +171 -0
- package/agents/engineering/prompt-engineer.md +37 -0
- package/agents/engineering/research/best-practices-researcher.md +115 -0
- package/agents/engineering/research/framework-docs-researcher.md +93 -0
- package/agents/engineering/research/git-history-analyzer.md +53 -0
- package/agents/engineering/research/learnings-researcher.md +285 -0
- package/agents/engineering/research/repo-research-analyst.md +118 -0
- package/agents/engineering/review/agent-native-reviewer.md +279 -0
- package/agents/engineering/review/architecture-strategist.md +61 -0
- package/agents/engineering/review/code-quality-analyst.md +92 -0
- package/agents/engineering/review/code-simplicity-reviewer.md +109 -0
- package/agents/engineering/review/data-integrity-guardian.md +82 -0
- package/agents/engineering/review/data-migration-expert.md +98 -0
- package/agents/engineering/review/deployment-verification-agent.md +183 -0
- package/agents/engineering/review/dhh-rails-reviewer.md +46 -0
- package/agents/engineering/review/kieran-rails-reviewer.md +87 -0
- package/agents/engineering/review/legacy-code-expert.md +55 -0
- package/agents/engineering/review/observability-coverage-reviewer.md +147 -0
- package/agents/engineering/review/pattern-recognition-specialist.md +59 -0
- package/agents/engineering/review/performance-oracle.md +130 -0
- package/agents/engineering/review/security-sentinel.md +167 -0
- package/agents/engineering/review/semgrep-sast.md +36 -0
- package/agents/engineering/review/test-design-reviewer.md +150 -0
- package/agents/engineering/review/user-impact-reviewer.md +70 -0
- package/agents/engineering/workflow/pr-comment-resolver.md +69 -0
- package/agents/finance/budget-analyst.md +29 -0
- package/agents/finance/cfo.md +44 -0
- package/agents/finance/financial-reporter.md +30 -0
- package/agents/finance/revenue-analyst.md +30 -0
- package/agents/legal/clo.md +115 -0
- package/agents/legal/legal-compliance-auditor.md +110 -0
- package/agents/legal/legal-document-generator.md +134 -0
- package/agents/marketing/analytics-analyst.md +25 -0
- package/agents/marketing/brand-architect.md +181 -0
- package/agents/marketing/cmo.md +76 -0
- package/agents/marketing/conversion-optimizer.md +19 -0
- package/agents/marketing/copywriter.md +20 -0
- package/agents/marketing/fact-checker.md +70 -0
- package/agents/marketing/growth-strategist.md +145 -0
- package/agents/marketing/paid-media-strategist.md +28 -0
- package/agents/marketing/pricing-strategist.md +17 -0
- package/agents/marketing/programmatic-seo-specialist.md +25 -0
- package/agents/marketing/retention-strategist.md +23 -0
- package/agents/marketing/seo-aeo-analyst.md +164 -0
- package/agents/operations/coo.md +50 -0
- package/agents/operations/ops-advisor.md +78 -0
- package/agents/operations/ops-provisioner.md +101 -0
- package/agents/operations/ops-research.md +52 -0
- package/agents/operations/service-automator.md +278 -0
- package/agents/product/business-validator.md +206 -0
- package/agents/product/competitive-intelligence.md +89 -0
- package/agents/product/cpo.md +63 -0
- package/agents/product/design/ux-design-lead.md +209 -0
- package/agents/product/spec-flow-analyzer.md +122 -0
- package/agents/sales/cro.md +44 -0
- package/agents/sales/deal-architect.md +29 -0
- package/agents/sales/outbound-strategist.md +29 -0
- package/agents/sales/pipeline-analyst.md +29 -0
- package/agents/support/cco.md +48 -0
- package/agents/support/community-manager.md +570 -0
- package/agents/support/ticket-triage.md +70 -0
- package/bin/soleur-opencode.js +29 -0
- package/commands/go.md +660 -0
- package/commands/help.md +273 -0
- package/commands/sync.md +977 -0
- package/commands/version.md +103 -0
- package/hooks/antigravity-hooks.json +84 -0
- package/hooks/antigravity-session-start.sh +15 -0
- package/hooks/bare-root-cwd-guard.sh +131 -0
- package/hooks/browser-snapshot-credential-guard.sh +200 -0
- package/hooks/codex-session-start.sh +9 -0
- package/hooks/compaction-state.sh +525 -0
- package/hooks/destructive-command-guard.sh +1387 -0
- package/hooks/devin-session-start.sh +90 -0
- package/hooks/ext-filter-detect.sh +171 -0
- package/hooks/hooks.json +133 -0
- package/hooks/lib/hook-tool-kind.sh +24 -0
- package/hooks/lib/log-rotation.sh +176 -0
- package/hooks/lib/shell-argv.pl +576 -0
- package/hooks/memory-backstop.sh +1114 -0
- package/hooks/operator-stage-approval.sh +391 -0
- package/hooks/stop-hook.sh +367 -0
- package/hooks/unkept-promise-hook.sh +383 -0
- package/hooks/version-banner.sh +127 -0
- package/hooks/welcome-hook.sh +50 -0
- package/install.js +29 -0
- package/lib/agent-registry.ts +154 -0
- package/lib/c4-canonical-cli.mjs +34 -0
- package/lib/c4-canonical.mjs +46 -0
- package/lib/c4-from-components.ts +554 -0
- package/lib/go-routing.ts +81 -0
- package/lib/grok-inspect-contract.ts +311 -0
- package/lib/harness-model-map.ts +101 -0
- package/lib/harness-parity.ts +726 -0
- package/lib/harness-tool-map.ts +180 -0
- package/lib/harness.ts +716 -0
- package/lib/heartbeat-live-reconcile.ts +1153 -0
- package/lib/heartbeat-manifest.ts +361 -0
- package/lib/kb-coverage.ts +252 -0
- package/lib/pr-merge-poll.ts +103 -0
- package/lib/workflow-fidelity.ts +388 -0
- package/opencode/INSTRUCTIONS.md +171 -0
- package/opencode/agents/engineering/cto.md +10 -0
- package/opencode/agents/engineering/design/ddd-architect.md +10 -0
- package/opencode/agents/engineering/discovery/agent-finder.md +10 -0
- package/opencode/agents/engineering/discovery/functional-discovery.md +10 -0
- package/opencode/agents/engineering/infra/infra-security.md +10 -0
- package/opencode/agents/engineering/infra/platform-strategist.md +10 -0
- package/opencode/agents/engineering/infra/terraform-architect.md +10 -0
- package/opencode/agents/engineering/prompt-engineer.md +10 -0
- package/opencode/agents/engineering/research/best-practices-researcher.md +10 -0
- package/opencode/agents/engineering/research/framework-docs-researcher.md +10 -0
- package/opencode/agents/engineering/research/git-history-analyzer.md +10 -0
- package/opencode/agents/engineering/research/learnings-researcher.md +10 -0
- package/opencode/agents/engineering/research/repo-research-analyst.md +10 -0
- package/opencode/agents/engineering/review/agent-native-reviewer.md +10 -0
- package/opencode/agents/engineering/review/architecture-strategist.md +10 -0
- package/opencode/agents/engineering/review/code-quality-analyst.md +10 -0
- package/opencode/agents/engineering/review/code-simplicity-reviewer.md +10 -0
- package/opencode/agents/engineering/review/data-integrity-guardian.md +10 -0
- package/opencode/agents/engineering/review/data-migration-expert.md +10 -0
- package/opencode/agents/engineering/review/deployment-verification-agent.md +10 -0
- package/opencode/agents/engineering/review/dhh-rails-reviewer.md +10 -0
- package/opencode/agents/engineering/review/kieran-rails-reviewer.md +10 -0
- package/opencode/agents/engineering/review/legacy-code-expert.md +10 -0
- package/opencode/agents/engineering/review/observability-coverage-reviewer.md +10 -0
- package/opencode/agents/engineering/review/pattern-recognition-specialist.md +10 -0
- package/opencode/agents/engineering/review/performance-oracle.md +10 -0
- package/opencode/agents/engineering/review/security-sentinel.md +10 -0
- package/opencode/agents/engineering/review/semgrep-sast.md +10 -0
- package/opencode/agents/engineering/review/test-design-reviewer.md +10 -0
- package/opencode/agents/engineering/review/user-impact-reviewer.md +10 -0
- package/opencode/agents/engineering/workflow/pr-comment-resolver.md +10 -0
- package/opencode/agents/finance/budget-analyst.md +10 -0
- package/opencode/agents/finance/cfo.md +10 -0
- package/opencode/agents/finance/financial-reporter.md +10 -0
- package/opencode/agents/finance/revenue-analyst.md +10 -0
- package/opencode/agents/legal/clo.md +10 -0
- package/opencode/agents/legal/legal-compliance-auditor.md +10 -0
- package/opencode/agents/legal/legal-document-generator.md +10 -0
- package/opencode/agents/marketing/analytics-analyst.md +10 -0
- package/opencode/agents/marketing/brand-architect.md +10 -0
- package/opencode/agents/marketing/cmo.md +10 -0
- package/opencode/agents/marketing/conversion-optimizer.md +10 -0
- package/opencode/agents/marketing/copywriter.md +10 -0
- package/opencode/agents/marketing/fact-checker.md +10 -0
- package/opencode/agents/marketing/growth-strategist.md +10 -0
- package/opencode/agents/marketing/paid-media-strategist.md +10 -0
- package/opencode/agents/marketing/pricing-strategist.md +10 -0
- package/opencode/agents/marketing/programmatic-seo-specialist.md +10 -0
- package/opencode/agents/marketing/retention-strategist.md +10 -0
- package/opencode/agents/marketing/seo-aeo-analyst.md +10 -0
- package/opencode/agents/operations/coo.md +10 -0
- package/opencode/agents/operations/ops-advisor.md +10 -0
- package/opencode/agents/operations/ops-provisioner.md +10 -0
- package/opencode/agents/operations/ops-research.md +10 -0
- package/opencode/agents/operations/service-automator.md +10 -0
- package/opencode/agents/product/business-validator.md +10 -0
- package/opencode/agents/product/competitive-intelligence.md +10 -0
- package/opencode/agents/product/cpo.md +10 -0
- package/opencode/agents/product/design/ux-design-lead.md +10 -0
- package/opencode/agents/product/spec-flow-analyzer.md +10 -0
- package/opencode/agents/sales/cro.md +10 -0
- package/opencode/agents/sales/deal-architect.md +10 -0
- package/opencode/agents/sales/outbound-strategist.md +10 -0
- package/opencode/agents/sales/pipeline-analyst.md +10 -0
- package/opencode/agents/support/cco.md +10 -0
- package/opencode/agents/support/community-manager.md +10 -0
- package/opencode/agents/support/ticket-triage.md +10 -0
- package/opencode/commands/go.md +10 -0
- package/opencode/commands/help.md +10 -0
- package/opencode/commands/sync.md +10 -0
- package/opencode/commands/version.md +11 -0
- package/opencode/opencode.json +8 -0
- package/opencode/plugin.ts +818 -0
- package/package.json +36 -4
- package/scripts/admin-merge-ready.sh +545 -0
- package/scripts/admin-merge-ready.test.sh +753 -0
- package/scripts/affected-tests.sh +908 -0
- package/scripts/affected-tests.test.sh +1797 -0
- package/scripts/alpha-metrics.sh +78 -0
- package/scripts/alpha-metrics.test.sh +115 -0
- package/scripts/ancestor-signal-helpers.baseline.txt +16 -0
- package/scripts/audit-flag-flip.sh +107 -0
- package/scripts/check-red-on-main.sh +316 -0
- package/scripts/ci-head-verdict.sh +411 -0
- package/scripts/cloud-detect.sh +193 -0
- package/scripts/delegation-verify.sh +657 -0
- package/scripts/delegation-verify.test.sh +1424 -0
- package/scripts/deploy-arm.sh +475 -0
- package/scripts/domain-model-drift.sh +419 -0
- package/scripts/emit-decision.sh +144 -0
- package/scripts/emit-decision.test.sh +144 -0
- package/scripts/ext-filter-divergence-corpus.txt +15 -0
- package/scripts/ext-filter-divergence.sh +189 -0
- package/scripts/generate-c4-from-components.ts +442 -0
- package/scripts/grok-fidelity-gate.sh +68 -0
- package/scripts/grok-pre-push-gate.sh +232 -0
- package/scripts/harness-discovery-smoke.ts +906 -0
- package/scripts/harness-parity-census.ts +92 -0
- package/scripts/inngest-luks-alert-armed-probe.ts +29 -0
- package/scripts/install-opencode-adapter.ts +509 -0
- package/scripts/lib/affected-tests/common.sh +365 -0
- package/scripts/lib/affected-tests/go.sh +157 -0
- package/scripts/lib/affected-tests/js.sh +188 -0
- package/scripts/lib/affected-tests/nx-turbo.sh +98 -0
- package/scripts/lib/affected-tests/python.sh +185 -0
- package/scripts/lib/context-sanitize.sh +49 -0
- package/scripts/lib/domain-model-lib.sh +180 -0
- package/scripts/lib/ext-filter-grammar.tsv +34 -0
- package/scripts/lib/operator-script.sh +1236 -0
- package/scripts/lib/proc.sh +580 -0
- package/scripts/lib/session-state.sh +748 -0
- package/scripts/lib/tmp-classify.sh +843 -0
- package/scripts/lib/wrapper-boundary.sh +47 -0
- package/scripts/lint-ratchet.py +1547 -0
- package/scripts/monitor-pr-checks.sh +441 -0
- package/scripts/pipeline-tally.sh +756 -0
- package/scripts/pipeline-tally.test.sh +740 -0
- package/scripts/plugin-version.sh +528 -0
- package/scripts/precommit-guard.sh +115 -0
- package/scripts/precommit-guard.test.sh +254 -0
- package/scripts/reconcile-live-heartbeats.ts +705 -0
- package/scripts/render-c4-model.sh +217 -0
- package/scripts/resolve-git-root.sh +55 -0
- package/scripts/resolve-regenerable-conflicts.sh +363 -0
- package/scripts/resolve-regenerable-conflicts.test.sh +780 -0
- package/scripts/ruff-requirements.txt +22 -0
- package/scripts/run-in-pid-namespace.sh +98 -0
- package/scripts/run-in-pid-namespace.test.sh +290 -0
- package/scripts/scan-ancestor-signal-helpers.py +556 -0
- package/scripts/scan-ancestor-signal-helpers.test.sh +670 -0
- package/scripts/setup-ralph-loop.sh +238 -0
- package/scripts/sync-cursor-name-map.ts +316 -0
- package/scripts/sync-grok-agent-compat.ts +188 -0
- package/scripts/sync-npm-package.ts +165 -0
- package/scripts/sync-opencode-adapter.ts +498 -0
- package/scripts/sync-pr-behind.sh +647 -0
- package/scripts/sync-pr-behind.test.sh +252 -0
- package/scripts/taste-profile-update.sh +180 -0
- package/scripts/taste-profile-update.test.sh +167 -0
- package/scripts/write-budget-marker.sh +23 -0
- package/scripts/write-kb-coverage.ts +113 -0
- package/skills/admin-ip-refresh/SKILL.md +81 -0
- package/skills/admin-ip-refresh/references/admin-ip-refresh-procedure.md +201 -0
- package/skills/agent-browser/SKILL.md +698 -0
- package/skills/agent-browser/scripts/playwright-mcp-profile-slot.sh +304 -0
- package/skills/agent-browser/scripts/playwright-mcp-redact-proxy.py +964 -0
- package/skills/agent-browser/scripts/redact-a11y-snapshot.py +443 -0
- package/skills/agent-browser/test/fixtures/capture-playwright-mcp-fixtures.py +217 -0
- package/skills/agent-browser/test/fixtures/fake-passthrough-proxy.py +24 -0
- package/skills/agent-browser/test/fixtures/fake-playwright-mcp.py +131 -0
- package/skills/agent-browser/test/fixtures/make-proxy-mutant.py +48 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/dialog-link-injection.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/empty-content.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-and-result.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-code-bool.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-code-string.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-message-tree.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-no-data.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-string.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-with-data.json +9 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/evaluate-escaped-object.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/invalid-utf8.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/link-result.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/list-two-results.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/meta-in-result.json +13 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/no-content.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/quoted-key.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/resource-block.json +18 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/result-and-method.json +11 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/result-string.json +3 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped-one-row.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/structured-content.json +13 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/tools-list-no-tools.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/tree-no-credential.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/two-text-blocks.json +14 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/unparsable-line.json +3 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-brace.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-link-substring.json +10 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/close.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/evaluate.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/find.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/framing.json +6 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/initialize.json +14 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate-link.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/roots-list-request.json +5 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/screenshot.json +17 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-filename.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-json.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-raw.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/tools-list.json +827 -0
- package/skills/agent-browser/test/fixtures/proxy-session-driver.py +138 -0
- package/skills/agent-browser/test/fixtures/session-facts.py +102 -0
- package/skills/agent-browser/test/playwright-mcp-lifetime.test.sh +1620 -0
- package/skills/agent-browser/test/playwright-mcp-redact-proxy.test.sh +1599 -0
- package/skills/agent-browser/test/redact-a11y-snapshot.test.sh +527 -0
- package/skills/agent-native-architecture/SKILL.md +479 -0
- package/skills/agent-native-architecture/references/action-parity-discipline.md +430 -0
- package/skills/agent-native-architecture/references/agent-execution-patterns.md +483 -0
- package/skills/agent-native-architecture/references/agent-native-testing.md +605 -0
- package/skills/agent-native-architecture/references/architecture-patterns.md +493 -0
- package/skills/agent-native-architecture/references/dynamic-context-injection.md +358 -0
- package/skills/agent-native-architecture/references/files-universal-interface.md +314 -0
- package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +381 -0
- package/skills/agent-native-architecture/references/mcp-tool-design.md +524 -0
- package/skills/agent-native-architecture/references/mobile-patterns.md +897 -0
- package/skills/agent-native-architecture/references/product-implications.md +478 -0
- package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +342 -0
- package/skills/agent-native-architecture/references/self-modification.md +294 -0
- package/skills/agent-native-architecture/references/shared-workspace-architecture.md +700 -0
- package/skills/agent-native-architecture/references/system-prompt-design.md +263 -0
- package/skills/agent-native-audit/SKILL.md +300 -0
- package/skills/agent-native-audit/workflows/agent-native-audit.workflow.js +423 -0
- package/skills/andrew-kane-gem-writer/SKILL.md +189 -0
- package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/skills/andrew-kane-gem-writer/references/resources.md +126 -0
- package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/skills/architecture/SKILL.md +436 -0
- package/skills/architecture/references/adr-template.md +154 -0
- package/skills/architecture/references/likec4-reference.md +136 -0
- package/skills/architecture/references/nfr-reference.md +126 -0
- package/skills/archive-kb/SKILL.md +86 -0
- package/skills/archive-kb/scripts/archive-kb.sh +300 -0
- package/skills/archive-kb/test/archive-kb-partial-run.test.sh +276 -0
- package/skills/atdd-developer/SKILL.md +65 -0
- package/skills/brainstorm/SKILL.md +701 -0
- package/skills/brainstorm/references/brainstorm-brand-workshop.md +153 -0
- package/skills/brainstorm/references/brainstorm-domain-config.md +63 -0
- package/skills/brainstorm/references/brainstorm-validation-workshop.md +65 -0
- package/skills/brainstorm/references/ui-surface-terms.md +42 -0
- package/skills/brainstorm-techniques/SKILL.md +231 -0
- package/skills/brainstorm-techniques/references/decision-principles.md +150 -0
- package/skills/brainstorm-techniques/references/phase-boundaries.md +103 -0
- package/skills/brainstorm-techniques/references/standing-delegation.md +407 -0
- package/skills/brainstorm-techniques/references/technical-ruling-delegation.md +174 -0
- package/skills/campaign-calendar/SKILL.md +145 -0
- package/skills/cf-token-scope/SKILL.md +164 -0
- package/skills/cf-token-scope/references/widen-playbook.md +114 -0
- package/skills/cf-token-scope/scripts/cf-token-scope.sh +253 -0
- package/skills/changelog/SKILL.md +142 -0
- package/skills/code-to-prd/SKILL.md +66 -0
- package/skills/code-to-prd/references/banner-template.md +12 -0
- package/skills/code-to-prd/references/prd-template.md +41 -0
- package/skills/code-to-prd/scripts/code-to-prd.sh +634 -0
- package/skills/code-to-prd/test/code-to-prd.test.sh +447 -0
- package/skills/code-to-prd/test/fixture/.env.example +7 -0
- package/skills/code-to-prd/test/fixture/app/about/page.tsx +11 -0
- package/skills/code-to-prd/test/fixture/app/api/health/route.ts +10 -0
- package/skills/code-to-prd/test/fixture/app/page.tsx +14 -0
- package/skills/code-to-prd/test/fixture/next.config.mjs +3 -0
- package/skills/code-to-prd/test/fixture/package.json +6 -0
- package/skills/cohort-status/SKILL.md +62 -0
- package/skills/community/SKILL.md +210 -0
- package/skills/community/scripts/bsky-community.sh +644 -0
- package/skills/community/scripts/bsky-setup.sh +631 -0
- package/skills/community/scripts/community-router.sh +93 -0
- package/skills/community/scripts/discord-community.sh +487 -0
- package/skills/community/scripts/discord-setup.sh +784 -0
- package/skills/community/scripts/github-community.sh +706 -0
- package/skills/community/scripts/hn-community.sh +233 -0
- package/skills/community/scripts/lib/hmac-sha1-b64.sh +126 -0
- package/skills/community/scripts/linkedin-community.sh +750 -0
- package/skills/community/scripts/linkedin-setup.sh +758 -0
- package/skills/community/scripts/x-community.sh +890 -0
- package/skills/community/scripts/x-setup.sh +713 -0
- package/skills/community/test/community-argv.test.sh +2922 -0
- package/skills/community/test/github-community.test.sh +981 -0
- package/skills/competitive-analysis/SKILL.md +69 -0
- package/skills/competitive-analysis/references/peer-plugin-audit.md +218 -0
- package/skills/compound/SKILL.md +673 -0
- package/skills/compound/scripts/token-efficiency-report.sh +334 -0
- package/skills/compound/test/phase-16.test.sh +379 -0
- package/skills/compound-capture/SKILL.md +791 -0
- package/skills/compound-capture/assets/critical-pattern-template.md +37 -0
- package/skills/compound-capture/assets/resolution-template.md +116 -0
- package/skills/compound-capture/references/yaml-schema.md +67 -0
- package/skills/compound-capture/schema.yaml +181 -0
- package/skills/constraint-scaffold/SKILL.md +175 -0
- package/skills/constraint-scaffold/references/boundary-readme.template +35 -0
- package/skills/constraint-scaffold/references/constraint-gates-workflow.template +69 -0
- package/skills/constraint-scaffold/references/depcruise-config.template +230 -0
- package/skills/constraint-scaffold/references/fix-constraints-stage-a.template +240 -0
- package/skills/constraint-scaffold/references/fix-constraints-stage-b.template +313 -0
- package/skills/constraint-scaffold/references/shared-runner.template +135 -0
- package/skills/constraint-scaffold/scripts/constraint-scaffold.sh +589 -0
- package/skills/constraint-scaffold/test/bite-proof.test.sh +1338 -0
- package/skills/constraint-scaffold/test/boundary.test.sh +734 -0
- package/skills/constraint-scaffold/test/emit-fix-constraints.test.sh +193 -0
- package/skills/constraint-scaffold/test/generator.test.sh +93 -0
- package/skills/constraint-scaffold/test/parity.test.sh +285 -0
- package/skills/content-writer/SKILL.md +242 -0
- package/skills/content-writer/scripts/blog-jargon-scan.sh +42 -0
- package/skills/cron-delete/SKILL.md +56 -0
- package/skills/cron-list/SKILL.md +54 -0
- package/skills/deepen-plan/SKILL.md +937 -0
- package/skills/deepen-plan/workflows/deepen-plan.workflow.js +488 -0
- package/skills/deploy/SKILL.md +115 -0
- package/skills/deploy/references/hetzner-setup.md +67 -0
- package/skills/deploy/scripts/deploy.sh +37 -0
- package/skills/deploy-docs/SKILL.md +95 -0
- package/skills/dhh-rails-style/SKILL.md +203 -0
- package/skills/dhh-rails-style/references/architecture.md +717 -0
- package/skills/dhh-rails-style/references/controllers.md +323 -0
- package/skills/dhh-rails-style/references/frontend.md +556 -0
- package/skills/dhh-rails-style/references/gems.md +316 -0
- package/skills/dhh-rails-style/references/models.md +388 -0
- package/skills/dhh-rails-style/references/testing.md +354 -0
- package/skills/discord-content/SKILL.md +126 -0
- package/skills/docs-site/SKILL.md +214 -0
- package/skills/drain-labeled-backlog/SKILL.md +203 -0
- package/skills/drain-labeled-backlog/scripts/group-by-area.sh +208 -0
- package/skills/drain-labeled-backlog/workflows/drain-labeled-backlog.workflow.js +433 -0
- package/skills/drain-prs/SKILL.md +143 -0
- package/skills/drain-prs/scripts/triage-prs.sh +192 -0
- package/skills/drain-prs/test/argv-ceiling.test.sh +183 -0
- package/skills/dspy-ruby/SKILL.md +634 -0
- package/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/skills/dspy-ruby/references/core-concepts.md +268 -0
- package/skills/dspy-ruby/references/optimization.md +623 -0
- package/skills/dspy-ruby/references/providers.md +335 -0
- package/skills/eval-harness/README.md +194 -0
- package/skills/eval-harness/SKILL.md +157 -0
- package/skills/eval-harness/enums/go-routes.json +13 -0
- package/skills/eval-harness/enums/incident-threshold.json +5 -0
- package/skills/eval-harness/enums/lane.json +5 -0
- package/skills/eval-harness/enums/tool-selection.json +20 -0
- package/skills/eval-harness/enums/triage-levels.json +5 -0
- package/skills/eval-harness/gated-skills.json +34 -0
- package/skills/eval-harness/models.generated.json +5 -0
- package/skills/eval-harness/promptfooconfig-go-routing.yaml +35 -0
- package/skills/eval-harness/promptfooconfig-incident-threshold.yaml +32 -0
- package/skills/eval-harness/promptfooconfig-lane-inference.yaml +33 -0
- package/skills/eval-harness/promptfooconfig-ticket-triage.yaml +34 -0
- package/skills/eval-harness/promptfooconfig-tool-selection.yaml +33 -0
- package/skills/eval-harness/prompts/go-baseline.txt +6 -0
- package/skills/eval-harness/prompts/go-skill.txt +21 -0
- package/skills/eval-harness/prompts/incident-threshold-baseline.txt +6 -0
- package/skills/eval-harness/prompts/incident-threshold-skill.txt +12 -0
- package/skills/eval-harness/prompts/lane-inference-baseline.txt +6 -0
- package/skills/eval-harness/prompts/lane-inference-skill.txt +15 -0
- package/skills/eval-harness/prompts/tool-selection-baseline.txt +19 -0
- package/skills/eval-harness/prompts/tool-selection-skill.txt +22 -0
- package/skills/eval-harness/prompts/triage-baseline.txt +7 -0
- package/skills/eval-harness/prompts/triage-skill.txt +10 -0
- package/skills/eval-harness/scripts/eval-gate.cjs +318 -0
- package/skills/eval-harness/scripts/extract-block.cjs +87 -0
- package/skills/eval-harness/scripts/gate-classification.cjs +28 -0
- package/skills/eval-harness/scripts/gen-models.sh +41 -0
- package/skills/eval-harness/scripts/gen-skill-prompt.cjs +177 -0
- package/skills/eval-harness/scripts/measure-classification.cjs +30 -0
- package/skills/eval-harness/scripts/parse-label.cjs +93 -0
- package/skills/eval-harness/scripts/verdict.cjs +132 -0
- package/skills/eval-harness/tasks/go-routing.jsonl +8 -0
- package/skills/eval-harness/tasks/incident-threshold.jsonl +7 -0
- package/skills/eval-harness/tasks/lane-inference.jsonl +7 -0
- package/skills/eval-harness/tasks/ticket-triage.jsonl +6 -0
- package/skills/eval-harness/tasks/tool-selection.jsonl +15 -0
- package/skills/eval-harness/test/eval-gate.test.sh +132 -0
- package/skills/eval-harness/test/extract-block.test.sh +87 -0
- package/skills/eval-harness/test/gate-classification.test.sh +77 -0
- package/skills/eval-harness/test/gen-models.test.sh +75 -0
- package/skills/eval-harness/test/measure-classification.test.sh +74 -0
- package/skills/eval-harness/test/parse-label.test.sh +129 -0
- package/skills/eval-harness/test/registry-completeness.test.sh +109 -0
- package/skills/eval-harness/test/verdict.test.sh +123 -0
- package/skills/every-style-editor/SKILL.md +142 -0
- package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/skills/feature-tweet/SKILL.md +199 -0
- package/skills/feature-video/SKILL.md +412 -0
- package/skills/feature-video/scripts/check_deps.sh +281 -0
- package/skills/file-todos/SKILL.md +267 -0
- package/skills/file-todos/assets/todo-template.md +166 -0
- package/skills/fix-issue/SKILL.md +278 -0
- package/skills/fix-issue/references/agent-authored-exclusion.md +120 -0
- package/skills/fix-issue/references/exclude-label-jq-snippet.md +97 -0
- package/skills/flag-bootstrap/SETUP.md +142 -0
- package/skills/flag-create/SKILL.md +145 -0
- package/skills/flag-create/scripts/create.sh +273 -0
- package/skills/flag-delete/SKILL.md +167 -0
- package/skills/flag-delete/scripts/delete.sh +288 -0
- package/skills/flag-list/SKILL.md +99 -0
- package/skills/flag-list/scripts/list.sh +275 -0
- package/skills/flag-set-role/SKILL.md +229 -0
- package/skills/flag-set-role/scripts/flip.sh +774 -0
- package/skills/frontend-anti-slop/SKILL.md +106 -0
- package/skills/frontend-anti-slop/references/anti-patterns.md +199 -0
- package/skills/frontend-anti-slop/references/slop-rules.md +65 -0
- package/skills/frontend-anti-slop/scripts/tier1-scan.ts +525 -0
- package/skills/frontend-design/SKILL.md +77 -0
- package/skills/gdpr-gate/NOTICE +136 -0
- package/skills/gdpr-gate/SKILL.md +341 -0
- package/skills/gdpr-gate/references/fields.md +160 -0
- package/skills/gdpr-gate/references/layers/api-layer.md +355 -0
- package/skills/gdpr-gate/references/layers/auth-sessions.md +290 -0
- package/skills/gdpr-gate/references/layers/data-in-transit.md +331 -0
- package/skills/gdpr-gate/references/layers/data-lifecycle.md +341 -0
- package/skills/gdpr-gate/references/layers/frontend.md +249 -0
- package/skills/gdpr-gate/references/layers/testing-seeding.md +256 -0
- package/skills/gdpr-gate/references/leakage-vectors.md +248 -0
- package/skills/gdpr-gate/references/legacy/legal-consent-v1-prose.md +48 -0
- package/skills/gdpr-gate/references/legal-consent.md +140 -0
- package/skills/gdpr-gate/references/non-negotiables.md +60 -0
- package/skills/gdpr-gate/scripts/gdpr-gate.sh +271 -0
- package/skills/gdpr-gate/scripts/notice-frontmatter.sh +337 -0
- package/skills/gdpr-gate/scripts/path-denylist.txt +39 -0
- package/skills/gdpr-gate/scripts/repo-scan.sh +158 -0
- package/skills/gdpr-gate/scripts/vendor-drift-classify.sh +120 -0
- package/skills/gdpr-gate/scripts/vendor-pin-integrity.sh +237 -0
- package/skills/gemini-imagegen/SKILL.md +283 -0
- package/skills/gemini-imagegen/requirements.txt +5 -0
- package/skills/gemini-imagegen/scripts/_error_handling.py +146 -0
- package/skills/gemini-imagegen/scripts/compose_images.py +150 -0
- package/skills/gemini-imagegen/scripts/edit_image.py +137 -0
- package/skills/gemini-imagegen/scripts/gemini_images.py +256 -0
- package/skills/gemini-imagegen/scripts/generate_image.py +147 -0
- package/skills/gemini-imagegen/scripts/multi_turn_chat.py +223 -0
- package/skills/gemini-imagegen/scripts/test_error_handling.py +179 -0
- package/skills/git-worktree/SKILL.md +534 -0
- package/skills/git-worktree/scripts/git-repo-readiness-diag.sh +98 -0
- package/skills/git-worktree/scripts/worktree-manager.sh +4773 -0
- package/skills/git-worktree/test/create-from-origin-main.test.sh +116 -0
- package/skills/git-worktree/test/lease-protects-active.test.sh +1056 -0
- package/skills/git-worktree/test/no-repo-fail-loud.test.sh +88 -0
- package/skills/git-worktree/test/orphan-reaper-honest-count.test.sh +379 -0
- package/skills/git-worktree/test/reap-archive-persistence.test.sh +725 -0
- package/skills/git-worktree/test/stale-lock-sweep.test.sh +176 -0
- package/skills/go/SKILL.md +54 -0
- package/skills/growth/SKILL.md +164 -0
- package/skills/harvest-debt/SKILL.md +78 -0
- package/skills/harvest-debt/scripts/harvest-debt.sh +114 -0
- package/skills/heal-skill/SKILL.md +173 -0
- package/skills/help/SKILL.md +46 -0
- package/skills/incident/NOTICE +39 -0
- package/skills/incident/SKILL.md +395 -0
- package/skills/incident/scripts/dry-run.sh +448 -0
- package/skills/incident/scripts/redact-engine.py +412 -0
- package/skills/incident/scripts/redact-sentinel.sh +44 -0
- package/skills/incident/templates/pir.md +139 -0
- package/skills/incident/test/fixtures/dry-run-incident.json +26 -0
- package/skills/incident/test/fixtures/dry-run-secret-leak.json +26 -0
- package/skills/incident/test/fixtures/legacy-bash-scanner.sh +82 -0
- package/skills/incident/test/fixtures/positive-corpus.md +87 -0
- package/skills/incident/test/redact-sentinel.test.sh +1420 -0
- package/skills/invoice/SKILL.md +206 -0
- package/skills/kb-glossary/SKILL.md +71 -0
- package/skills/kb-glossary/references/glossary-format.md +111 -0
- package/skills/kb-glossary/references/rejected-request-register.md +115 -0
- package/skills/kb-search/SKILL.md +276 -0
- package/skills/kb-search/scripts/kb-search-cache.sh +123 -0
- package/skills/legal-audit/SKILL.md +161 -0
- package/skills/legal-generate/NOTICE +242 -0
- package/skills/legal-generate/SKILL.md +244 -0
- package/skills/legal-generate/references/templates/advisor-agreement/template.md +12 -0
- package/skills/legal-generate/references/templates/business-associate-agreement/template.md +112 -0
- package/skills/legal-generate/references/templates/cookie-notice/template.md +82 -0
- package/skills/legal-generate/references/templates/dpa-global/template.md +439 -0
- package/skills/legal-generate/references/templates/dpa-us/template.md +222 -0
- package/skills/legal-generate/references/templates/employee-offer-letter/template.md +75 -0
- package/skills/legal-generate/references/templates/master-services-agreement/template.md +252 -0
- package/skills/legal-generate/references/templates/mutual-nda/template.md +66 -0
- package/skills/legal-generate/references/templates/one-way-nda/template.md +38 -0
- package/skills/legal-generate/references/templates/privacy-policy-gdpr/template.md +427 -0
- package/skills/legal-generate/references/templates/privacy-policy-us/template.md +313 -0
- package/skills/legal-generate/references/templates/terms-of-use/template.md +75 -0
- package/skills/legal-generate/scripts/strip-vendor-credit.sh +96 -0
- package/skills/linear-fetch/SKILL.md +247 -0
- package/skills/linear-fetch/scripts/assert-no-linear-telemetry.sh +66 -0
- package/skills/linear-fetch/scripts/redact-linear-urls.sh +51 -0
- package/skills/linear-fetch/scripts/render-caller-template.sh +36 -0
- package/skills/linear-fetch/test/assert-no-linear-telemetry.test.sh +113 -0
- package/skills/linear-fetch/test/parity.test.sh +112 -0
- package/skills/linear-fetch/test/persist-safe-integration.test.sh +191 -0
- package/skills/linear-fetch/test/redact-linear-urls.test.sh +226 -0
- package/skills/merge-pr/SKILL.md +670 -0
- package/skills/model-launch-review/SKILL.md +255 -0
- package/skills/model-launch-review/scripts/audit-models.sh +501 -0
- package/skills/one-shot/SKILL.md +316 -0
- package/skills/one-shot/references/token-discipline.md +74 -0
- package/skills/operator-bootstrap/SKILL.md +412 -0
- package/skills/operator-bootstrap/template.sh +439 -0
- package/skills/operator-digest/SKILL.md +287 -0
- package/skills/operator-digest/assets/operator-digest.workflow.yml +175 -0
- package/skills/operator-digest/scripts/digest-scrub.sh +119 -0
- package/skills/operator-digest/scripts/provision-operator-digest-repo.sh +147 -0
- package/skills/operator-rephrase/SKILL.md +98 -0
- package/skills/pencil-setup/SKILL.md +201 -0
- package/skills/pencil-setup/scripts/check_deps.sh +556 -0
- package/skills/pencil-setup/scripts/copy_adapter.sh +80 -0
- package/skills/pencil-setup/scripts/package-lock.json +1172 -0
- package/skills/pencil-setup/scripts/package.json +14 -0
- package/skills/pencil-setup/scripts/pencil-error-enrichment.mjs +21 -0
- package/skills/pencil-setup/scripts/pencil-mcp-adapter.mjs +733 -0
- package/skills/pencil-setup/scripts/pencil-response-classify.mjs +38 -0
- package/skills/pencil-setup/scripts/pencil-save-gate.mjs +26 -0
- package/skills/pencil-setup/scripts/sanitize-filename.mjs +25 -0
- package/skills/plan/SKILL.md +1140 -0
- package/skills/plan/references/differential-migration-testing.md +185 -0
- package/skills/plan/references/plan-community-discovery.md +42 -0
- package/skills/plan/references/plan-founder-check.md +137 -0
- package/skills/plan/references/plan-functional-overlap.md +25 -0
- package/skills/plan/references/plan-host-fanout.md +51 -0
- package/skills/plan/references/plan-issue-templates.md +779 -0
- package/skills/plan/references/plan-network-outage-checklist.md +131 -0
- package/skills/plan/references/plan-scope-check.md +126 -0
- package/skills/plan/references/plan-sharp-edges.md +331 -0
- package/skills/plan/references/plan-signoff-lifecycle.md +12 -0
- package/skills/plan/references/webapp-cwv-observability.md +143 -0
- package/skills/plan-review/SKILL.md +91 -0
- package/skills/plan-review/lib/named-panel.mjs +58 -0
- package/skills/plan-review/workflows/plan-review.workflow.js +477 -0
- package/skills/postmerge/SKILL.md +642 -0
- package/skills/postmerge/references/deploy-status-debugging.md +141 -0
- package/skills/postmerge/references/graceful-degradation.md +20 -0
- package/skills/postmerge/references/sentry-error-count-delta.md +137 -0
- package/skills/preflight/SKILL.md +1553 -0
- package/skills/preflight/references/check-13-founder-check.md +286 -0
- package/skills/preflight/scripts/founder-check.py +1640 -0
- package/skills/preflight/scripts/parse-form-a.awk +107 -0
- package/skills/preflight/scripts/probe-verb-gate.sh +108 -0
- package/skills/product-roadmap/SKILL.md +357 -0
- package/skills/product-roadmap/scripts/roadmap-reconcile.sh +327 -0
- package/skills/provision-cloudflare/SKILL.md +67 -0
- package/skills/provision-cloudflare/scripts/provision-cloudflare.sh +271 -0
- package/skills/provision-doppler/SKILL.md +58 -0
- package/skills/provision-doppler/scripts/provision-doppler.sh +358 -0
- package/skills/provision-github/SKILL.md +59 -0
- package/skills/provision-github/scripts/provision-github.sh +300 -0
- package/skills/provision-hetzner/SKILL.md +76 -0
- package/skills/provision-hetzner/scripts/provision-hetzner.sh +262 -0
- package/skills/provision-hetzner/test/fixture/knowledge-base/legal/tenant-dpa-register.md +26 -0
- package/skills/provision-hetzner/test/fixture/provision-hetzner-dry-run.golden +34 -0
- package/skills/provision-hetzner/test/provision-hetzner-characterization.test.sh +227 -0
- package/skills/qa/SKILL.md +263 -0
- package/skills/qa/references/browser-preflight.md +22 -0
- package/skills/questionnaire-generate/SKILL.md +328 -0
- package/skills/questionnaire-generate/references/questionnaire.template +60 -0
- package/skills/rclone/SKILL.md +162 -0
- package/skills/rclone/scripts/check_setup.sh +63 -0
- package/skills/release-announce/SKILL.md +65 -0
- package/skills/release-docs/SKILL.md +92 -0
- package/skills/reproduce-bug/SKILL.md +358 -0
- package/skills/resolve-debt/SKILL.md +85 -0
- package/skills/resolve-debt/scripts/resolve-debt.py +584 -0
- package/skills/resolve-parallel/SKILL.md +45 -0
- package/skills/resolve-parallel/workflows/resolve-parallel.workflow.js +415 -0
- package/skills/resolve-pr-parallel/SKILL.md +68 -0
- package/skills/resolve-pr-parallel/workflows/resolve-pr-parallel.workflow.js +419 -0
- package/skills/resolve-todo-parallel/SKILL.md +57 -0
- package/skills/resolve-todo-parallel/workflows/resolve-todo-parallel.workflow.js +398 -0
- package/skills/review/SKILL.md +1620 -0
- package/skills/review/references/anti-slop-scanner-hook.md +40 -0
- package/skills/review/references/defect-classes-migrations.md +9 -0
- package/skills/review/references/generated-input-oracle-surfaces.md +15 -0
- package/skills/review/references/review-e2e-testing.md +116 -0
- package/skills/review/references/review-evidence-comment.md +36 -0
- package/skills/review/references/review-todo-structure.md +165 -0
- package/skills/review/references/risk-tier-and-fix-rounds.md +97 -0
- package/skills/review/references/semgrep-custom-rules.yaml +174 -0
- package/skills/review/references/ultrathink-phases.md +98 -0
- package/skills/review/references/wfs.md +6 -0
- package/skills/review/scripts/emit-review-trailer.sh +499 -0
- package/skills/review/scripts/ensure-semgrep.sh +99 -0
- package/skills/review/scripts/fix-round-seats.sh +197 -0
- package/skills/review/test/emit-review-trailer.test.sh +387 -0
- package/skills/review/test/ensure-semgrep.test.sh +111 -0
- package/skills/review/test/fix-round-seats.test.sh +237 -0
- package/skills/review/workflows/README.md +147 -0
- package/skills/review/workflows/review.workflow.js +667 -0
- package/skills/schedule/SKILL.md +796 -0
- package/skills/seo-aeo/SKILL.md +139 -0
- package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
- package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
- package/skills/ship/SKILL.md +3023 -0
- package/skills/ship/references/adr-ordinal-collision.md +47 -0
- package/skills/ship/references/affected-gate-contract.md +115 -0
- package/skills/ship/references/ci-run-diagnosis.md +29 -0
- package/skills/ship/references/ci-workflow-authoring.md +68 -0
- package/skills/ship/references/cleanup-merged-archival.md +39 -0
- package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
- package/skills/ship/references/cto-ruling-armed-push.md +5 -0
- package/skills/ship/references/decision-challenges-render.md +71 -0
- package/skills/ship/references/delegated-act-authoring-form.md +7 -0
- package/skills/ship/references/detached-battery-verdict.md +13 -0
- package/skills/ship/references/epic-close-sweep.md +62 -0
- package/skills/ship/references/feature-tweet-draft.md +42 -0
- package/skills/ship/references/followthrough-issue-body-template.md +47 -0
- package/skills/ship/references/followthrough-stub-template.sh +92 -0
- package/skills/ship/references/merge-queue-dequeue.md +39 -0
- package/skills/ship/references/operator-holds.md +179 -0
- package/skills/ship/references/pipeline-tally-render.md +43 -0
- package/skills/ship/references/pr-overlap.md +153 -0
- package/skills/ship/references/queue-mode.md +7 -0
- package/skills/ship/references/ready-run-wait.md +36 -0
- package/skills/ship/references/red-on-main-quarantine.md +110 -0
- package/skills/ship/references/register-update-pr-pattern.md +88 -0
- package/skills/ship/references/settle-then-admin-merge.md +130 -0
- package/skills/ship/references/supabase-column-probe.md +13 -0
- package/skills/ship/scripts/auto-close-scan.sh +76 -0
- package/skills/ship/scripts/battery-owed.sh +403 -0
- package/skills/ship/scripts/hold-gate-eval.sh +410 -0
- package/skills/ship/scripts/net-issue-flow.sh +665 -0
- package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
- package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
- package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
- package/skills/skill-creator/SKILL.md +299 -0
- package/skills/skill-creator/references/api-security.md +248 -0
- package/skills/skill-creator/references/authoring-levers.md +110 -0
- package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
- package/skills/skill-creator/references/best-practices.md +431 -0
- package/skills/skill-creator/references/common-patterns.md +639 -0
- package/skills/skill-creator/references/core-principles.md +193 -0
- package/skills/skill-creator/references/executable-code.md +188 -0
- package/skills/skill-creator/references/iteration-and-testing.md +509 -0
- package/skills/skill-creator/references/official-spec.md +193 -0
- package/skills/skill-creator/references/recommended-structure.md +180 -0
- package/skills/skill-creator/references/skill-structure.md +396 -0
- package/skills/skill-creator/references/using-scripts.md +122 -0
- package/skills/skill-creator/references/using-templates.md +120 -0
- package/skills/skill-creator/references/workflows-and-validation.md +541 -0
- package/skills/skill-creator/scripts/init_skill.py +322 -0
- package/skills/skill-creator/scripts/package_skill.py +110 -0
- package/skills/skill-creator/scripts/quick_validate.py +64 -0
- package/skills/skill-creator/templates/router-skill.md +73 -0
- package/skills/skill-creator/templates/simple-skill.md +34 -0
- package/skills/skill-creator/workflows/add-reference.md +96 -0
- package/skills/skill-creator/workflows/add-script.md +104 -0
- package/skills/skill-creator/workflows/add-template.md +83 -0
- package/skills/skill-creator/workflows/add-workflow.md +128 -0
- package/skills/skill-creator/workflows/audit-skill.md +159 -0
- package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
- package/skills/skill-creator/workflows/create-new-skill.md +207 -0
- package/skills/skill-creator/workflows/get-guidance.md +134 -0
- package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
- package/skills/skill-creator/workflows/verify-skill.md +226 -0
- package/skills/skill-security-scan/SKILL.md +145 -0
- package/skills/skill-security-scan/references/disclaimer.md +21 -0
- package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
- package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
- package/skills/skill-security-scan/references/override-mechanism.md +171 -0
- package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
- package/skills/skill-security-scan/references/regex-patterns.md +136 -0
- package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
- package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
- package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
- package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
- package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
- package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
- package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
- package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
- package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
- package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
- package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
- package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
- package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
- package/skills/skill-security-scan/scripts/lib.sh +197 -0
- package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
- package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
- package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
- package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
- package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
- package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
- package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
- package/skills/social-distribute/SKILL.md +485 -0
- package/skills/spec-templates/SKILL.md +274 -0
- package/skills/sync/SKILL.md +49 -0
- package/skills/test-browser/SKILL.md +464 -0
- package/skills/test-browser/references/feature-map.md +161 -0
- package/skills/test-fix-loop/SKILL.md +159 -0
- package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
- package/skills/triage/SKILL.md +402 -0
- package/skills/trigger-cron/SKILL.md +136 -0
- package/skills/trigger-cron/scripts/trigger.sh +198 -0
- package/skills/user-set-role/SKILL.md +147 -0
- package/skills/user-set-role/scripts/set-role.sh +218 -0
- package/skills/user-story-writer/SKILL.md +77 -0
- package/skills/ux-audit/SKILL.md +251 -0
- package/skills/ux-audit/references/finding.schema.json +62 -0
- package/skills/ux-audit/references/route-list.yaml +95 -0
- package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
- package/skills/ux-audit/scripts/bot-signin.ts +135 -0
- package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
- package/skills/verification-scaffold/SKILL.md +73 -0
- package/skills/verification-scaffold/references/gitleaks.toml.template +13 -0
- package/skills/verification-scaffold/references/guardrail-audit.md +156 -0
- package/skills/verification-scaffold/references/guards/block-stash-hook.sh.template +72 -0
- package/skills/verification-scaffold/references/guards/commit-on-main-hook.sh.template +82 -0
- package/skills/verification-scaffold/references/guards/orphan-test-check.sh.template +102 -0
- package/skills/verification-scaffold/references/guards/precommit-guard.sh.template +115 -0
- package/skills/verification-scaffold/references/guards/secret-scan-hook.sh.template +176 -0
- package/skills/verification-scaffold/references/guards/verification-gates.yml.template +49 -0
- package/skills/verification-scaffold/references/guards/wrapper-boundary.sh.template +47 -0
- package/skills/verification-scaffold/references/guards.tsv +21 -0
- package/skills/verification-scaffold/references/verification-readme.template +55 -0
- package/skills/verification-scaffold/scripts/verification-scaffold.sh +785 -0
- package/skills/verification-scaffold/test/bite-proof.test.sh +159 -0
- package/skills/verification-scaffold/test/generator.test.sh +319 -0
- package/skills/verification-scaffold/test/manifest.test.sh +144 -0
- package/skills/work/SKILL.md +1480 -0
- package/skills/work/references/end-of-work-resume.md +7 -0
- package/skills/work/references/hook-test-main-cwd.md +24 -0
- package/skills/work/references/key-principles.md +83 -0
- package/skills/work/references/property-testing.md +122 -0
- package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
- package/skills/work/references/work-agent-teams.md +97 -0
- package/skills/work/references/work-lifecycle-parallel.md +147 -0
- package/skills/work/references/work-lockfile-bumps.md +97 -0
- package/skills/work/references/work-scratch-sandboxes.md +212 -0
- package/skills/work/references/work-subagent-fanout.md +61 -0
- package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
- 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
|