@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,785 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verification-scaffold — deterministic generator for the generic guardrail set
|
|
3
|
+
# (#10404). Iterates references/guards.tsv (tier=v1) and emits the selected
|
|
4
|
+
# guards into a user repo: PreToolUse hooks + a .claude/settings.json merge +
|
|
5
|
+
# scripts/verification/ helpers + a repo-root .github/workflows/verification-
|
|
6
|
+
# gates.yml, advisory-first by default. A TMPDIR-detached-worktree bite-proof
|
|
7
|
+
# proves each installed guard pass -> fail -> pass before the doc block is
|
|
8
|
+
# written.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# verification-scaffold.sh [--target PATH] [--guards a,b | --all]
|
|
12
|
+
# [--mode advisory|enforce] [--dry-run] [--list]
|
|
13
|
+
# [--uninstall GUARD] [--headless]
|
|
14
|
+
#
|
|
15
|
+
# Defaults: --target $PWD, --guards = all v1 rows, --mode advisory, interactive
|
|
16
|
+
# is a no-op (--headless only declares the intent — the script never prompts).
|
|
17
|
+
#
|
|
18
|
+
# Exit matrix (every non-zero is a hard, fail-closed stop; every message prints
|
|
19
|
+
# on STDOUT first — agent runtimes surface stdout and swallow stderr):
|
|
20
|
+
# 0 success (incl. --uninstall of a never-installed guard: prints, exits 0)
|
|
21
|
+
# 64 usage error (unknown flag, unknown/v2 guard, --mode bogus)
|
|
22
|
+
# 65 precondition: --target is not inside a git work tree
|
|
23
|
+
# 66 refuse-if-exists (an emitted artifact already present; NO --force)
|
|
24
|
+
# 69 TMPDIR resolves inside the target, neither node nor jq resolves, the
|
|
25
|
+
# existing .claude/settings.json is malformed, a git error, or the run
|
|
26
|
+
# lock is held (<git-common-dir>/verification-scaffold.lock, pid inside)
|
|
27
|
+
# 71 bite-proof: an installed guard did not pass on the clean staged tree
|
|
28
|
+
# 72 bite-proof: an installed guard did NOT decide on the injected probe
|
|
29
|
+
# 73 bite-proof: an installed guard did not return to green after the probe
|
|
30
|
+
#
|
|
31
|
+
# Self-cleanup: armed BEFORE the first write, disarmed after the bite passes —
|
|
32
|
+
# every failure in between removes exactly the artifacts THIS run emitted
|
|
33
|
+
# (tracked in $WORK/emitted + the settings.json backup), so a failed install is
|
|
34
|
+
# re-runnable and never leaves a half-installed guard set. The doc block is
|
|
35
|
+
# written last; a doc failure warns only.
|
|
36
|
+
#
|
|
37
|
+
# Portable: POSIX tools only (no realpath, no `sed -i`, timeout->gtimeout->bare
|
|
38
|
+
# fallback); runs on founder hosts including stock macOS.
|
|
39
|
+
set -euo pipefail
|
|
40
|
+
|
|
41
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
42
|
+
SKILL_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
43
|
+
REF_DIR="$SKILL_DIR/references"
|
|
44
|
+
GUARDS_TSV="$REF_DIR/guards.tsv"
|
|
45
|
+
|
|
46
|
+
TAG="verification-scaffold"
|
|
47
|
+
log() { printf '%s: %s\n' "$TAG" "$*" >&2; }
|
|
48
|
+
say() { printf '%s: %s\n' "$TAG" "$*"; }
|
|
49
|
+
warn() { printf '%s: WARNING: %s\n' "$TAG" "$*"; }
|
|
50
|
+
die() { say "FAILED (${2:-1}): $1"; log "$1"; exit "${2:-1}"; }
|
|
51
|
+
|
|
52
|
+
# physdir <dir>: physical path of an EXISTING directory (pwd -P — no realpath on
|
|
53
|
+
# stock macOS). Prints nothing when the dir is missing.
|
|
54
|
+
physdir() { ( cd -- "$1" 2>/dev/null && pwd -P ); }
|
|
55
|
+
|
|
56
|
+
# --- guards.tsv ---------------------------------------------------------------
|
|
57
|
+
# Row: name \t verdict \t surfaces \t templates \t default_mode \t deps \t
|
|
58
|
+
# source_files \t emits \t tier. Read once into parallel scalars via per-row
|
|
59
|
+
# helpers (small fixed set — a line-scan per lookup is fine and keeps bash 3.2
|
|
60
|
+
# compatibility: no associative arrays).
|
|
61
|
+
_guard_field() { # _guard_field <name> <col> -> field value (empty when absent)
|
|
62
|
+
awk -F'\t' -v n="$1" -v c="$2" '$1 == n { print $c; exit }' "$GUARDS_TSV"
|
|
63
|
+
}
|
|
64
|
+
v1_guards() { awk -F'\t' '$1 !~ /^#/ && $1 != "name" && $9 == "v1" { print $1 }' "$GUARDS_TSV"; }
|
|
65
|
+
|
|
66
|
+
# --- args ---------------------------------------------------------------------
|
|
67
|
+
TARGET="$PWD"
|
|
68
|
+
GUARDS_ARG=""
|
|
69
|
+
MODE="advisory"
|
|
70
|
+
DRY_RUN=0
|
|
71
|
+
DO_LIST=0
|
|
72
|
+
UNINSTALL=""
|
|
73
|
+
HEADLESS=0
|
|
74
|
+
while [[ $# -gt 0 ]]; do
|
|
75
|
+
case "$1" in
|
|
76
|
+
--target) TARGET="${2:-}"; shift 2 ;;
|
|
77
|
+
--guards) GUARDS_ARG="${2:-}"; shift 2 ;;
|
|
78
|
+
--all) GUARDS_ARG=""; shift ;;
|
|
79
|
+
--mode) MODE="${2:-}"; shift 2 ;;
|
|
80
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
81
|
+
--list) DO_LIST=1; shift ;;
|
|
82
|
+
--uninstall) UNINSTALL="${2:-}"; shift 2 ;;
|
|
83
|
+
--headless) HEADLESS=1; shift ;;
|
|
84
|
+
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
|
|
85
|
+
*) die "unknown argument: $1" 64 ;;
|
|
86
|
+
esac
|
|
87
|
+
done
|
|
88
|
+
case "$MODE" in advisory|enforce) ;; *) die "--mode must be advisory|enforce, got: $MODE" 64 ;; esac
|
|
89
|
+
|
|
90
|
+
# Resolve the guard set.
|
|
91
|
+
if [[ -n "$GUARDS_ARG" ]]; then
|
|
92
|
+
GUARDS="$(printf '%s' "$GUARDS_ARG" | tr ',' ' ')"
|
|
93
|
+
else
|
|
94
|
+
GUARDS="$(v1_guards | tr '\n' ' ')"
|
|
95
|
+
fi
|
|
96
|
+
GUARDS="${GUARDS% }"
|
|
97
|
+
|
|
98
|
+
for g in $GUARDS; do
|
|
99
|
+
tier="$(_guard_field "$g" 9)"
|
|
100
|
+
[[ -n "$tier" ]] || die "unknown guard: $g (run --list)" 64
|
|
101
|
+
[[ "$tier" == "v1" ]] || die "guard $g is a v2 candidate only — not emittable" 64
|
|
102
|
+
done
|
|
103
|
+
[[ -n "$UNINSTALL" ]] && { tier="$(_guard_field "$UNINSTALL" 9)"; [[ -n "$tier" ]] || die "unknown guard: $UNINSTALL" 64; }
|
|
104
|
+
|
|
105
|
+
# Target must resolve to a git work tree BEFORE anything else (SpecFlow #2).
|
|
106
|
+
TARGET="$(physdir "$TARGET")" || TARGET=""
|
|
107
|
+
[[ -n "$TARGET" ]] || die "--target does not resolve to a directory" 65
|
|
108
|
+
if [[ "$DO_LIST" -eq 0 ]]; then
|
|
109
|
+
git -C "$TARGET" rev-parse --is-inside-work-tree >/dev/null 2>&1 \
|
|
110
|
+
|| die "target $TARGET is not inside a git work tree" 65
|
|
111
|
+
TARGET="$(physdir "$(git -C "$TARGET" rev-parse --show-toplevel)")"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# --- --list ---------------------------------------------------------------------
|
|
115
|
+
if [[ "$DO_LIST" -eq 1 ]]; then
|
|
116
|
+
awk -F'\t' '$1 !~ /^#/ && $1 != "name" { printf "%-14s %-4s %-7s emits: %s\n", $1, $9, $3, $8 }' "$GUARDS_TSV"
|
|
117
|
+
if [[ -n "$TARGET" && -d "$TARGET/.git" || -f "$TARGET/.git" ]]; then
|
|
118
|
+
for g in $GUARDS; do
|
|
119
|
+
inst=""
|
|
120
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$g" 8)"
|
|
121
|
+
for p in "${ep[@]}"; do [[ -e "$TARGET/$p" ]] && { inst="$inst $p"; }; done
|
|
122
|
+
[[ -n "$inst" ]] && say "installed:$g →${inst}"
|
|
123
|
+
done
|
|
124
|
+
fi
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# --- run lock (mkdir in the git common dir) -------------------------------------
|
|
129
|
+
RUN_LOCK=""
|
|
130
|
+
take_run_lock() {
|
|
131
|
+
local common
|
|
132
|
+
common="$(git -C "$TARGET" rev-parse --git-common-dir 2>/dev/null)" || common=".git"
|
|
133
|
+
[[ "$common" == /* ]] || common="$TARGET/$common"
|
|
134
|
+
RUN_LOCK="$common/verification-scaffold.lock"
|
|
135
|
+
if ! mkdir "$RUN_LOCK" 2>/dev/null; then
|
|
136
|
+
local owner
|
|
137
|
+
owner="$(cat "$RUN_LOCK/pid" 2>/dev/null || true)"
|
|
138
|
+
if [[ -n "$owner" ]] && kill -0 "$owner" 2>/dev/null; then
|
|
139
|
+
RUN_LOCK=""
|
|
140
|
+
die "another verification-scaffold run (pid $owner) is in progress in this repository; wait for it" 69
|
|
141
|
+
fi
|
|
142
|
+
rm -rf -- "$RUN_LOCK"
|
|
143
|
+
mkdir "$RUN_LOCK" 2>/dev/null || { RUN_LOCK=""; die "cannot take the run lock at $RUN_LOCK" 69; }
|
|
144
|
+
fi
|
|
145
|
+
printf '%s\n' "$$" > "$RUN_LOCK/pid"
|
|
146
|
+
}
|
|
147
|
+
release_run_lock() {
|
|
148
|
+
[[ -n "${RUN_LOCK:-}" && -d "$RUN_LOCK" ]] && rm -rf -- "$RUN_LOCK"
|
|
149
|
+
RUN_LOCK=""
|
|
150
|
+
return 0
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# TMPDIR containment — a mktemp inside the repo would land the bite worktree and
|
|
154
|
+
# its logs in the user's tree as untracked files (constraint-scaffold exit-69
|
|
155
|
+
# precedent).
|
|
156
|
+
check_tmpdir_outside_repo() {
|
|
157
|
+
local t
|
|
158
|
+
t="$(physdir "${TMPDIR:-/tmp}")" || t=""
|
|
159
|
+
[[ -n "$t" ]] || return 0
|
|
160
|
+
case "$t" in
|
|
161
|
+
"$TARGET"|"$TARGET"/*)
|
|
162
|
+
die "TMPDIR ($t) resolves inside the target repository; refusing" 69 ;;
|
|
163
|
+
esac
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# --- emitted-file bookkeeping + self-cleanup -------------------------------------
|
|
167
|
+
WORK="$(mktemp -d -t verification-scaffold-XXXXXX)"
|
|
168
|
+
EMITTED_LOG="$WORK/emitted" # one target-absolute path per line, as written
|
|
169
|
+
DIRS_LOG="$WORK/dirs-created" # dirs mkdir -p created THIS run (deepest first)
|
|
170
|
+
: > "$EMITTED_LOG"; : > "$DIRS_LOG"
|
|
171
|
+
# Remove the bookkeeping dir on every pre-arm exit (early dies, --dry-run,
|
|
172
|
+
# --uninstall). The fuller _vs_cleanup trap replaces this at the arming site.
|
|
173
|
+
trap 'rm -rf -- "$WORK"' EXIT
|
|
174
|
+
|
|
175
|
+
record_emit() { printf '%s\n' "$1" >> "$EMITTED_LOG"; }
|
|
176
|
+
record_dir() { grep -qxF "$1" "$DIRS_LOG" 2>/dev/null || printf '%s\n' "$1" >> "$DIRS_LOG"; }
|
|
177
|
+
|
|
178
|
+
ensure_dir() { # ensure_dir <abs dir> — mkdir -p, recording only NEW dirs (leaf-up)
|
|
179
|
+
local d="$1" missing=""
|
|
180
|
+
while [[ ! -d "$d" ]]; do missing="$d $missing"; d="$(dirname "$d")"; done
|
|
181
|
+
for d in $missing; do mkdir "$d" && record_dir "$d"; done
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
SETTINGS="$TARGET/.claude/settings.json"
|
|
185
|
+
SETTINGS_BACKED_UP=0
|
|
186
|
+
backup_settings() {
|
|
187
|
+
[[ "$SETTINGS_BACKED_UP" -eq 1 ]] && return 0
|
|
188
|
+
if [[ -f "$SETTINGS" ]]; then cp "$SETTINGS" "$WORK/settings.json.bak"; else : > "$WORK/settings.json.absent"; fi
|
|
189
|
+
SETTINGS_BACKED_UP=1
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
CLEANUP_ARMED=0
|
|
193
|
+
_vs_cleanup() {
|
|
194
|
+
local why="${1:-run aborted}"
|
|
195
|
+
release_run_lock
|
|
196
|
+
if [[ "$CLEANUP_ARMED" -eq 1 ]]; then
|
|
197
|
+
local removed="" f d
|
|
198
|
+
while IFS= read -r f; do
|
|
199
|
+
[[ -e "$f" ]] && { rm -f -- "$f"; removed="$removed ${f#"$TARGET"/}"; }
|
|
200
|
+
done < "$EMITTED_LOG"
|
|
201
|
+
if [[ "$SETTINGS_BACKED_UP" -eq 1 ]]; then
|
|
202
|
+
if [[ -f "$WORK/settings.json.bak" ]]; then cp "$WORK/settings.json.bak" "$SETTINGS" && removed="$removed .claude/settings.json(restored)"
|
|
203
|
+
else rm -f -- "$SETTINGS" && removed="$removed .claude/settings.json"; fi
|
|
204
|
+
fi
|
|
205
|
+
# DIRS_LOG is recorded shallowest-first; remove deepest-first via a portable
|
|
206
|
+
# reverse (neither tac nor tail -r exists on both GNU and BSD).
|
|
207
|
+
while IFS= read -r d; do rmdir "$d" >/dev/null 2>&1 || true; done \
|
|
208
|
+
< <(awk '{a[NR]=$0} END{for(i=NR;i>0;i--)print a[i]}' "$DIRS_LOG")
|
|
209
|
+
[[ -n "$removed" ]] && say "$why; removed:${removed}"
|
|
210
|
+
fi
|
|
211
|
+
# The bite worktree is removed on the same path — a mid-bite failure must not
|
|
212
|
+
# leave a registered worktree behind.
|
|
213
|
+
if [[ -n "${WT:-}" && -d "$WT" ]]; then
|
|
214
|
+
git -C "$TARGET" worktree remove --force "$WT" >/dev/null 2>&1 || true
|
|
215
|
+
rm -rf -- "$WT"
|
|
216
|
+
fi
|
|
217
|
+
[[ -n "${BITE_DIR:-}" && -d "$BITE_DIR" ]] && rm -rf -- "$BITE_DIR"
|
|
218
|
+
rm -rf -- "$WORK"
|
|
219
|
+
return 0
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# --- template emission -----------------------------------------------------------
|
|
223
|
+
# emit_tpl <template-rel> <target-rel> <mode>: render @MODE@/@DEFAULT_BRANCHES@/
|
|
224
|
+
# @CI_CONTINUE_ON_ERROR@ substitutions, write, record. Substitution is bash
|
|
225
|
+
# parameter expansion — a literal-replacement pass with no delimiter: under sed
|
|
226
|
+
# an unescaped `&` in DEFAULT_BRANCHES re-inserted the literal placeholder
|
|
227
|
+
# (fail-open: the emitted regex then never matched the real default branch) and
|
|
228
|
+
# sed's replacement layer ate the regex escapes outright (#10759).
|
|
229
|
+
emit_tpl() {
|
|
230
|
+
local tpl="$REF_DIR/$1" out="$TARGET/$2" mode="${3:-0644}"
|
|
231
|
+
ensure_dir "$(dirname "$out")"
|
|
232
|
+
local content
|
|
233
|
+
content="$(cat "$tpl")"
|
|
234
|
+
content="${content//@MODE@/$MODE}"
|
|
235
|
+
content="${content//@DEFAULT_BRANCHES@/$DEFAULT_BRANCHES}"
|
|
236
|
+
content="${content//@CI_CONTINUE_ON_ERROR@/$CI_CONTINUE_ON_ERROR}"
|
|
237
|
+
printf '%s\n' "$content" > "$out"
|
|
238
|
+
chmod "$mode" "$out"
|
|
239
|
+
record_emit "$out"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
HOOK_COMMANDS=() # settings.json command strings, one per emitted hook
|
|
243
|
+
emit_guard() {
|
|
244
|
+
local g="$1"
|
|
245
|
+
case "$g" in
|
|
246
|
+
secret-scan)
|
|
247
|
+
emit_tpl "guards/secret-scan-hook.sh.template" ".claude/hooks/secret-scan.sh" 0755
|
|
248
|
+
HOOK_COMMANDS+=("bash \"\$CLAUDE_PROJECT_DIR/.claude/hooks/secret-scan.sh\"")
|
|
249
|
+
emit_tpl "gitleaks.toml.template" ".gitleaks.toml" 0644
|
|
250
|
+
;;
|
|
251
|
+
commit-on-main)
|
|
252
|
+
emit_tpl "guards/commit-on-main-hook.sh.template" ".claude/hooks/commit-on-main.sh" 0755
|
|
253
|
+
HOOK_COMMANDS+=("bash \"\$CLAUDE_PROJECT_DIR/.claude/hooks/commit-on-main.sh\"")
|
|
254
|
+
emit_tpl "guards/precommit-guard.sh.template" "scripts/verification/precommit-guard.sh" 0755
|
|
255
|
+
emit_tpl "guards/wrapper-boundary.sh.template" "scripts/verification/lib/wrapper-boundary.sh" 0644
|
|
256
|
+
;;
|
|
257
|
+
block-stash)
|
|
258
|
+
emit_tpl "guards/block-stash-hook.sh.template" ".claude/hooks/block-stash.sh" 0755
|
|
259
|
+
HOOK_COMMANDS+=("bash \"\$CLAUDE_PROJECT_DIR/.claude/hooks/block-stash.sh\"")
|
|
260
|
+
;;
|
|
261
|
+
orphan-test)
|
|
262
|
+
emit_tpl "guards/orphan-test-check.sh.template" "scripts/verification/orphan-test-check.sh" 0755
|
|
263
|
+
emit_known_orphans
|
|
264
|
+
;;
|
|
265
|
+
*) die "no emitter for guard: $g" 64 ;;
|
|
266
|
+
esac
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# Seed .github/verification-known-orphans.txt: header + one grandfathered row per
|
|
270
|
+
# currently-unregistered tracked *.test.sh (the baseline precedent — pre-existing
|
|
271
|
+
# orphans are recorded, never silently absorbed).
|
|
272
|
+
RUNNER_GLOBS_SEED="scripts/test-all.sh scripts/run-tests* Makefile makefile package.json"
|
|
273
|
+
emit_known_orphans() {
|
|
274
|
+
local out="$TARGET/.github/verification-known-orphans.txt"
|
|
275
|
+
{
|
|
276
|
+
printf '# verification-known-orphans.txt — seeded by soleur:verification-scaffold.\n'
|
|
277
|
+
printf '# Format: <repo-relative path>.test.sh # <reason>. A suite listed here is\n'
|
|
278
|
+
printf '# excluded from the orphan-test check; a line without a reason fails it.\n'
|
|
279
|
+
} > "$out"
|
|
280
|
+
# Registration surfaces, same rule the emitted check applies: any workflow
|
|
281
|
+
# file plus any file matching a runner glob.
|
|
282
|
+
local surfaces="" f pat
|
|
283
|
+
for f in "$TARGET"/.github/workflows/*.yml "$TARGET"/.github/workflows/*.yaml; do
|
|
284
|
+
[[ -f "$f" ]] && surfaces="$surfaces $f"
|
|
285
|
+
done
|
|
286
|
+
local rf
|
|
287
|
+
while IFS= read -r rf; do surfaces="$surfaces $TARGET/$rf"; done < <(
|
|
288
|
+
cd "$TARGET" && for pat in $RUNNER_GLOBS_SEED; do for f in $pat; do [[ -f "$f" ]] && printf '%s\n' "$f"; done; done
|
|
289
|
+
)
|
|
290
|
+
local suite base seeded=0
|
|
291
|
+
while IFS= read -r suite; do
|
|
292
|
+
[[ -n "$suite" ]] || continue
|
|
293
|
+
base="$(basename "$suite")"
|
|
294
|
+
# shellcheck disable=SC2086
|
|
295
|
+
if [[ -n "${surfaces# }" ]] && grep -q . < <(grep -lFq -- "$base" $surfaces 2>/dev/null); then continue; fi
|
|
296
|
+
printf '%s # pre-existing at verification-scaffold install (grandfathered — register or remove)\n' "$suite" >> "$out"
|
|
297
|
+
seeded=$((seeded + 1))
|
|
298
|
+
done < <(git -C "$TARGET" ls-files '*.test.sh' 2>/dev/null)
|
|
299
|
+
[[ "$seeded" -gt 0 ]] && log "seeded $seeded grandfathered orphan row(s) in ${out#"$TARGET"/}"
|
|
300
|
+
record_emit "$out"
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# --- settings.json merge (node-first, jq fallback, else refuse) -------------------
|
|
304
|
+
merge_settings() {
|
|
305
|
+
backup_settings
|
|
306
|
+
ensure_dir "$(dirname "$SETTINGS")"
|
|
307
|
+
local cmds=() h
|
|
308
|
+
for h in "${HOOK_COMMANDS[@]+"${HOOK_COMMANDS[@]}"}"; do [[ -n "$h" ]] && cmds+=("$h"); done
|
|
309
|
+
if command -v node >/dev/null 2>&1; then
|
|
310
|
+
node - "$SETTINGS" ${cmds[@]+"${cmds[@]}"} <<'NODE'
|
|
311
|
+
const fs = require('fs');
|
|
312
|
+
const [,, file, ...cmds] = process.argv;
|
|
313
|
+
let d = {};
|
|
314
|
+
if (fs.existsSync(file) && fs.statSync(file).size > 0) d = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
315
|
+
d.hooks = d.hooks || {};
|
|
316
|
+
d.hooks.PreToolUse = d.hooks.PreToolUse || [];
|
|
317
|
+
for (const c of cmds) {
|
|
318
|
+
const seen = d.hooks.PreToolUse.some(m => (m.hooks || []).some(h => h.command === c));
|
|
319
|
+
if (!seen) d.hooks.PreToolUse.push({ matcher: 'Bash', hooks: [{ type: 'command', command: c }] });
|
|
320
|
+
}
|
|
321
|
+
fs.writeFileSync(file, JSON.stringify(d, null, 2) + '\n');
|
|
322
|
+
NODE
|
|
323
|
+
elif command -v jq >/dev/null 2>&1; then
|
|
324
|
+
[[ -f "$SETTINGS" ]] || printf '{}\n' > "$SETTINGS"
|
|
325
|
+
local c
|
|
326
|
+
for c in ${cmds[@]+"${cmds[@]}"}; do
|
|
327
|
+
local tmp="$WORK/settings.jq"
|
|
328
|
+
jq --arg c "$c" '
|
|
329
|
+
.hooks //= {} | .hooks.PreToolUse //= [] |
|
|
330
|
+
if ([.hooks.PreToolUse[].hooks[]? | .command] | index($c)) then .
|
|
331
|
+
else .hooks.PreToolUse += [{matcher:"Bash",hooks:[{type:"command",command:$c}]}] end
|
|
332
|
+
' "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
|
|
333
|
+
done
|
|
334
|
+
else
|
|
335
|
+
die "neither node nor jq resolves — cannot merge .claude/settings.json safely (install one, or register the hooks by hand)" 69
|
|
336
|
+
fi
|
|
337
|
+
# NOT recorded in EMITTED_LOG: the backup governs the restore path, and the
|
|
338
|
+
# file itself may be user-owned and pre-existing — never rm'd.
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
unmerge_settings() { # remove the given guards' hook command entries
|
|
342
|
+
local names="$1" c
|
|
343
|
+
[[ -f "$SETTINGS" ]] || return 0
|
|
344
|
+
if command -v node >/dev/null 2>&1; then
|
|
345
|
+
node - "$SETTINGS" $names <<'NODE'
|
|
346
|
+
const fs = require('fs');
|
|
347
|
+
const [,, file, ...names] = process.argv;
|
|
348
|
+
if (!fs.existsSync(file)) process.exit(0);
|
|
349
|
+
const d = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
350
|
+
if (!d.hooks || !d.hooks.PreToolUse) process.exit(0);
|
|
351
|
+
const drop = new Set(names.map(n => `bash "$CLAUDE_PROJECT_DIR/.claude/hooks/${n}.sh"`));
|
|
352
|
+
d.hooks.PreToolUse = d.hooks.PreToolUse
|
|
353
|
+
.map(m => ({ ...m, hooks: (m.hooks || []).filter(h => !drop.has(h.command)) }))
|
|
354
|
+
.filter(m => (m.hooks || []).length > 0);
|
|
355
|
+
fs.writeFileSync(file, JSON.stringify(d, null, 2) + '\n');
|
|
356
|
+
NODE
|
|
357
|
+
elif command -v jq >/dev/null 2>&1; then
|
|
358
|
+
local tmp="$WORK/settings.jq" expr="."
|
|
359
|
+
for c in $names; do
|
|
360
|
+
expr="$expr | .hooks.PreToolUse = ([.hooks.PreToolUse[]? | .hooks = ([.hooks[]? | select(.command != \"bash \\\"\$CLAUDE_PROJECT_DIR/.claude/hooks/$c.sh\\\"\")])] | map(select((.hooks|length) > 0)))"
|
|
361
|
+
done
|
|
362
|
+
jq "$expr" "$SETTINGS" > "$tmp" && mv "$tmp" "$SETTINGS"
|
|
363
|
+
fi
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# --- workflow render -----------------------------------------------------------
|
|
367
|
+
# Keep the marker block only for selected ci guards; stamp continue-on-error.
|
|
368
|
+
render_workflow() {
|
|
369
|
+
local tpl="$REF_DIR/guards/verification-gates.yml.template" out="$1"
|
|
370
|
+
local keep_secret=0 keep_orphan=0 g
|
|
371
|
+
for g in $GUARDS; do
|
|
372
|
+
case "$g" in secret-scan) keep_secret=1 ;; orphan-test) keep_orphan=1 ;; esac
|
|
373
|
+
done
|
|
374
|
+
awk -v ks="$keep_secret" -v ko="$keep_orphan" '
|
|
375
|
+
/^ # verification-scaffold:begin:secret-scan$/ { skip = (ks ? 0 : 1); if (skip) next }
|
|
376
|
+
/^ # verification-scaffold:end:secret-scan$/ { if (skip) { skip=0; next } }
|
|
377
|
+
/^ # verification-scaffold:begin:orphan-test$/ { skip = (ko ? 0 : 1); if (skip) next }
|
|
378
|
+
/^ # verification-scaffold:end:orphan-test$/ { if (skip) { skip=0; next } }
|
|
379
|
+
!skip { print }
|
|
380
|
+
' "$tpl" | sed -e "s|@CI_CONTINUE_ON_ERROR@|$CI_CONTINUE_ON_ERROR|g" > "$out"
|
|
381
|
+
record_emit "$out"
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# --- default-branch detection (SpecFlow #1) --------------------------------------
|
|
385
|
+
resolve_default_branches() {
|
|
386
|
+
local ref
|
|
387
|
+
ref="$(git -C "$TARGET" symbolic-ref -q --short refs/remotes/origin/HEAD 2>/dev/null || true)"
|
|
388
|
+
if [[ -n "$ref" ]]; then
|
|
389
|
+
# ERE-escape every metachar legal in a ref name — emit_tpl substitutes this
|
|
390
|
+
# string literally into the emitted pattern, so a default named release/1.0
|
|
391
|
+
# or a&b compiles to the intended literal (#10759: a bare & once re-inserted
|
|
392
|
+
# the @DEFAULT_BRANCHES@ placeholder through sed's replacement).
|
|
393
|
+
DEFAULT_BRANCHES="$(printf '%s' "${ref#origin/}" | sed 's/[][\.*^$/|&~()+?{}]/\\&/g')"
|
|
394
|
+
else
|
|
395
|
+
# Raw | — the intended alternation, which is why the escape stays inside
|
|
396
|
+
# the ref-resolved branch only.
|
|
397
|
+
DEFAULT_BRANCHES="main|master"
|
|
398
|
+
fi
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
# --- bite-proof ------------------------------------------------------------------
|
|
402
|
+
WT=""
|
|
403
|
+
BITE_DIR=""
|
|
404
|
+
prove_bite() {
|
|
405
|
+
BITE_DIR="$(mktemp -d -t verification-bite-XXXXXX)"
|
|
406
|
+
WT="$BITE_DIR/wt"
|
|
407
|
+
local git_err
|
|
408
|
+
git_err="$(git -C "$TARGET" worktree add --detach "$WT" HEAD 2>&1 >/dev/null)" \
|
|
409
|
+
|| die "git worktree add failed: $(printf '%s' "$git_err" | tail -n 3 | tr '\n' ' ')" 69
|
|
410
|
+
# Mirror the emitted files into the worktree (untracked emits are not in HEAD).
|
|
411
|
+
local f
|
|
412
|
+
while IFS= read -r f; do
|
|
413
|
+
local rel="${f#"$TARGET"/}"
|
|
414
|
+
mkdir -p "$WT/$(dirname "$rel")"
|
|
415
|
+
[[ -f "$f" ]] && cp "$f" "$WT/$rel" && [[ "$f" == *.sh ]] && chmod +x "$WT/$rel"
|
|
416
|
+
done < "$EMITTED_LOG"
|
|
417
|
+
|
|
418
|
+
for g in $GUARDS; do
|
|
419
|
+
case "$g" in
|
|
420
|
+
secret-scan) bite_secret_scan ;;
|
|
421
|
+
commit-on-main) bite_commit_on_main ;;
|
|
422
|
+
block-stash) bite_block_stash ;;
|
|
423
|
+
orphan-test) bite_orphan_test ;;
|
|
424
|
+
esac
|
|
425
|
+
done
|
|
426
|
+
git -C "$TARGET" worktree remove --force "$WT" >/dev/null 2>&1 || true
|
|
427
|
+
rm -rf -- "$BITE_DIR"
|
|
428
|
+
WT=""; BITE_DIR=""
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
bite_fail() { # bite_fail <code> <guard> <msg>
|
|
432
|
+
say "bite-proof FAILED ($1): $2 — $3"
|
|
433
|
+
exit "$1"
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
# Run an emitted hook: stdin payload, cwd = $WT. Captures stdout JSON + stderr.
|
|
437
|
+
# stderr APPENDS to a per-hook log: truncation dropped every earlier arm's
|
|
438
|
+
# diagnostics, while one shared file would let an earlier guard's refusal WARN
|
|
439
|
+
# satisfy a later guard's stderr grep (#10759).
|
|
440
|
+
run_hook() { # run_hook <hook-path> <command> <cwd-override?>
|
|
441
|
+
local hook="$1" cmd="$2" cwd="${3:-$WT}"
|
|
442
|
+
( cd "$cwd" && printf '%s' "$(payload_json "$cmd" "$cwd")" | bash "$hook" ) \
|
|
443
|
+
2>>"$BITE_DIR/hook-err.$(basename "$hook" .sh).log"
|
|
444
|
+
}
|
|
445
|
+
payload_json() { # payload_json <command> <cwd>
|
|
446
|
+
if command -v jq >/dev/null 2>&1; then
|
|
447
|
+
jq -nc --arg c "$1" --arg w "$2" '{tool_name:"Bash",tool_input:{command:$c},cwd:$w}'
|
|
448
|
+
elif command -v node >/dev/null 2>&1; then
|
|
449
|
+
node -e 'console.log(JSON.stringify({tool_name:"Bash",tool_input:{command:process.argv[1]},cwd:process.argv[2]}))' "$1" "$2"
|
|
450
|
+
else
|
|
451
|
+
printf '{"tool_name":"Bash","tool_input":{"command":"%s"},"cwd":"%s"}' "$1" "$2"
|
|
452
|
+
fi
|
|
453
|
+
}
|
|
454
|
+
decision_of() { sed -n 's/.*"permissionDecision": *"\([a-z]*\)".*/\1/p' "$1" | head -n 1; }
|
|
455
|
+
expect_decision() { # expect_decision <guard> <stage> <got-file> <want...>
|
|
456
|
+
local g="$1" stage="$2" f="$3"; shift 3
|
|
457
|
+
local got; got="$(decision_of "$f")"
|
|
458
|
+
local w
|
|
459
|
+
for w in "$@"; do [[ "$got" == "$w" ]] && return 0; done
|
|
460
|
+
return 1
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
bite_secret_scan() {
|
|
464
|
+
if ! command -v gitleaks >/dev/null 2>&1; then
|
|
465
|
+
say "bite-proof secret-scan: SKIP — gitleaks is not on PATH (the hook will warn+allow per commit; the CI job still scans). Install gitleaks 8.24.x to prove this arm."
|
|
466
|
+
return 0
|
|
467
|
+
fi
|
|
468
|
+
local hook="$WT/.claude/hooks/secret-scan.sh" out
|
|
469
|
+
# pass: clean staged file
|
|
470
|
+
printf 'hello\n' > "$WT/bite-clean.txt"
|
|
471
|
+
git -C "$WT" add bite-clean.txt
|
|
472
|
+
out="$(run_hook "$hook" "git commit -m x")" || true
|
|
473
|
+
expect_decision secret-scan pass <(printf '%s' "$out") allow \
|
|
474
|
+
|| bite_fail 71 secret-scan "clean staged tree did not allow (got: $out; stderr: $(cat "$BITE_DIR/hook-err.secret-scan.log"))"
|
|
475
|
+
# fail: staged secret-shaped string
|
|
476
|
+
# Synthetic-but-shaped secret (the AWS docs example key is allowlisted in
|
|
477
|
+
# gitleaks's default ruleset — this variant keeps the 40-char shape only).
|
|
478
|
+
printf 'AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYSYNTHETIC0"\n' > "$WT/bite-secret.txt"
|
|
479
|
+
git -C "$WT" add bite-secret.txt
|
|
480
|
+
out="$(run_hook "$hook" "git commit -m x")" || true
|
|
481
|
+
if [[ "$MODE" == "enforce" ]]; then want=deny; else want=ask; fi
|
|
482
|
+
expect_decision secret-scan fail <(printf '%s' "$out") "$want" \
|
|
483
|
+
|| bite_fail 72 secret-scan "staged secret did not produce $want (got: $out; stderr: $(cat "$BITE_DIR/hook-err.secret-scan.log"))"
|
|
484
|
+
# pass again: revert
|
|
485
|
+
git -C "$WT" rm -q --cached bite-secret.txt && rm -f "$WT/bite-secret.txt"
|
|
486
|
+
out="$(run_hook "$hook" "git commit -m x")" || true
|
|
487
|
+
expect_decision secret-scan pass2 <(printf '%s' "$out") allow \
|
|
488
|
+
|| bite_fail 73 secret-scan "did not return to green after the probe was reverted (got: $out)"
|
|
489
|
+
say "bite-proof secret-scan: pass -> fail($want) -> pass"
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
bite_commit_on_main() {
|
|
493
|
+
local hook="$WT/.claude/hooks/commit-on-main.sh" out
|
|
494
|
+
local fx="$BITE_DIR/fx-commit" first="${DEFAULT_BRANCHES%%|*}"
|
|
495
|
+
first="${first//\\/}" # un-escape for git branch creation
|
|
496
|
+
mkdir -p "$fx"
|
|
497
|
+
# symbolic-ref points the unborn HEAD at the resolved default BEFORE the first
|
|
498
|
+
# commit — `git init -b` would fail on old gits and `checkout -b` collides when
|
|
499
|
+
# the init default already IS the resolved name.
|
|
500
|
+
git -C "$fx" init -q
|
|
501
|
+
git -C "$fx" symbolic-ref HEAD "refs/heads/$first"
|
|
502
|
+
git -C "$fx" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init
|
|
503
|
+
git -C "$fx" checkout -qb feat-bite
|
|
504
|
+
local want; [[ "$MODE" == "enforce" ]] && want=deny || want=allow
|
|
505
|
+
# The hook resolves its guard via the cwd's toplevel — run it from the
|
|
506
|
+
# worktree; the PAYLOAD's -C targets the fixture repo the guard branches on.
|
|
507
|
+
# pass: feature branch
|
|
508
|
+
out="$(run_hook "$hook" "git -C $fx commit -m x" "$WT")" || true
|
|
509
|
+
expect_decision commit-on-main pass <(printf '%s' "$out") allow \
|
|
510
|
+
|| bite_fail 71 commit-on-main "feature-branch payload was not allowed (got: $out; stderr: $(cat "$BITE_DIR/hook-err.commit-on-main.log"))"
|
|
511
|
+
# fail: default branch
|
|
512
|
+
git -C "$fx" checkout -q "$first"
|
|
513
|
+
out="$(run_hook "$hook" "git -C $fx commit -m x" "$WT")" || true
|
|
514
|
+
expect_decision commit-on-main fail <(printf '%s' "$out") "$want" \
|
|
515
|
+
|| bite_fail 72 commit-on-main "default-branch ($first) payload did not produce $want (got: $out; stderr: $(cat "$BITE_DIR/hook-err.commit-on-main.log"))"
|
|
516
|
+
# Advisory must warn a REFUSAL on stderr — an allow with no signal is a silent
|
|
517
|
+
# guard, and a tool-error warn must not satisfy the arm either.
|
|
518
|
+
if [[ "$MODE" == "advisory" ]] && ! grep -qE 'not allowed|refused' "$BITE_DIR/hook-err.commit-on-main.log"; then
|
|
519
|
+
bite_fail 72 commit-on-main "advisory refusal carried no refusal WARN on stderr (got: $out; stderr: $(cat "$BITE_DIR/hook-err.commit-on-main.log"))"
|
|
520
|
+
fi
|
|
521
|
+
# pass again
|
|
522
|
+
git -C "$fx" checkout -q feat-bite
|
|
523
|
+
out="$(run_hook "$hook" "git -C $fx commit -m x" "$WT")" || true
|
|
524
|
+
expect_decision commit-on-main pass2 <(printf '%s' "$out") allow \
|
|
525
|
+
|| bite_fail 73 commit-on-main "did not return to allow on a feature branch (got: $out)"
|
|
526
|
+
say "bite-proof commit-on-main: pass -> fail($want) -> pass"
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
bite_block_stash() {
|
|
530
|
+
local hook="$WT/.claude/hooks/block-stash.sh" out want
|
|
531
|
+
[[ "$MODE" == "enforce" ]] && want=deny || want=allow
|
|
532
|
+
out="$(run_hook "$hook" "git status")" || true
|
|
533
|
+
expect_decision block-stash pass <(printf '%s' "$out") allow \
|
|
534
|
+
|| bite_fail 71 block-stash "git status was not allowed (got: $out)"
|
|
535
|
+
out="$(run_hook "$hook" "git add x && git stash pop")" || true
|
|
536
|
+
expect_decision block-stash fail <(printf '%s' "$out") "$want" \
|
|
537
|
+
|| bite_fail 72 block-stash "chained 'git stash pop' did not produce $want (got: $out)"
|
|
538
|
+
if [[ "$MODE" == "advisory" ]] && ! grep -qE 'not allowed' "$BITE_DIR/hook-err.block-stash.log"; then
|
|
539
|
+
bite_fail 72 block-stash "advisory refusal carried no refusal WARN on stderr (got: $out; stderr: $(cat "$BITE_DIR/hook-err.block-stash.log"))"
|
|
540
|
+
fi
|
|
541
|
+
out="$(run_hook "$hook" "git stash list")" || true
|
|
542
|
+
expect_decision block-stash pass2 <(printf '%s' "$out") allow \
|
|
543
|
+
|| bite_fail 73 block-stash "documented read-only 'git stash list' was not allowed (got: $out)"
|
|
544
|
+
say "bite-proof block-stash: pass -> fail($want) -> pass"
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
bite_orphan_test() {
|
|
548
|
+
local check="$WT/scripts/verification/orphan-test-check.sh" out rc
|
|
549
|
+
# pass: current tree (grandfather rows already cover pre-existing orphans)
|
|
550
|
+
out="$(cd "$WT" && bash "$check" 2>&1)" || bite_fail 71 orphan-test "clean tree did not pass: $out"
|
|
551
|
+
# fail: an unregistered suite
|
|
552
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$WT/scratch-bite.test.sh"
|
|
553
|
+
git -C "$WT" add scratch-bite.test.sh
|
|
554
|
+
rc=0; out="$(cd "$WT" && bash "$check" 2>&1)" || rc=$?
|
|
555
|
+
if [[ "$rc" -eq 0 ]] || ! grep -q 'scratch-bite.test.sh' <<<"$out"; then
|
|
556
|
+
git -C "$WT" rm -q --cached scratch-bite.test.sh; rm -f "$WT/scratch-bite.test.sh"
|
|
557
|
+
bite_fail 72 orphan-test "injected unregistered suite was not named (rc=$rc): $out"
|
|
558
|
+
fi
|
|
559
|
+
# pass again
|
|
560
|
+
git -C "$WT" rm -q --cached scratch-bite.test.sh; rm -f "$WT/scratch-bite.test.sh"
|
|
561
|
+
out="$(cd "$WT" && bash "$check" 2>&1)" \
|
|
562
|
+
|| bite_fail 73 orphan-test "did not return to green after the probe was reverted: $out"
|
|
563
|
+
say "bite-proof orphan-test: pass -> fail(named scratch-bite.test.sh) -> pass"
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
# --- doc block (append-once, written LAST) -----------------------------------------
|
|
567
|
+
emit_doc() {
|
|
568
|
+
local guard_list="${GUARDS// /, }" guard_rows="" g surfaces artifacts decides
|
|
569
|
+
for g in $GUARDS; do
|
|
570
|
+
surfaces="$(_guard_field "$g" 3)"
|
|
571
|
+
artifacts="$(printf '%s' "$(_guard_field "$g" 8)" | tr ';' ' ')"
|
|
572
|
+
case "$g" in
|
|
573
|
+
secret-scan) decides="secret-shaped strings staged for commit" ;;
|
|
574
|
+
commit-on-main) decides="commits on the default branch(es): $DEFAULT_BRANCHES" ;;
|
|
575
|
+
block-stash) decides="every git stash except list/show" ;;
|
|
576
|
+
orphan-test) decides="tracked *.test.sh named by no runner surface" ;;
|
|
577
|
+
esac
|
|
578
|
+
guard_rows="$guard_rows| \`$g\` ($surfaces) | \`$artifacts\` | $decides |"$'\n'
|
|
579
|
+
done
|
|
580
|
+
printf '%s' "$guard_rows" > "$WORK/doc-rows"
|
|
581
|
+
local content
|
|
582
|
+
content="$(sed -e "s|@MODE@|$MODE|g" -e "s|@GUARD_LIST@|$guard_list|g" \
|
|
583
|
+
-e "s|@DATE@|$(date -u +%Y-%m-%d)|g" \
|
|
584
|
+
-e "/@GUARD_TABLE@/r $WORK/doc-rows" -e "/@GUARD_TABLE@/d" \
|
|
585
|
+
"$REF_DIR/verification-readme.template")"
|
|
586
|
+
local doc="$TARGET/VERIFICATION.md"
|
|
587
|
+
[[ -e "$doc" || -L "$doc" ]] && doc="$TARGET/README.md"
|
|
588
|
+
[[ -L "$doc" ]] && { warn "refusing to append through a symlink: ${doc#"$TARGET"/}"; return 0; }
|
|
589
|
+
if grep -qF 'verification-scaffold:readme:start' "$doc" 2>/dev/null; then
|
|
590
|
+
log "doc block already present in ${doc#"$TARGET"/}"
|
|
591
|
+
return 0
|
|
592
|
+
fi
|
|
593
|
+
[[ -s "$doc" && -n "$(tail -c 1 "$doc")" ]] && printf '\n' >> "$doc"
|
|
594
|
+
printf '%s\n' "$content" >> "$doc" \
|
|
595
|
+
|| warn "could not append the doc block to ${doc#"$TARGET"/} — the guards are installed and proven; add references/verification-readme.template by hand"
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
# --- --uninstall -------------------------------------------------------------------
|
|
599
|
+
if [[ -n "$UNINSTALL" ]]; then
|
|
600
|
+
g="$UNINSTALL"
|
|
601
|
+
emits="$(_guard_field "$g" 8)"
|
|
602
|
+
removed=""
|
|
603
|
+
IFS=';' read -ra paths <<<"$emits"
|
|
604
|
+
for p in "${paths[@]}"; do
|
|
605
|
+
f="$TARGET/$p"
|
|
606
|
+
case "$p" in
|
|
607
|
+
.github/workflows/verification-gates.yml)
|
|
608
|
+
# Shared artifact: strip only this guard's job block; delete only when no
|
|
609
|
+
# other selected-capable ci guard remains installed.
|
|
610
|
+
other_ci=0
|
|
611
|
+
for og in $(v1_guards); do
|
|
612
|
+
[[ "$og" == "$g" ]] && continue
|
|
613
|
+
case "$(_guard_field "$og" 3)" in *ci*)
|
|
614
|
+
IFS=';' read -ra op <<<"$(_guard_field "$og" 8)"
|
|
615
|
+
for q in "${op[@]}"; do [[ "$q" == "scripts/verification/orphan-test-check.sh" || "$q" == ".claude/hooks/secret-scan.sh" ]] && [[ -e "$TARGET/$q" ]] && other_ci=1; done
|
|
616
|
+
esac
|
|
617
|
+
done
|
|
618
|
+
if [[ -f "$f" ]]; then
|
|
619
|
+
if [[ "$other_ci" -eq 0 ]]; then
|
|
620
|
+
rm -f -- "$f"; removed="$removed $p"
|
|
621
|
+
elif grep -qF "# verification-scaffold:begin:$g" "$f"; then
|
|
622
|
+
sed "/# verification-scaffold:begin:$g/,/# verification-scaffold:end:$g/d" "$f" > "$f.tmp" && mv "$f.tmp" "$f"
|
|
623
|
+
removed="$removed $p($g-job-stripped)"
|
|
624
|
+
fi
|
|
625
|
+
# other_ci=1 with no begin:$g marker: this guard's job block was never
|
|
626
|
+
# emitted (subset install) — the shared file stays byte-identical and
|
|
627
|
+
# nothing is reported (#10759 phantom-job-strip).
|
|
628
|
+
fi ;;
|
|
629
|
+
.gitleaks.toml)
|
|
630
|
+
if [[ -f "$f" ]]; then
|
|
631
|
+
if cmp -s "$f" "$REF_DIR/gitleaks.toml.template"; then rm -f -- "$f"; removed="$removed $p"
|
|
632
|
+
else warn "leaving modified $p (no longer the emitted bytes — remove by hand if unwanted)"; fi
|
|
633
|
+
fi ;;
|
|
634
|
+
.claude/hooks/*.sh|scripts/verification/*)
|
|
635
|
+
[[ -e "$f" ]] && { rm -f -- "$f"; removed="$removed $p"; } ;;
|
|
636
|
+
*) [[ -e "$f" ]] && { rm -f -- "$f"; removed="$removed $p"; } ;;
|
|
637
|
+
esac
|
|
638
|
+
done
|
|
639
|
+
# settings.json hook entries for this guard's hooks
|
|
640
|
+
hook_names=""
|
|
641
|
+
for p in "${paths[@]}"; do case "$p" in .claude/hooks/*.sh) hook_names="$hook_names $(basename "$p" .sh)" ;; esac; done
|
|
642
|
+
[[ -n "$hook_names" ]] && unmerge_settings "$hook_names"
|
|
643
|
+
# Empty dirs — only those THIS scaffold created (the persisted ledger), only
|
|
644
|
+
# when empty (rmdir enforces that).
|
|
645
|
+
if [[ -f "$TARGET/.claude/.verification-scaffold-dirs" ]]; then
|
|
646
|
+
while IFS= read -r d; do [[ -d "$d" ]] && rmdir "$d" 2>/dev/null || true; done \
|
|
647
|
+
< <(awk '{a[NR]=$0} END{for(i=NR;i>0;i--)print a[i]}' "$TARGET/.claude/.verification-scaffold-dirs")
|
|
648
|
+
fi
|
|
649
|
+
# Drop the ledger itself when no v1 guard has a single artifact left.
|
|
650
|
+
left=""
|
|
651
|
+
for og in $(v1_guards); do
|
|
652
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$og" 8)"
|
|
653
|
+
for p in "${ep[@]}"; do [[ -e "$TARGET/$p" ]] && left=1; done
|
|
654
|
+
done
|
|
655
|
+
[[ -z "$left" ]] && rm -f -- "$TARGET/.claude/.verification-scaffold-dirs" \
|
|
656
|
+
&& rmdir "$TARGET/.claude" 2>/dev/null || true
|
|
657
|
+
# Nothing installed -> the marker doc block is a stale claim that guards are
|
|
658
|
+
# present; strip the begin:end range (only when BOTH markers are present, so a
|
|
659
|
+
# truncated block can never delete to EOF) and drop the file if it emptied
|
|
660
|
+
# (#10759 — VERIFICATION.md is the block alone; README.md keeps its content).
|
|
661
|
+
if [[ -z "$left" ]]; then
|
|
662
|
+
for doc in "$TARGET/VERIFICATION.md" "$TARGET/README.md"; do
|
|
663
|
+
[[ -f "$doc" && ! -L "$doc" ]] || continue
|
|
664
|
+
grep -qF 'verification-scaffold:readme:start' "$doc" || continue
|
|
665
|
+
grep -qF 'verification-scaffold:readme:end' "$doc" || continue
|
|
666
|
+
sed '/verification-scaffold:readme:start/,/verification-scaffold:readme:end/d' "$doc" > "$WORK/docstrip" \
|
|
667
|
+
&& mv "$WORK/docstrip" "$doc"
|
|
668
|
+
[[ -s "$doc" ]] || rm -f -- "$doc"
|
|
669
|
+
removed="$removed ${doc#"$TARGET"/}(doc-block)"
|
|
670
|
+
done
|
|
671
|
+
fi
|
|
672
|
+
if [[ -z "$removed" ]]; then
|
|
673
|
+
say "$g: not installed — nothing removed."
|
|
674
|
+
for og in $(v1_guards); do
|
|
675
|
+
inst=""
|
|
676
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$og" 8)"
|
|
677
|
+
for p in "${ep[@]}"; do [[ -e "$TARGET/$p" ]] && inst="$inst $p"; done
|
|
678
|
+
[[ -n "$inst" ]] && say "installed:$og →${inst}"
|
|
679
|
+
done
|
|
680
|
+
exit 0
|
|
681
|
+
fi
|
|
682
|
+
say "uninstalled $g — removed:${removed}"
|
|
683
|
+
exit 0
|
|
684
|
+
fi
|
|
685
|
+
|
|
686
|
+
# --- default mode ------------------------------------------------------------------
|
|
687
|
+
check_tmpdir_outside_repo
|
|
688
|
+
resolve_default_branches
|
|
689
|
+
[[ "$MODE" == "enforce" ]] && CI_CONTINUE_ON_ERROR="false" || CI_CONTINUE_ON_ERROR="true"
|
|
690
|
+
|
|
691
|
+
# Merge tool precheck BEFORE the lock/write window (69 with named remediation).
|
|
692
|
+
need_merge=0
|
|
693
|
+
for g in $GUARDS; do case "$(_guard_field "$g" 3)" in *hook*) need_merge=1 ;; esac; done
|
|
694
|
+
if [[ "$need_merge" -eq 1 ]] && ! command -v node >/dev/null 2>&1 && ! command -v jq >/dev/null 2>&1; then
|
|
695
|
+
die "no node and no jq on PATH — cannot merge .claude/settings.json; install either or deselect the hook guards" 69
|
|
696
|
+
fi
|
|
697
|
+
# Malformed existing settings.json refused pre-write (AC7).
|
|
698
|
+
if [[ -f "$SETTINGS" ]]; then
|
|
699
|
+
if command -v node >/dev/null 2>&1; then
|
|
700
|
+
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' "$SETTINGS" 2>/dev/null \
|
|
701
|
+
|| die ".claude/settings.json is not valid JSON — refusing before any write" 69
|
|
702
|
+
elif command -v jq >/dev/null 2>&1; then
|
|
703
|
+
jq -e . "$SETTINGS" >/dev/null 2>&1 || die ".claude/settings.json is not valid JSON — refusing before any write" 69
|
|
704
|
+
fi
|
|
705
|
+
fi
|
|
706
|
+
|
|
707
|
+
# Refuse-if-exists over every emit path (deduped), BEFORE anything is written.
|
|
708
|
+
# .claude/settings.json is merged, never refused. Doc files are append-once.
|
|
709
|
+
collide="" seen=""
|
|
710
|
+
for g in $GUARDS; do
|
|
711
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$g" 8)"
|
|
712
|
+
for p in "${ep[@]}"; do
|
|
713
|
+
[[ "$p" == ".claude/settings.json" ]] && continue
|
|
714
|
+
case " $seen " in *" $p "*) continue ;; esac
|
|
715
|
+
seen="$seen $p"
|
|
716
|
+
[[ -e "$TARGET/$p" ]] && collide="$collide $p"
|
|
717
|
+
done
|
|
718
|
+
done
|
|
719
|
+
[[ -n "$collide" ]] && die "refuse-if-exists:${collide} already present (no --force). Remove or --uninstall the owning guard first." 66
|
|
720
|
+
|
|
721
|
+
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
722
|
+
say "dry-run — would emit into $TARGET (mode=$MODE, default-branches=$DEFAULT_BRANCHES):"
|
|
723
|
+
for g in $GUARDS; do
|
|
724
|
+
say " [$g]"
|
|
725
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$g" 8)"
|
|
726
|
+
for p in "${ep[@]}"; do say " $p"; done
|
|
727
|
+
done
|
|
728
|
+
if [[ "$need_merge" -eq 1 ]]; then
|
|
729
|
+
say " merge .claude/settings.json (PreToolUse Bash):"
|
|
730
|
+
for g in $GUARDS; do
|
|
731
|
+
IFS=';' read -ra ep <<<"$(_guard_field "$g" 8)"
|
|
732
|
+
for p in "${ep[@]}"; do case "$p" in .claude/hooks/*.sh) say " hook: $p" ;; esac; done
|
|
733
|
+
done
|
|
734
|
+
fi
|
|
735
|
+
say " doc block → VERIFICATION.md (else README.md marker block), written last"
|
|
736
|
+
say " then a TMPDIR detached-worktree bite-proof per guard (pass -> fail -> pass)"
|
|
737
|
+
exit 0
|
|
738
|
+
fi
|
|
739
|
+
|
|
740
|
+
# Arm self-cleanup before the FIRST write.
|
|
741
|
+
CLEANUP_ARMED=1
|
|
742
|
+
trap '_vs_cleanup' EXIT
|
|
743
|
+
trap '_vs_cleanup interrupted; trap - EXIT; exit 143' INT TERM
|
|
744
|
+
take_run_lock
|
|
745
|
+
|
|
746
|
+
# Emission order (Guard 5): hooks+scripts+aux → settings merge → workflow → bite → doc.
|
|
747
|
+
ensure_dir "$TARGET/.claude/hooks"
|
|
748
|
+
ensure_dir "$TARGET/scripts/verification"
|
|
749
|
+
ensure_dir "$TARGET/.github/workflows"
|
|
750
|
+
for g in $GUARDS; do
|
|
751
|
+
emit_guard "$g"
|
|
752
|
+
log "emitted guard: $g"
|
|
753
|
+
done
|
|
754
|
+
[[ "${#HOOK_COMMANDS[@]}" -gt 0 ]] && merge_settings
|
|
755
|
+
need_workflow=0
|
|
756
|
+
for g in $GUARDS; do case "$(_guard_field "$g" 3)" in *ci*) need_workflow=1 ;; esac; done
|
|
757
|
+
if [[ "$need_workflow" -eq 1 ]]; then
|
|
758
|
+
render_workflow "$TARGET/.github/workflows/verification-gates.yml"
|
|
759
|
+
log "emitted: .github/workflows/verification-gates.yml"
|
|
760
|
+
fi
|
|
761
|
+
|
|
762
|
+
# Persist the created-dir ledger so --uninstall can honor "removed only if the
|
|
763
|
+
# run created them AND they're empty" (SpecFlow #3).
|
|
764
|
+
if [[ -s "$DIRS_LOG" ]]; then
|
|
765
|
+
ensure_dir "$TARGET/.claude"
|
|
766
|
+
cp "$DIRS_LOG" "$TARGET/.claude/.verification-scaffold-dirs"
|
|
767
|
+
record_emit "$TARGET/.claude/.verification-scaffold-dirs"
|
|
768
|
+
fi
|
|
769
|
+
|
|
770
|
+
# Bite-proof, then prose. A repo with no commits cannot host a detached worktree
|
|
771
|
+
# — skip loudly rather than pretend (the emitted artifacts still refuse-if-exists).
|
|
772
|
+
if git -C "$TARGET" rev-parse --verify HEAD >/dev/null 2>&1; then
|
|
773
|
+
prove_bite
|
|
774
|
+
else
|
|
775
|
+
say "bite-proof: SKIP — HEAD does not resolve (no commits yet); the installed guards are unproven until the first commit, then re-run with --guards none semantics: verify by hand"
|
|
776
|
+
fi
|
|
777
|
+
|
|
778
|
+
release_run_lock
|
|
779
|
+
CLEANUP_ARMED=0
|
|
780
|
+
trap - EXIT INT TERM
|
|
781
|
+
|
|
782
|
+
emit_doc
|
|
783
|
+
rm -rf -- "$WORK"
|
|
784
|
+
say "done (mode=$MODE). Guards: ${GUARDS}. Promote per VERIFICATION.md after >=1 clean run."
|
|
785
|
+
exit 0
|