@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,556 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Inventory and shrink-only ratchet for signal helpers inside shell test suites.
|
|
3
|
+
|
|
4
|
+
WHY THIS EXISTS
|
|
5
|
+
---------------
|
|
6
|
+
A helper that walks `$PPID` upwards and signals the outermost ancestor whose argv matches is bounded
|
|
7
|
+
only by its own predicate. Mutate the predicate (invert it, drop it, make it a catch-all) and every
|
|
8
|
+
ancestor matches: the walk reaches the top of the user's desktop session. That happened four times on
|
|
9
|
+
2026-10-09 (the learning recorded that day under the learnings test-failures category: "an inverted-match
|
|
10
|
+
mutant of an ancestor-walking helper ended the desktop session four times"). This scan lists the suites that
|
|
11
|
+
contain such a helper, and the other signal-to-ancestors patterns, BEFORE anyone mutates them, and
|
|
12
|
+
fails the full battery when a new one lands unbaselined or an unbounded walker is baselined.
|
|
13
|
+
|
|
14
|
+
USAGE
|
|
15
|
+
-----
|
|
16
|
+
scan-ancestor-signal-helpers.py [--root DIR] [--baseline FILE] [--list] [--write-baseline FILE] [PATH ...]
|
|
17
|
+
|
|
18
|
+
* Population: the tracked files matching the pathspecs in PATHSPECS (`*.test.sh`, `test-*.sh`,
|
|
19
|
+
`test_*.sh` and the mutation-run names `*-mutation-battery.sh`, `*.mutation.sh`, `*-mutations.sh`),
|
|
20
|
+
read from ONE git listing (NUL separated) made with every GIT_* variable removed from the child
|
|
21
|
+
environment, or the explicit PATHs given (paths are used as given and shown as given).
|
|
22
|
+
* Without --baseline the scan is list-only: rc 0 and the notice `no baseline: listing only`, so a
|
|
23
|
+
plugin consumer running it on their own repository does not see every site as new.
|
|
24
|
+
* With --baseline the verdict is gated on classes W, P and G (below). Exit 0 clean, 1 findings,
|
|
25
|
+
3 UNRESOLVED (empty population, failed git, unreadable or malformed baseline, a listed member that
|
|
26
|
+
is missing or was skipped (symlink, special file, over 2 MB: an unread file is an unchecked file), or
|
|
27
|
+
any uncaught Exception or KeyboardInterrupt: a traceback from those can never read as rc 1 "findings").
|
|
28
|
+
A BaseException subclass outside those (GeneratorExit, CancelledError) is unreachable from this synchronous,
|
|
29
|
+
stdlib-only scan and is deliberately not caught: it would exit 1 with a traceback. A usage error is argparse's rc 2.
|
|
30
|
+
* --list prints `class TAB bounded TAB path TAB text` for every finding of every class (bounded is
|
|
31
|
+
yes or no for W and a dash for the others); `--list PATH...` scopes it to the given files, which is
|
|
32
|
+
how a mutation seat checks the one file it is about to mutate.
|
|
33
|
+
* --write-baseline FILE writes the gated rows of the tracked population (overwrites the whole file:
|
|
34
|
+
review the diff). It refuses (rc 2) with PATHs or --list, refuses while any W is unbounded, and refuses
|
|
35
|
+
(rc 3) while any member was skipped.
|
|
36
|
+
* In explicit-PATH mode only baseline rows whose path is among the given PATHs are judged stale.
|
|
37
|
+
|
|
38
|
+
CLASSES (a finding is one source line, content-keyed)
|
|
39
|
+
-----------------------------------------------------
|
|
40
|
+
W (gated) ancestor walk with a signal: a self-referential cursor assignment (v=$(... /proc/$v/stat
|
|
41
|
+
...), v=$(ps -o ppid= -p $v), v=$(... $v ... PPid ...)) with kill, pkill or killall within
|
|
42
|
+
40 lines either side (raw text, so heredoc and string-embedded helpers count). Reported
|
|
43
|
+
bounded=yes only when a NON-COMMENT line from 15 lines above to 3 lines below the cursor
|
|
44
|
+
line mentions SOLEUR_TEST_SUITE_PID in a comparison (!=, ==, =, -ne, -eq, -lt, -le, -gt, -ge)
|
|
45
|
+
together with exit, break or return on that line, or in a while/until loop condition. A
|
|
46
|
+
mention only in a comment, an echo or a printf, or a presence, emptiness or numeric-validation
|
|
47
|
+
test ([ -n ], [ -z ], =~), reads bounded=no. bounded=yes is a LEXICAL
|
|
48
|
+
check: it cannot see that an unset variable means "signal nothing" or that a trailing
|
|
49
|
+
`|| true` defeats the comparison.
|
|
50
|
+
P (gated) a signal to the parent: kill and $PPID (or ps -o ppid=) on one line, or a variable assigned
|
|
51
|
+
from $PPID / ps -o ppid= within 20 lines above a kill that names it.
|
|
52
|
+
G (gated) an all-process or own-group signal at command position: kill [-SIG] 0 and kill [-SIG] -1.
|
|
53
|
+
A line over 4 KB that contains a signal verb is reported here too (fail closed).
|
|
54
|
+
L (listed) targeted group signals (kill -- -<pgid>) and name-pattern signals (pkill, killall). Never
|
|
55
|
+
gated, no baseline: they are not signals to ancestors.
|
|
56
|
+
Never findings: comment lines, signal verbs inside quoted spans (except the body of sh -c, bash -c,
|
|
57
|
+
eval and trap strings, which are scanned as code), kill -0, kill -l. A verb may carry an absolute path
|
|
58
|
+
and the usual prefixes (sudo, env, VAR=x, time, nice, setsid, timeout with options).
|
|
59
|
+
|
|
60
|
+
READING MODEL
|
|
61
|
+
-------------
|
|
62
|
+
Files are decoded with errors="surrogateescape" and split on "\\n" only (never str.splitlines(), which
|
|
63
|
+
splits on separators bash does not), with "\\r" stripped. A line over 4 KB gets no regex work (counted as
|
|
64
|
+
overlong; a substring pre-filter still reports a signal verb on it), a file over 2 MB, a symlink or a
|
|
65
|
+
special file is counted skipped. Only the Python standard library is used and nothing from the scanned
|
|
66
|
+
tree is imported or executed: the scan reads text and sends no signal.
|
|
67
|
+
|
|
68
|
+
BASELINE ROWS
|
|
69
|
+
-------------
|
|
70
|
+
`<path> TAB <class> TAB <normalised line>` (whitespace collapsed, a trailing comment cut), matched as a
|
|
71
|
+
multiset against the live gated findings. New, stale and unbounded are red; a baselined W that reads
|
|
72
|
+
bounded=no is red (an unbounded walker cannot be baselined).
|
|
73
|
+
|
|
74
|
+
DOCUMENTED BLIND SPOTS
|
|
75
|
+
----------------------
|
|
76
|
+
Multi-line quoted strings and backslash-newline continuations (kill \\ then -1); a walk written in
|
|
77
|
+
another language (os.getppid, os.kill) or through read, printf -v, pgrep -P, a function or $PROC_ROOT; a
|
|
78
|
+
two-step cursor (parent=$(...); cur=$parent); a kill more than 40 lines from the cursor, a cursor line
|
|
79
|
+
over 4 KB, or a kill reached through a function, a variable verb ($K), kill $(...) or xargs kill; a
|
|
80
|
+
parent held in a variable made by read, printf -v or let, or more than 20 lines above its kill; helpers
|
|
81
|
+
sourced from a non-suite file; scripts that match none of the name shapes (a helper in a lib or a hook);
|
|
82
|
+
fixtures stored under another extension; signal-delivering programs other than kill, pkill and killall
|
|
83
|
+
(systemctl --user, loginctl, tmux kill-server, fuser -k, docker kill, killall5). The scan is a finding
|
|
84
|
+
aid plus a ratchet on the lines it can see, not a proof of absence, and class L is listed but never
|
|
85
|
+
gated. The producer lists tracked files only: an untracked suite is not scanned until it is added.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
import argparse
|
|
89
|
+
import bisect
|
|
90
|
+
import collections
|
|
91
|
+
import contextlib
|
|
92
|
+
import os
|
|
93
|
+
import re
|
|
94
|
+
import stat
|
|
95
|
+
import subprocess
|
|
96
|
+
import sys
|
|
97
|
+
|
|
98
|
+
TOKEN = "SOLEUR_TEST_SUITE_PID"
|
|
99
|
+
KILL_WINDOW = 40 # a signal within this many lines of a W cursor
|
|
100
|
+
BOUND_ABOVE = 15 # bound token window above the cursor line
|
|
101
|
+
BOUND_BELOW = 3 # bound token window below the cursor line
|
|
102
|
+
DERIVED_WINDOW = 20 # parent variable assigned within this many lines above its kill
|
|
103
|
+
MAX_LINE = 4096
|
|
104
|
+
MAX_FILE = 2 * 1024 * 1024
|
|
105
|
+
GATED = ("W", "P", "G")
|
|
106
|
+
PATHSPECS = [
|
|
107
|
+
"*.test.sh", ":(glob)**/test-*.sh", ":(glob)**/test_*.sh",
|
|
108
|
+
":(glob)**/*-mutation-battery.sh", ":(glob)**/*.mutation.sh", ":(glob)**/*-mutations.sh",
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
# A signal verb at command position (the text is quote-masked first).
|
|
112
|
+
CMDPOS = re.compile(
|
|
113
|
+
r"(?:^|[;&|(){`!]|\$\(|\b(?:then|do|else|if|elif|while|until)\b)\s*"
|
|
114
|
+
r"(?:(?:\w+=\S*|sudo|exec|command|builtin|nohup|time|nice|setsid|env)\s+(?:-\S+\s+)*"
|
|
115
|
+
r"|timeout\s+(?:[-\dA-Z][^\s=]*\s+){1,4}|\\)*"
|
|
116
|
+
r"(?:[\w.+~/-]*/)?(kill|pkill|killall)(?![\w./-])"
|
|
117
|
+
)
|
|
118
|
+
# A -c / eval / trap body: a quoted span that runs as shell text and is masked in the outer line.
|
|
119
|
+
BODY = re.compile(r"(?:\b(?:sh|bash|dash|zsh)\s+-\w*c\w*|\beval|\btrap)\s+(['\"])(.*?)\1")
|
|
120
|
+
# A signal word anywhere in raw code (used only for the W window; string-embedded helpers count).
|
|
121
|
+
LOOSE_SIGNAL = re.compile(r"(?<![\w.-])(?:kill|pkill|killall)(?![\w./-])(?:\s+(?!-0\b|-l\b|-L\b|-s\s+0\b)|$)")
|
|
122
|
+
CURSOR = re.compile(r"(?:^|[\s;&|({])(?:local\s+|export\s+|declare\s+-\w+\s+)?(\w+)=(?:\"?\$\(|`)")
|
|
123
|
+
PPID_ASSIGN = re.compile(
|
|
124
|
+
r"(?:^|[\s;&|({])(?:local\s+|export\s+|readonly\s+|declare\s+-\w+\s+)?(\w+)=(?:\"?\$\{?PPID\b|\"?\$\(.{0,200}?\b(?i:ppid)=|`[^`]{0,200}\b(?i:ppid)=)"
|
|
125
|
+
)
|
|
126
|
+
PPID_USE = re.compile(r"\$\{?PPID\b|\bps\b[^;|&)]*\b(?i:ppid)=")
|
|
127
|
+
TARGET_VAR = re.compile(r"^\$\{?(\w+)\}?$")
|
|
128
|
+
CMP = re.compile(r"(?:^|\s)(?:!=|==|=|-ne|-eq|-lt|-le|-gt|-ge)(?:\s|$)")
|
|
129
|
+
EXIT_WORD = re.compile(r"\b(?:exit|break|return)\b")
|
|
130
|
+
LOOP_WORD = re.compile(r"\b(?:while|until)\b")
|
|
131
|
+
# Only lines that carry one of these can matter to a detector or a window: the rest are never analysed.
|
|
132
|
+
KEYS = ("kill", "PPID", "ppid", "PPid", "/proc/", TOKEN)
|
|
133
|
+
NOT_A_BOUND = re.compile(r"^\s*(?:echo|printf|print|cat|:)\b")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Unresolved(Exception):
|
|
137
|
+
"""The scan could not produce a verdict (rc 3)."""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
Finding = collections.namedtuple("Finding", "path cls text bounded lineno")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def normalise(text):
|
|
144
|
+
return " ".join(text.split())
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def analyse(line):
|
|
148
|
+
"""Return (code, masked): code is the line cut at an unquoted comment start; masked is code with the
|
|
149
|
+
contents of quoted spans blanked (same length, so indexes map between the two)."""
|
|
150
|
+
code = []
|
|
151
|
+
masked = []
|
|
152
|
+
quote = None
|
|
153
|
+
i = 0
|
|
154
|
+
n = len(line)
|
|
155
|
+
while i < n:
|
|
156
|
+
c = line[i]
|
|
157
|
+
if quote is None:
|
|
158
|
+
if c == "\\" and i + 1 < n:
|
|
159
|
+
code.append(line[i:i + 2])
|
|
160
|
+
masked.append(line[i:i + 2])
|
|
161
|
+
i += 2
|
|
162
|
+
continue
|
|
163
|
+
if c == "#" and (i == 0 or line[i - 1].isspace() or line[i - 1] in ";&|"):
|
|
164
|
+
break
|
|
165
|
+
if c == "'" or c == '"':
|
|
166
|
+
quote = c
|
|
167
|
+
code.append(c)
|
|
168
|
+
masked.append(c)
|
|
169
|
+
else:
|
|
170
|
+
if quote == '"' and c == "\\" and i + 1 < n:
|
|
171
|
+
code.append(line[i:i + 2])
|
|
172
|
+
masked.append(" ")
|
|
173
|
+
i += 2
|
|
174
|
+
continue
|
|
175
|
+
if c == quote:
|
|
176
|
+
quote = None
|
|
177
|
+
code.append(c)
|
|
178
|
+
masked.append(c)
|
|
179
|
+
else:
|
|
180
|
+
code.append(c)
|
|
181
|
+
masked.append(" ")
|
|
182
|
+
i += 1
|
|
183
|
+
return "".join(code), "".join(masked)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def arg_tokens(code, masked, start):
|
|
187
|
+
"""The tokens after a verb, cut at the next unquoted terminator or redirection."""
|
|
188
|
+
end = len(masked)
|
|
189
|
+
for j in range(start, len(masked)):
|
|
190
|
+
if masked[j] in ";&|)}`<>":
|
|
191
|
+
end = j
|
|
192
|
+
break
|
|
193
|
+
toks = []
|
|
194
|
+
for raw in code[start:end].split():
|
|
195
|
+
toks.append(raw.strip("'\""))
|
|
196
|
+
return toks
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def classify_kill(toks, is_derived):
|
|
200
|
+
"""The class of one kill invocation, or None (a probe, a listing, or a plain pid). is_derived(var) says
|
|
201
|
+
whether var was assigned from the parent pid within DERIVED_WINDOW lines above this kill."""
|
|
202
|
+
if not toks:
|
|
203
|
+
return None
|
|
204
|
+
first = toks[0]
|
|
205
|
+
i = 0
|
|
206
|
+
if first in ("-l", "-L", "--list", "--table"):
|
|
207
|
+
return None
|
|
208
|
+
if first in ("-s", "-n", "--signal"):
|
|
209
|
+
if len(toks) < 2:
|
|
210
|
+
return None
|
|
211
|
+
if toks[1] == "0":
|
|
212
|
+
return None
|
|
213
|
+
i = 2
|
|
214
|
+
elif first == "-0":
|
|
215
|
+
return None
|
|
216
|
+
elif first.startswith("-") and first != "--":
|
|
217
|
+
i = 1
|
|
218
|
+
if i < len(toks) and toks[i] == "--":
|
|
219
|
+
i += 1
|
|
220
|
+
targets = toks[i:]
|
|
221
|
+
if PPID_USE.search(" ".join(toks)):
|
|
222
|
+
return "P"
|
|
223
|
+
kind = None
|
|
224
|
+
for t in targets:
|
|
225
|
+
if t in ("0", "-1"):
|
|
226
|
+
return "G"
|
|
227
|
+
m = TARGET_VAR.match(t) or (TARGET_VAR.match(t[1:]) if t.startswith("-") else None)
|
|
228
|
+
if m and is_derived(m.group(1)):
|
|
229
|
+
return "P"
|
|
230
|
+
if t.startswith("-") and kind is None:
|
|
231
|
+
kind = "L"
|
|
232
|
+
return kind
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def derived_at(assigns, var, i):
|
|
236
|
+
idxs = assigns.get(var)
|
|
237
|
+
if not idxs:
|
|
238
|
+
return False
|
|
239
|
+
k = bisect.bisect_left(idxs, i - DERIVED_WINDOW)
|
|
240
|
+
return k < len(idxs) and idxs[k] <= i
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def is_bound_line(code):
|
|
244
|
+
if TOKEN not in code or NOT_A_BOUND.match(code):
|
|
245
|
+
return False
|
|
246
|
+
if not CMP.search(code):
|
|
247
|
+
return False
|
|
248
|
+
return bool(EXIT_WORD.search(code) or LOOP_WORD.search(code))
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def scan_text(path, text, counters):
|
|
252
|
+
lines = [ln.rstrip("\r") for ln in text.split("\n")]
|
|
253
|
+
counters["overlong"] += sum(1 for ln in lines if len(ln) > MAX_LINE)
|
|
254
|
+
findings = []
|
|
255
|
+
if "kill" not in text:
|
|
256
|
+
return findings
|
|
257
|
+
n = len(lines)
|
|
258
|
+
code = [""] * n
|
|
259
|
+
masked = [""] * n
|
|
260
|
+
for i, ln in enumerate(lines):
|
|
261
|
+
if len(ln) > MAX_LINE:
|
|
262
|
+
if LOOSE_SIGNAL.search(ln):
|
|
263
|
+
head = normalise(ln[:200])[:100]
|
|
264
|
+
findings.append(Finding(path, "G", "<overlong line, %d chars, carries a signal verb> %s" % (len(ln), head), "-", i + 1))
|
|
265
|
+
continue
|
|
266
|
+
if not any(k in ln for k in KEYS):
|
|
267
|
+
continue
|
|
268
|
+
if ln.lstrip().startswith("#"):
|
|
269
|
+
continue
|
|
270
|
+
code[i], masked[i] = analyse(ln)
|
|
271
|
+
|
|
272
|
+
assigns = collections.defaultdict(list)
|
|
273
|
+
for i in range(n):
|
|
274
|
+
if "PPID" in code[i] or "ppid=" in code[i].lower():
|
|
275
|
+
for m in PPID_ASSIGN.finditer(code[i]):
|
|
276
|
+
assigns[m.group(1)].append(i)
|
|
277
|
+
|
|
278
|
+
for i in range(n):
|
|
279
|
+
if not code[i]:
|
|
280
|
+
continue
|
|
281
|
+
# signal verbs at command position
|
|
282
|
+
for m in CMDPOS.finditer(masked[i]):
|
|
283
|
+
verb = m.group(1)
|
|
284
|
+
toks = arg_tokens(code[i], masked[i], m.end())
|
|
285
|
+
if verb in ("pkill", "killall"):
|
|
286
|
+
cls = "L"
|
|
287
|
+
else:
|
|
288
|
+
cls = classify_kill(toks, lambda v, i=i: derived_at(assigns, v, i))
|
|
289
|
+
if cls:
|
|
290
|
+
findings.append(Finding(path, cls, normalise(code[i]), "-", i + 1))
|
|
291
|
+
# the same detectors over the body of sh -c '...', eval "..." and trap '...' (masked in the outer line)
|
|
292
|
+
for bm in BODY.finditer(code[i]):
|
|
293
|
+
bcode, bmasked = analyse(bm.group(2))
|
|
294
|
+
for m in CMDPOS.finditer(bmasked):
|
|
295
|
+
verb = m.group(1)
|
|
296
|
+
toks = arg_tokens(bcode, bmasked, m.end())
|
|
297
|
+
if verb in ("pkill", "killall"):
|
|
298
|
+
cls = "L"
|
|
299
|
+
else:
|
|
300
|
+
cls = classify_kill(toks, lambda v, i=i: derived_at(assigns, v, i))
|
|
301
|
+
if cls:
|
|
302
|
+
findings.append(Finding(path, cls, normalise(code[i]), "-", i + 1))
|
|
303
|
+
# W cursor: a self-referential assignment, with a signal nearby
|
|
304
|
+
if ("/proc/" in code[i] or "ppid" in code[i].lower()) and ("=$(" in code[i] or '="$(' in code[i] or "=`" in code[i]):
|
|
305
|
+
for m in CURSOR.finditer(code[i]):
|
|
306
|
+
v = m.group(1)
|
|
307
|
+
tail = code[i][m.end():]
|
|
308
|
+
ref = re.compile(r"\$\{?%s\b" % re.escape(v))
|
|
309
|
+
form1 = re.search(r"/proc/\$\{?%s\}?/(?:stat|status)" % re.escape(v), tail)
|
|
310
|
+
form2 = re.search(r"\bps\b", tail) and re.search(r"ppid", tail) and ref.search(tail)
|
|
311
|
+
form3 = "PPid" in tail and ref.search(tail)
|
|
312
|
+
if not (form1 or form2 or form3):
|
|
313
|
+
continue
|
|
314
|
+
lo = max(0, i - KILL_WINDOW)
|
|
315
|
+
hi = min(n, i + KILL_WINDOW + 1)
|
|
316
|
+
if not any(code[j] and LOOSE_SIGNAL.search(code[j]) for j in range(lo, hi)):
|
|
317
|
+
continue
|
|
318
|
+
blo = max(0, i - BOUND_ABOVE)
|
|
319
|
+
bhi = min(n, i + BOUND_BELOW + 1)
|
|
320
|
+
bounded = "yes" if any(is_bound_line(code[j]) for j in range(blo, bhi)) else "no"
|
|
321
|
+
findings.append(Finding(path, "W", normalise(code[i]), bounded, i + 1))
|
|
322
|
+
break
|
|
323
|
+
findings.sort(key=lambda f: (f.lineno, f.cls))
|
|
324
|
+
return findings
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def git_population(root):
|
|
328
|
+
env = {k: v for k, v in os.environ.items() if not k.startswith("GIT_")}
|
|
329
|
+
cmd = ["git", "-C", root, "ls-files", "-z", "--"] + PATHSPECS
|
|
330
|
+
try:
|
|
331
|
+
cp = subprocess.run(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=120)
|
|
332
|
+
except (OSError, subprocess.SubprocessError) as exc:
|
|
333
|
+
raise Unresolved("git listing failed: %s" % exc)
|
|
334
|
+
if cp.returncode != 0:
|
|
335
|
+
msg = cp.stderr.decode("utf-8", "replace").strip().splitlines()
|
|
336
|
+
raise Unresolved("git listing failed (rc %d): %s" % (cp.returncode, msg[0] if msg else "no message"))
|
|
337
|
+
names = sorted({os.fsdecode(p) for p in cp.stdout.split(b"\0") if p})
|
|
338
|
+
if not names:
|
|
339
|
+
raise Unresolved("the producer returned no files (no tracked *.test.sh or test-*.sh under %s)" % root)
|
|
340
|
+
return names
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def read_member(display, full, counters):
|
|
344
|
+
try:
|
|
345
|
+
st = os.lstat(full)
|
|
346
|
+
except OSError as exc:
|
|
347
|
+
raise Unresolved("listed member cannot be read: %s (%s)" % (display, exc.strerror))
|
|
348
|
+
if not stat.S_ISREG(st.st_mode):
|
|
349
|
+
counters["skipped"] += 1
|
|
350
|
+
return None
|
|
351
|
+
if st.st_size > MAX_FILE:
|
|
352
|
+
counters["skipped"] += 1
|
|
353
|
+
return None
|
|
354
|
+
try:
|
|
355
|
+
with open(full, "rb") as fh:
|
|
356
|
+
data = fh.read()
|
|
357
|
+
except OSError as exc:
|
|
358
|
+
raise Unresolved("listed member cannot be read: %s (%s)" % (display, exc.strerror))
|
|
359
|
+
return data.decode("utf-8", "surrogateescape")
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def read_baseline(path):
|
|
363
|
+
rows = collections.Counter()
|
|
364
|
+
try:
|
|
365
|
+
with open(path, "rb") as fh:
|
|
366
|
+
raw = fh.read().decode("utf-8", "surrogateescape")
|
|
367
|
+
except OSError as exc:
|
|
368
|
+
raise Unresolved("baseline unreadable: %s (%s)" % (path, exc.strerror))
|
|
369
|
+
for n, ln in enumerate(raw.split("\n"), 1):
|
|
370
|
+
ln = ln.rstrip("\r")
|
|
371
|
+
if not ln.strip() or ln.startswith("#"):
|
|
372
|
+
continue
|
|
373
|
+
parts = ln.split("\t")
|
|
374
|
+
if len(parts) != 3 or parts[1] not in GATED or not parts[0]:
|
|
375
|
+
raise Unresolved("baseline row %d is malformed (want path TAB class TAB text, class W/P/G): %s" % (n, path))
|
|
376
|
+
rows[(parts[0], parts[1], parts[2])] += 1
|
|
377
|
+
return rows
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
BASELINE_HEADER = """\
|
|
381
|
+
# ancestor-signal baseline: one row per gated finding, <path> TAB <class> TAB <normalised line>.
|
|
382
|
+
# Classes: W ancestor walk that signals (must read bounded=yes), P signal to the parent, G all-process signal.
|
|
383
|
+
# Maintenance: an in-place replacement of an existing row (same path and class, e.g. after an unrelated edit of
|
|
384
|
+
# that line) is allowed; a net addition needs a reviewer's read; otherwise the file may only shrink. A stale row
|
|
385
|
+
# (no live line) and an unbounded W are red. bounded=yes is LEXICAL (a non-comment SOLEUR_TEST_SUITE_PID
|
|
386
|
+
# comparison with exit, break or return near the walk), not proof of containment.
|
|
387
|
+
# Regenerate: python3 plugins/soleur/scripts/scan-ancestor-signal-helpers.py --write-baseline <this file>
|
|
388
|
+
# (overwrites the whole file: review the diff row by row).
|
|
389
|
+
"""
|
|
390
|
+
|
|
391
|
+
REMEDY = (
|
|
392
|
+
"remedy: new W: bound the walk with a non-comment test such as [ \"$p\" = \"$SOLEUR_TEST_SUITE_PID\" ] plus exit, break or return "
|
|
393
|
+
"on that line, then add one baseline row ($$ comparisons read bounded=no; the suite's F10 floor is a minimum, not a pin). "
|
|
394
|
+
"new P or G: rewrite to a nonce-scoped target, or baseline it with a reviewer's read. "
|
|
395
|
+
"stale: delete or swap the row. unbounded: bounding is mandatory, baselining is refused. "
|
|
396
|
+
"--write-baseline overwrites the whole file, so review the diff; untracked files are not scanned "
|
|
397
|
+
"(the producer lists tracked files: git add first). See the section 'Mutating a signalling or "
|
|
398
|
+
"file-removing helper' in plugins/soleur/skills/work/references/work-scratch-sandboxes.md."
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def out(msg=""):
|
|
403
|
+
sys.stdout.write(msg + "\n")
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def run(argv):
|
|
407
|
+
ap = argparse.ArgumentParser(prog="scan-ancestor-signal-helpers.py", description="Inventory of signal helpers in shell test suites.")
|
|
408
|
+
ap.add_argument("--root", default=".", help="repository root for the tracked-file listing (default: the current directory)")
|
|
409
|
+
ap.add_argument("--baseline", help="baseline file; without it the scan is list-only")
|
|
410
|
+
ap.add_argument("--list", action="store_true", help="print every finding of every class and exit 0")
|
|
411
|
+
ap.add_argument("--write-baseline", metavar="FILE", help="write the gated rows to FILE")
|
|
412
|
+
ap.add_argument("paths", nargs="*", metavar="PATH", help="scan these files instead of the tracked listing")
|
|
413
|
+
args = ap.parse_args(argv)
|
|
414
|
+
|
|
415
|
+
counters = collections.Counter()
|
|
416
|
+
explicit = bool(args.paths)
|
|
417
|
+
names = list(args.paths) if explicit else git_population(args.root)
|
|
418
|
+
|
|
419
|
+
findings = []
|
|
420
|
+
roots = set()
|
|
421
|
+
nfiles = 0
|
|
422
|
+
for name in names:
|
|
423
|
+
full = name if explicit else os.path.join(args.root, name)
|
|
424
|
+
text = read_member(name, full, counters)
|
|
425
|
+
if text is None:
|
|
426
|
+
continue
|
|
427
|
+
nfiles += 1
|
|
428
|
+
if "/" in name:
|
|
429
|
+
roots.add(name.split("/", 1)[0])
|
|
430
|
+
findings.extend(scan_text(name, text, counters))
|
|
431
|
+
findings.sort(key=lambda f: (f.path, f.lineno, f.cls))
|
|
432
|
+
|
|
433
|
+
gated = [f for f in findings if f.cls in GATED]
|
|
434
|
+
listed = [f for f in findings if f.cls == "L"]
|
|
435
|
+
skipped = counters["skipped"]
|
|
436
|
+
|
|
437
|
+
if args.write_baseline:
|
|
438
|
+
if explicit or args.list:
|
|
439
|
+
sys.stderr.write("ancestor-signal scan: --write-baseline rewrites the whole file from the tracked population: pass no PATH and no --list (a partial write would drop every other row)\n")
|
|
440
|
+
return 2
|
|
441
|
+
if skipped:
|
|
442
|
+
raise Unresolved("%d listed member(s) were skipped: refusing to write a baseline that silently drops their rows" % skipped)
|
|
443
|
+
bad = [f for f in gated if f.cls == "W" and f.bounded == "no"]
|
|
444
|
+
if bad:
|
|
445
|
+
for f in bad:
|
|
446
|
+
out("UNBOUNDED W %s:%d: %s" % (f.path, f.lineno, f.text))
|
|
447
|
+
out("ancestor-signal scan: refusing to write a baseline that certifies an unbounded walker (UNBOUNDED above)")
|
|
448
|
+
out("ancestor-signal scan: " + REMEDY)
|
|
449
|
+
return 1
|
|
450
|
+
rows = sorted((f.path, f.cls, f.text) for f in gated)
|
|
451
|
+
tmp = args.write_baseline + ".tmp-%d" % os.getpid()
|
|
452
|
+
try:
|
|
453
|
+
with open(tmp, "wb") as fh:
|
|
454
|
+
fh.write(BASELINE_HEADER.encode("utf-8", "surrogateescape"))
|
|
455
|
+
for r in rows:
|
|
456
|
+
fh.write(("\t".join(r) + "\n").encode("utf-8", "surrogateescape"))
|
|
457
|
+
os.replace(tmp, args.write_baseline)
|
|
458
|
+
finally:
|
|
459
|
+
if os.path.exists(tmp):
|
|
460
|
+
os.unlink(tmp)
|
|
461
|
+
out("ancestor-signal scan: wrote %d baseline rows to %s" % (len(rows), args.write_baseline))
|
|
462
|
+
return 0
|
|
463
|
+
|
|
464
|
+
if args.list or not args.baseline:
|
|
465
|
+
for f in findings:
|
|
466
|
+
out("\t".join((f.cls, f.bounded, f.path, f.text)))
|
|
467
|
+
if counters["overlong"]:
|
|
468
|
+
out("ancestor-signal scan: overlong lines: %d" % counters["overlong"])
|
|
469
|
+
out("ancestor-signal scan: roots: %s" % " ".join(sorted(roots)))
|
|
470
|
+
out("ancestor-signal scan: %d files, %d findings, no baseline: listing only, %d skipped" % (nfiles, len(findings), skipped))
|
|
471
|
+
return 0
|
|
472
|
+
|
|
473
|
+
if skipped:
|
|
474
|
+
raise Unresolved("%d listed member(s) were skipped (a symlink, a special file or over %d bytes): a skipped file is an unchecked file, so no verdict" % (skipped, MAX_FILE))
|
|
475
|
+
base = read_baseline(args.baseline)
|
|
476
|
+
live = collections.Counter((f.path, f.cls, f.text) for f in gated)
|
|
477
|
+
if explicit:
|
|
478
|
+
scanned = set(names)
|
|
479
|
+
base = collections.Counter({k: v for k, v in base.items() if k[0] in scanned})
|
|
480
|
+
matched = sum((live & base).values())
|
|
481
|
+
new = live - base
|
|
482
|
+
stale = base - live
|
|
483
|
+
unbounded = [f for f in gated if f.cls == "W" and f.bounded == "no"]
|
|
484
|
+
|
|
485
|
+
seen = collections.Counter()
|
|
486
|
+
for f in gated:
|
|
487
|
+
key = (f.path, f.cls, f.text)
|
|
488
|
+
seen[key] += 1
|
|
489
|
+
if seen[key] > base.get(key, 0):
|
|
490
|
+
out("NEW %s %s:%d: %s" % (f.cls, f.path, f.lineno, f.text))
|
|
491
|
+
out(" row: %s" % "\t".join(key))
|
|
492
|
+
for key, cnt in sorted(stale.items()):
|
|
493
|
+
for _ in range(cnt):
|
|
494
|
+
out("STALE %s %s: %s" % (key[1], key[0], key[2]))
|
|
495
|
+
for f in unbounded:
|
|
496
|
+
out("UNBOUNDED W %s:%d: %s" % (f.path, f.lineno, f.text))
|
|
497
|
+
red = bool(new or stale or unbounded)
|
|
498
|
+
|
|
499
|
+
if counters["overlong"]:
|
|
500
|
+
out("ancestor-signal scan: overlong lines: %d" % counters["overlong"])
|
|
501
|
+
out("ancestor-signal scan: listed-only (class L): %d" % len(listed))
|
|
502
|
+
out("ancestor-signal scan: roots: %s" % " ".join(sorted(roots)))
|
|
503
|
+
out("ancestor-signal scan: %d files, %d findings, %d baselined, %d new, %d stale, %d unbounded, %d skipped" % (
|
|
504
|
+
nfiles, len(gated), matched, sum(new.values()), sum(stale.values()), len(unbounded), skipped))
|
|
505
|
+
if red:
|
|
506
|
+
out("ancestor-signal scan: " + REMEDY)
|
|
507
|
+
return 1
|
|
508
|
+
out("ancestor-signal scan: CLEAN")
|
|
509
|
+
return 0
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _note(exc, site):
|
|
513
|
+
# Class name and a static site label only. Never str(exc)/repr(exc)/input text: exception text can carry
|
|
514
|
+
# scanned content. `site` is a string literal at every call site.
|
|
515
|
+
# A closed, full or missing stderr (None when fd 2 is closed) must not turn a swallowed error into a crash.
|
|
516
|
+
with contextlib.suppress(OSError, ValueError, AttributeError):
|
|
517
|
+
sys.stderr.write("ancestor-signal scan: swallowed %s at %s\n" % (type(exc).__name__, site))
|
|
518
|
+
sys.stderr.flush()
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
def flush_stdout():
|
|
522
|
+
try:
|
|
523
|
+
sys.stdout.flush()
|
|
524
|
+
except (OSError, ValueError) as exc:
|
|
525
|
+
_note(exc, "flush-stdout") # a full or closed stdout must not hide the verdict line on stderr
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def main():
|
|
529
|
+
try:
|
|
530
|
+
# A replaced stream without reconfigure(): a capability probe, the negative result is normal.
|
|
531
|
+
with contextlib.suppress(AttributeError):
|
|
532
|
+
sys.stdout.reconfigure(errors="surrogateescape")
|
|
533
|
+
sys.stderr.reconfigure(errors="surrogateescape")
|
|
534
|
+
rc = run(sys.argv[1:])
|
|
535
|
+
sys.stdout.flush()
|
|
536
|
+
return rc
|
|
537
|
+
except SystemExit:
|
|
538
|
+
raise
|
|
539
|
+
except Unresolved as exc:
|
|
540
|
+
flush_stdout()
|
|
541
|
+
sys.stderr.write("ancestor-signal scan: UNRESOLVED: %s\n" % exc)
|
|
542
|
+
sys.stderr.flush()
|
|
543
|
+
os._exit(3)
|
|
544
|
+
except (Exception, KeyboardInterrupt) as exc: # an uncaught exception must read as rc 3, never as rc 1 "findings" (other BaseException subclasses: see the docstring)
|
|
545
|
+
try:
|
|
546
|
+
flush_stdout()
|
|
547
|
+
import traceback
|
|
548
|
+
traceback.print_exc()
|
|
549
|
+
sys.stderr.write("ancestor-signal scan: UNRESOLVED: uncaught %s: %s\n" % (type(exc).__name__, exc))
|
|
550
|
+
sys.stderr.flush()
|
|
551
|
+
finally:
|
|
552
|
+
os._exit(3)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
if __name__ == "__main__":
|
|
556
|
+
sys.exit(main())
|