@soleur/opencode 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +659 -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 +108 -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 +342 -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 +152 -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 +493 -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 +363 -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 +520 -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 +113 -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 +178 -0
- package/scripts/sync-opencode-adapter.ts +479 -0
- package/scripts/sync-pr-behind.sh +534 -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 +65 -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 +142 -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 +118 -0
- package/skills/fix-issue/references/exclude-label-jq-snippet.md +96 -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 +668 -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 +325 -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 +138 -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 +1621 -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-todo-structure.md +165 -0
- package/skills/review/references/risk-tier-and-fix-rounds.md +95 -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 +3022 -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/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/detached-battery-verdict.md +13 -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 +149 -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/work/SKILL.md +1479 -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,443 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Redact credential-shaped values out of an accessibility snapshot (#7947).
|
|
3
|
+
|
|
4
|
+
Reads a snapshot on stdin, writes the redacted form to stdout.
|
|
5
|
+
|
|
6
|
+
WHY THIS IS NAME-BASED AND NOT STRUCTURAL
|
|
7
|
+
-----------------------------------------
|
|
8
|
+
Phase 0.1 measured both leaking surfaces (record:
|
|
9
|
+
knowledge-base/project/specs/feat-one-shot-7946-7947-sentry-org-token-and-snapshot-redaction/phase-0-measurement.md).
|
|
10
|
+
Neither `agent-browser` 0.22.3 nor the Playwright MCP serializes the input's
|
|
11
|
+
`type=` into the tree -- a password box, a readonly token box and an email box
|
|
12
|
+
all render as `textbox`. So a structural "is this a password input" predicate
|
|
13
|
+
has nothing to read, and the accessible NAME is the only signal present.
|
|
14
|
+
|
|
15
|
+
That is a real ceiling, not a shortcut. This filter is defense-in-depth on one
|
|
16
|
+
enumerated sink; it is not a control that makes snapshotting a credential page
|
|
17
|
+
safe. See the STATED BYPASSES below and property P5 in the plan.
|
|
18
|
+
|
|
19
|
+
MEASURED BEHAVIOUR THIS FILTER IS BUILT AGAINST
|
|
20
|
+
-----------------------------------------------
|
|
21
|
+
* `agent-browser` masks `type=password` itself (renders bullets) and leaks
|
|
22
|
+
the readonly `type=text` credential panel -- the "Token" class, which is
|
|
23
|
+
the one with a recorded in-repo incident.
|
|
24
|
+
* The Playwright MCP leaks both.
|
|
25
|
+
* The no-flag and `-d N` shapes emit the value TWICE: once as the `: value`
|
|
26
|
+
tail and again as a nested `StaticText` child. Redacting only the tail is
|
|
27
|
+
half a fix, so children of a redacted node are suppressed too.
|
|
28
|
+
* `--json` carries the same text inside `data.snapshot`, but `diff snapshot
|
|
29
|
+
--json` carries it under `data.diff`, so the JSON arm recognises a tree by
|
|
30
|
+
SHAPE rather than by field name.
|
|
31
|
+
* `diff snapshot` prefixes a marker column (`+- textbox ...`) and colourises
|
|
32
|
+
the line with SGR escapes, both outside the bullet every regex anchors on.
|
|
33
|
+
* A multi-line value (a textarea holding a PEM block) emits its 2nd..Nth
|
|
34
|
+
lines raw at column 0, so suppression cannot key on indent alone.
|
|
35
|
+
* Playwright's YAML serialiser wraps the WHOLE key in single quotes when the
|
|
36
|
+
accessible name contains ` #`, `: `, `{`, `}`, a backtick or a control
|
|
37
|
+
character (`- 'textbox "API Key #1" [ref=e2]': value`, `''` escaping an
|
|
38
|
+
apostrophe). Every regex below anchors on `- role`, so the quoted key is
|
|
39
|
+
unwrapped before matching and re-quoted on output -- measured against the
|
|
40
|
+
real @playwright/mcp@0.0.78 server during the #7980 review, where a field
|
|
41
|
+
labelled "API Key #1" leaked in clear.
|
|
42
|
+
|
|
43
|
+
STATED BYPASSES (P5's explicit non-coverage -- do not read this filter as
|
|
44
|
+
closing them):
|
|
45
|
+
* a localised or renamed accessible name ("Contrasena", "Clave", "Value");
|
|
46
|
+
* an unnamed node whose role is NOT a text-input role (an unnamed text-input
|
|
47
|
+
node carrying a value IS redacted, on the fail-safe side);
|
|
48
|
+
* a credential rendered outside a text-input role (a status region, a
|
|
49
|
+
validation message, a `<pre>` block);
|
|
50
|
+
* a value split across segmented single-character inputs;
|
|
51
|
+
* every node on a Playwright-MCP registration NOT routed through
|
|
52
|
+
`playwright-mcp-redact-proxy.py` (the sibling transport proxy, #7980), which
|
|
53
|
+
this filter can only reach if a human pipes a saved snapshot through it.
|
|
54
|
+
|
|
55
|
+
FAIL-CLOSED CONTRACT (ADR-095 shape): on refusal, exit 2 with stdout EMPTY and
|
|
56
|
+
a reason on stderr that never quotes the offending input.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
from __future__ import annotations
|
|
60
|
+
|
|
61
|
+
import json
|
|
62
|
+
import re
|
|
63
|
+
import sys
|
|
64
|
+
|
|
65
|
+
REDACTED = "<redacted>"
|
|
66
|
+
|
|
67
|
+
# CONSUMER CONTRACT (#7980). `playwright-mcp-redact-proxy.py` loads this file by
|
|
68
|
+
# its sibling BASENAME (`REDACTOR_BASENAME` there -- renaming this file is a proxy
|
|
69
|
+
# edit too) with `importlib.util.spec_from_file_location` and binds exactly these
|
|
70
|
+
# four names at startup -- ALL FOUR, so a refactor that renames one fails loud
|
|
71
|
+
# at the proxy's exit 2 instead of as a per-result withhold that reads like an
|
|
72
|
+
# outage. The contract is declared and tested HERE, where it is provided
|
|
73
|
+
# (redact-a11y-snapshot.test.sh carries the import row); the proxy defines no
|
|
74
|
+
# predicate of its own -- this module is the one predicate for both surfaces.
|
|
75
|
+
__all__ = ["redact_text", "looks_like_a11y_tree", "MAX_INPUT_BYTES", "REDACTED"]
|
|
76
|
+
|
|
77
|
+
# 4 MiB. A real snapshot is kilobytes; anything at this size is not a snapshot,
|
|
78
|
+
# and refusing is cheaper than trying to redact it correctly.
|
|
79
|
+
MAX_INPUT_BYTES = 4 * 1024 * 1024
|
|
80
|
+
|
|
81
|
+
# Roles that can carry a user-editable value. A credential does not appear in a
|
|
82
|
+
# `heading` or a `link`, and widening this set widens the over-redaction risk
|
|
83
|
+
# that the must-PASS rows exist to catch.
|
|
84
|
+
TEXT_INPUT_ROLES = frozenset(
|
|
85
|
+
{"textbox", "searchbox", "combobox", "spinbutton", "textarea"}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Descendant roles that repeat a PARENT's value rather than carrying a label
|
|
89
|
+
# of their own. Measured: the no-flag and `-d N` shapes emit the value twice,
|
|
90
|
+
# the second time as a StaticText child. Everything else under a redacted
|
|
91
|
+
# node (an `option` label, say) is a label and must survive.
|
|
92
|
+
VALUE_CARRYING_CHILD_ROLES = frozenset({"StaticText"})
|
|
93
|
+
|
|
94
|
+
# Credential-shaped accessible names. Word-anchored on both sides so
|
|
95
|
+
# "Keyboard shortcut" does not match `key` and "Pinned items" does not match
|
|
96
|
+
# `pin`. Deliberately broader than "password": the only node `agent-browser`
|
|
97
|
+
# leaks is named "Token", so a password-only list would miss the single class
|
|
98
|
+
# with a recorded incident here.
|
|
99
|
+
CREDENTIAL_NAME_RE = re.compile(
|
|
100
|
+
r"\b(?:"
|
|
101
|
+
r"pass(?:word|wd|phrase|code)?"
|
|
102
|
+
r"|token"
|
|
103
|
+
r"|secret"
|
|
104
|
+
r"|credential"
|
|
105
|
+
r"|key"
|
|
106
|
+
r"|bearer"
|
|
107
|
+
r"|otp"
|
|
108
|
+
r"|totp"
|
|
109
|
+
r"|pin"
|
|
110
|
+
r"|2fa"
|
|
111
|
+
r"|mfa"
|
|
112
|
+
r"|two[\s_-]?factor"
|
|
113
|
+
r"|authenticator"
|
|
114
|
+
r"|seed[\s_-]?phrase"
|
|
115
|
+
r"|mnemonic"
|
|
116
|
+
r"|(?:verification|confirmation|one[\s_-]?time|backup|access|recovery"
|
|
117
|
+
r"|security|sms|authenticator|auth(?:entication)?|2fa|mfa|totp)[\s_-]?code"
|
|
118
|
+
r"|connection[\s_-]?string"
|
|
119
|
+
r"|dsn"
|
|
120
|
+
# `signature`, `session` and `cookie` are QUALIFIED, not bare. Bare, they
|
|
121
|
+
# ate `Email signature`, `Session name`, `Cookie name` and
|
|
122
|
+
# `Search sessions` -- measured. A filter that eats ordinary fields gets
|
|
123
|
+
# switched off, which costs more than the narrow miss. The bare words are
|
|
124
|
+
# still caught when they are the WHOLE name, in `_is_credential_name`.
|
|
125
|
+
r"|(?:request|webhook|hmac|jwt|api|payload)[\s_-]?signature"
|
|
126
|
+
r"|authorization"
|
|
127
|
+
r"|session[\s_-]?(?:token|id|key|secret|cookie|string)"
|
|
128
|
+
r"|cookie[\s_-]?(?:value|string|header|jar)"
|
|
129
|
+
r")e?s?\b",
|
|
130
|
+
re.IGNORECASE,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def _fmt_name(name: str | None) -> str:
|
|
134
|
+
"""Re-emit the name exactly as it arrived, including its absence."""
|
|
135
|
+
return "" if name is None else f' "{name}"'
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# Split a camelCase hump, and an acronym that runs INTO a word (`APIKey`), but
|
|
139
|
+
# never an acronym that is already a whole word. The naive
|
|
140
|
+
# `(?<=[a-z0-9])(?=[A-Z])` split `2FA` into `2 FA`, which silently un-matched
|
|
141
|
+
# the `2fa` alternative and leaked a field named `2FA code` -- caught only
|
|
142
|
+
# because the must-REDACT row was driven with the real label.
|
|
143
|
+
_CAMEL_BOUNDARY_RE = re.compile(r"(?<=[a-zA-Z0-9])(?=[A-Z][a-z])|(?<=[a-z])(?=[A-Z]+$)")
|
|
144
|
+
_WORD_SEP_RE = re.compile(r"[_\-]+")
|
|
145
|
+
|
|
146
|
+
# Bare words that are credential-shaped only when they are the ENTIRE accessible
|
|
147
|
+
# name. Qualified forms live in CREDENTIAL_NAME_RE; see the comment there.
|
|
148
|
+
WHOLE_NAME_CREDENTIALS = frozenset({"session", "cookie", "signature"})
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _normalise_name(name: str) -> str:
|
|
152
|
+
"""Split camelCase and `_`/`-` runs so one word list matches every casing.
|
|
153
|
+
|
|
154
|
+
Two measured failures, in order of severity:
|
|
155
|
+
|
|
156
|
+
* `_` is a `\\w` character, so `\\btoken\\b` never fired inside
|
|
157
|
+
`SENTRY_AUTH_TOKEN` -- and `API_KEY`, `DB_PASSWORD` and every other
|
|
158
|
+
env-var-style label passed through IN CLEAR. That casing is the standard
|
|
159
|
+
shape of a secrets editor (Doppler, Vercel, Supabase), i.e. exactly the
|
|
160
|
+
credential-panel class this filter exists for. Splitting separators first
|
|
161
|
+
is what fixes it; the alternatives' own `[\\s_-]?` groups still match
|
|
162
|
+
afterwards, because a space satisfies `\\s`.
|
|
163
|
+
* The previous lookbehind form rejected `apiKey` and every English plural
|
|
164
|
+
(`API Keys`, `Tokens`, `Secrets`), which are the literal labels on the
|
|
165
|
+
Cloudflare and Sentry token pages.
|
|
166
|
+
"""
|
|
167
|
+
return _CAMEL_BOUNDARY_RE.sub(" ", _WORD_SEP_RE.sub(" ", name))
|
|
168
|
+
|
|
169
|
+
# `- <role> "<name>" [attrs]: <value>` with the value and the attribute bracket
|
|
170
|
+
# both optional. Anchored on the list-dash so prose lines are never rewritten.
|
|
171
|
+
NODE_RE = re.compile(
|
|
172
|
+
r"^(?P<indent>\s*)(?P<bullet>[-+])\s+(?P<role>[A-Za-z][A-Za-z0-9_]*)"
|
|
173
|
+
r'(?:\s+"(?P<name>(?:[^"\\]|\\.)*)")?'
|
|
174
|
+
r"(?P<attrs>(?:\s+\[[^\]]*\])*)"
|
|
175
|
+
r"(?P<sep>:\s*)(?P<value>.*)$"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# A bare quoted child, e.g. ` - StaticText "the value"`.
|
|
179
|
+
QUOTED_CHILD_RE = re.compile(
|
|
180
|
+
r"^(?P<indent>\s*)(?P<bullet>[-+])\s+(?P<role>[A-Za-z][A-Za-z0-9_]*)"
|
|
181
|
+
r'\s+"(?P<text>(?:[^"\\]|\\.)*)"(?P<rest>.*)$'
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
INDENT_RE = re.compile(r"^(\s*)")
|
|
185
|
+
|
|
186
|
+
# A YAML single-quoted KEY, e.g. `- 'textbox "Token: prod" [ref=e3]': value`.
|
|
187
|
+
QUOTED_KEY_RE = re.compile(
|
|
188
|
+
r"^(?P<indent>\s*)(?P<bullet>[-+])\s+'(?P<key>(?:[^']|'')*)'(?P<tail>(?::.*)?)$"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _unquote_key(line: str) -> tuple[str, str | None]:
|
|
193
|
+
"""Return (line to MATCH against, the raw quoted key or None).
|
|
194
|
+
|
|
195
|
+
The matched form carries the unescaped key so NODE_RE / QUOTED_CHILD_RE and
|
|
196
|
+
the name predicate see the name Playwright rendered; the raw key is kept so
|
|
197
|
+
a redacted line is re-emitted with the quoting it arrived with.
|
|
198
|
+
"""
|
|
199
|
+
quoted = QUOTED_KEY_RE.match(line)
|
|
200
|
+
if not quoted:
|
|
201
|
+
return line, None
|
|
202
|
+
key = quoted.group("key")
|
|
203
|
+
unescaped = key.replace("''", "'")
|
|
204
|
+
return f"{quoted.group('indent')}{quoted.group('bullet')} {unescaped}{quoted.group('tail')}", key
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _render_redacted_node(node: "re.Match[str]", quoted_key: str | None) -> str:
|
|
208
|
+
"""Render a credential node with its VALUE replaced, keeping name and quoting."""
|
|
209
|
+
if quoted_key is not None:
|
|
210
|
+
return f"{node.group('indent')}{node.group('bullet')} '{quoted_key}'{node.group('sep')}{REDACTED}"
|
|
211
|
+
return (
|
|
212
|
+
f"{node.group('indent')}{node.group('bullet')} {node.group('role')}"
|
|
213
|
+
f"{_fmt_name(node.group('name'))}{node.group('attrs') or ''}"
|
|
214
|
+
f"{node.group('sep')}{REDACTED}"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Any tree bullet, used to tell a NODE line from a value CONTINUATION line.
|
|
218
|
+
BULLET_RE = re.compile(r"^\s*[-+]\s")
|
|
219
|
+
|
|
220
|
+
# `agent-browser diff snapshot` colourises its output, and SGR escapes sit
|
|
221
|
+
# OUTSIDE the bullet, so every regex below fails on a coloured line. Measured:
|
|
222
|
+
# 8 sentinel survivals straight through the redactor.
|
|
223
|
+
ANSI_SGR_RE = re.compile(r"\x1b\[[0-9;]*m")
|
|
224
|
+
|
|
225
|
+
# A unified-diff marker column. The real CLI emits `+- textbox ...` and
|
|
226
|
+
# `+ - textbox ...`; the suite previously pinned `+ textbox ...`, a shape the
|
|
227
|
+
# CLI never produces, so the rule was verified against a fixture and not
|
|
228
|
+
# against the surface.
|
|
229
|
+
#
|
|
230
|
+
# The lookahead is load-bearing: an ordinary tree line already STARTS with `-`,
|
|
231
|
+
# so a bare `^[+-]` would eat the tree's own bullet. Requiring a SECOND bullet
|
|
232
|
+
# after optional spaces distinguishes a marker column from a bullet.
|
|
233
|
+
DIFF_MARKER_RE = re.compile(r"^(?P<mark>[+-])(?=\s*[-+]\s)")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _is_credential_name(name: str | None) -> bool:
|
|
237
|
+
"""True when the accessible name marks this node as credential-bearing.
|
|
238
|
+
|
|
239
|
+
An UNNAMED node (`name is None`) is not decidable by a name predicate, and
|
|
240
|
+
an unlabeled readonly box is the commonest shape of a generated-credential
|
|
241
|
+
panel. It is handled at the call site as its own case rather than folded in
|
|
242
|
+
here, so the two decisions stay legible.
|
|
243
|
+
"""
|
|
244
|
+
if name is None:
|
|
245
|
+
return False
|
|
246
|
+
normalised = _normalise_name(name)
|
|
247
|
+
# A field labelled exactly `Session` / `Cookie` / `Signature` IS the
|
|
248
|
+
# credential; the same word inside a longer label (`Session name`,
|
|
249
|
+
# `Email signature`) is ordinary content. Qualifying them in the pattern
|
|
250
|
+
# loses the first case, so it is recovered here rather than by widening the
|
|
251
|
+
# pattern back to the form that ate four benign fields.
|
|
252
|
+
if normalised.strip().lower() in WHOLE_NAME_CREDENTIALS:
|
|
253
|
+
return True
|
|
254
|
+
return bool(CREDENTIAL_NAME_RE.search(normalised))
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def redact_text(text: str) -> str:
|
|
258
|
+
"""Redact credential-shaped node values in the indented-text shape."""
|
|
259
|
+
out: list[str] = []
|
|
260
|
+
# Indent of the node currently being suppressed, or None. Children deeper
|
|
261
|
+
# than this inherit the redaction; a line at or above it ends suppression.
|
|
262
|
+
suppress_indent: int | None = None
|
|
263
|
+
|
|
264
|
+
for raw_line in text.split("\n"):
|
|
265
|
+
line = ANSI_SGR_RE.sub("", raw_line)
|
|
266
|
+
marker = ""
|
|
267
|
+
mark = DIFF_MARKER_RE.match(line)
|
|
268
|
+
if mark:
|
|
269
|
+
marker = mark.group("mark")
|
|
270
|
+
line = line[mark.end():]
|
|
271
|
+
|
|
272
|
+
def emit(rendered: str, _marker: str = marker) -> None:
|
|
273
|
+
out.append(_marker + rendered)
|
|
274
|
+
|
|
275
|
+
indent = len(INDENT_RE.match(line).group(1))
|
|
276
|
+
|
|
277
|
+
if suppress_indent is not None and line.strip():
|
|
278
|
+
if BULLET_RE.match(line):
|
|
279
|
+
if indent <= suppress_indent:
|
|
280
|
+
suppress_indent = None
|
|
281
|
+
else:
|
|
282
|
+
# A CONTINUATION line of a multi-line value. A textarea holding
|
|
283
|
+
# a PEM block or an SSH key emits its 2nd..Nth lines raw, at
|
|
284
|
+
# column 0 -- so the old `indent <= suppress_indent` test ended
|
|
285
|
+
# suppression on the second line and printed the rest of the key
|
|
286
|
+
# in clear, along with every StaticText duplicate below it.
|
|
287
|
+
# Measured on the real CLI, on all four output shapes.
|
|
288
|
+
#
|
|
289
|
+
# It is not a node, so it must not end suppression; it carries
|
|
290
|
+
# the tail of the secret, so it must not be emitted. The node
|
|
291
|
+
# line above already rendered `<redacted>` in its place.
|
|
292
|
+
continue
|
|
293
|
+
|
|
294
|
+
match_line, quoted_key = _unquote_key(line)
|
|
295
|
+
node = NODE_RE.match(match_line)
|
|
296
|
+
# An UNNAMED text-input node carrying a value is redacted on the
|
|
297
|
+
# fail-safe side. We cannot decide it by name -- there is no name -- and
|
|
298
|
+
# an unlabeled readonly box is the commonest shape of a
|
|
299
|
+
# generated-credential panel. Playwright derives an accessible name from
|
|
300
|
+
# a label, placeholder or aria-label, so a genuinely unnamed input with
|
|
301
|
+
# a value is rare; losing its value costs an agent little, and keeping
|
|
302
|
+
# it is the class with a recorded in-repo incident.
|
|
303
|
+
unnamed_input = (
|
|
304
|
+
node is not None
|
|
305
|
+
and node.group("name") is None
|
|
306
|
+
and node.group("role") in TEXT_INPUT_ROLES
|
|
307
|
+
and node.group("value").strip() != ""
|
|
308
|
+
)
|
|
309
|
+
if node and node.group("role") in TEXT_INPUT_ROLES and (
|
|
310
|
+
unnamed_input or _is_credential_name(node.group("name"))
|
|
311
|
+
):
|
|
312
|
+
emit(_render_redacted_node(node, quoted_key))
|
|
313
|
+
suppress_indent = indent
|
|
314
|
+
continue
|
|
315
|
+
|
|
316
|
+
if suppress_indent is not None and indent > suppress_indent:
|
|
317
|
+
# A descendant of a redacted node. Two distinct cases, and the
|
|
318
|
+
# earlier revision got both wrong because QUOTED_CHILD_RE strictly
|
|
319
|
+
# SUBSUMES NODE_RE -- it matched first, so a nested credential node
|
|
320
|
+
# had its NAME redacted and its VALUE preserved. That is the exact
|
|
321
|
+
# inversion of the point: it destroyed the signal and kept the
|
|
322
|
+
# secret.
|
|
323
|
+
inner = NODE_RE.match(match_line)
|
|
324
|
+
if inner:
|
|
325
|
+
# A node with its own `: value` tail -- redact the VALUE, keep
|
|
326
|
+
# the name, exactly as for the parent.
|
|
327
|
+
emit(_render_redacted_node(inner, quoted_key))
|
|
328
|
+
continue
|
|
329
|
+
child = QUOTED_CHILD_RE.match(match_line)
|
|
330
|
+
if child and child.group("role") in VALUE_CARRYING_CHILD_ROLES:
|
|
331
|
+
# The measured `-d N` / no-flag duplicate: the value repeated as
|
|
332
|
+
# a StaticText child. Redact it.
|
|
333
|
+
emit(
|
|
334
|
+
f"{child.group('indent')}{child.group('bullet')} {child.group('role')} "
|
|
335
|
+
f"\"{REDACTED}\"{child.group('rest')}"
|
|
336
|
+
)
|
|
337
|
+
continue
|
|
338
|
+
# Anything else under a redacted node is a LABEL, not a value --
|
|
339
|
+
# `option "id"`, `option "email"` under a combobox named
|
|
340
|
+
# "Primary key". Redacting those destroys the agent's ability to
|
|
341
|
+
# pick the right option while protecting nothing, so they pass
|
|
342
|
+
# through untouched.
|
|
343
|
+
|
|
344
|
+
emit(line)
|
|
345
|
+
|
|
346
|
+
return "\n".join(out)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def looks_like_a11y_tree(value: str) -> bool:
|
|
350
|
+
"""True when a JSON string value carries an accessibility tree.
|
|
351
|
+
|
|
352
|
+
Keying on the field NAME was the defect: `snapshot --json` puts the tree
|
|
353
|
+
under `data.snapshot`, but `diff snapshot --json` puts it under `data.diff`,
|
|
354
|
+
so the envelope parsed cleanly, matched no key, and every value was emitted
|
|
355
|
+
verbatim at exit 0 -- on a command the interceptor ALLOWS, because the
|
|
356
|
+
redactor is in the pipe. A key allowlist is a standing bet that the CLI
|
|
357
|
+
never adds a fourth key; recognising the SHAPE does not take that bet.
|
|
358
|
+
"""
|
|
359
|
+
for raw in value.split("\n"):
|
|
360
|
+
line = ANSI_SGR_RE.sub("", raw)
|
|
361
|
+
mark = DIFF_MARKER_RE.match(line)
|
|
362
|
+
if mark:
|
|
363
|
+
line = line[mark.end():]
|
|
364
|
+
line, _quoted = _unquote_key(line)
|
|
365
|
+
if NODE_RE.match(line) or QUOTED_CHILD_RE.match(line):
|
|
366
|
+
return True
|
|
367
|
+
return False
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _redact_json_in_place(node: object) -> object:
|
|
371
|
+
"""Redact any string field carrying an a11y tree, at any depth."""
|
|
372
|
+
if isinstance(node, dict):
|
|
373
|
+
return {
|
|
374
|
+
k: (redact_text(v)
|
|
375
|
+
if isinstance(v, str) and (k == "snapshot" or looks_like_a11y_tree(v))
|
|
376
|
+
else _redact_json_in_place(v))
|
|
377
|
+
for k, v in node.items()
|
|
378
|
+
}
|
|
379
|
+
if isinstance(node, list):
|
|
380
|
+
return [_redact_json_in_place(v) for v in node]
|
|
381
|
+
return node
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def die(reason: str) -> None:
|
|
385
|
+
"""Fail closed. Never quote the input -- that would re-open the leak."""
|
|
386
|
+
sys.stderr.write(f"redact-a11y-snapshot: refusing to emit: {reason}\n")
|
|
387
|
+
sys.exit(2)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def main() -> None:
|
|
391
|
+
raw = sys.stdin.buffer.read(MAX_INPUT_BYTES + 1)
|
|
392
|
+
if len(raw) > MAX_INPUT_BYTES:
|
|
393
|
+
die(f"input exceeds {MAX_INPUT_BYTES} bytes")
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
text = raw.decode("utf-8")
|
|
397
|
+
except UnicodeDecodeError:
|
|
398
|
+
die("input is not valid UTF-8")
|
|
399
|
+
|
|
400
|
+
if not text:
|
|
401
|
+
return
|
|
402
|
+
|
|
403
|
+
# Locate a JSON payload even when something is printed AHEAD of it.
|
|
404
|
+
#
|
|
405
|
+
# This is the shape the guard itself prescribes: `agent-browser snapshot -i
|
|
406
|
+
# --json 2>&1 | python3 <this>`. The `2>&1` merges any agent-browser
|
|
407
|
+
# diagnostic in front of the JSON, so a `startswith` test is False, the
|
|
408
|
+
# input falls to the line-based path, NODE_RE never matches a single-line
|
|
409
|
+
# JSON blob, and the credential passes through verbatim at exit 0.
|
|
410
|
+
# Measured, and it is the highest-severity shape because our own
|
|
411
|
+
# instructions steer every agent onto it.
|
|
412
|
+
brace = min(
|
|
413
|
+
(i for i in (text.find("{"), text.find("[")) if i != -1),
|
|
414
|
+
default=-1,
|
|
415
|
+
)
|
|
416
|
+
if brace != -1:
|
|
417
|
+
try:
|
|
418
|
+
parsed = json.loads(text[brace:])
|
|
419
|
+
except json.JSONDecodeError as exc:
|
|
420
|
+
# Fail closed on anything that PRESENTS as a JSON document: some
|
|
421
|
+
# line, after stripping, begins with `{` or `[`.
|
|
422
|
+
#
|
|
423
|
+
# The earlier form keyed on the literal `"snapshot"` being present,
|
|
424
|
+
# so an envelope truncated BEFORE that key -- or one with a typo'd
|
|
425
|
+
# key -- matched neither clause, fell through to the line-based
|
|
426
|
+
# path, and was emitted verbatim at exit 0. A partial envelope is
|
|
427
|
+
# exactly what a killed `agent-browser` produces, so that was the
|
|
428
|
+
# likely shape, not an exotic one.
|
|
429
|
+
#
|
|
430
|
+
# A stray brace INSIDE an a11y node value does not begin a line, so
|
|
431
|
+
# ordinary text is unaffected and does not take the run down.
|
|
432
|
+
if any(ln.lstrip().startswith(("{", "[")) for ln in text.splitlines()):
|
|
433
|
+
die(f"input looks like JSON but does not parse ({exc.msg})")
|
|
434
|
+
else:
|
|
435
|
+
prefix = redact_text(text[:brace])
|
|
436
|
+
sys.stdout.write(prefix + json.dumps(_redact_json_in_place(parsed)))
|
|
437
|
+
return
|
|
438
|
+
|
|
439
|
+
sys.stdout.write(redact_text(text))
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
if __name__ == "__main__":
|
|
443
|
+
main()
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Capture Playwright-MCP stdio fixtures from the surface (#7980, Phase 0.2).
|
|
3
|
+
|
|
4
|
+
python3 capture-playwright-mcp-fixtures.py <output-dir> -- <server argv...>
|
|
5
|
+
|
|
6
|
+
Serves a synthesized form on 127.0.0.1 (no real credential, no real origin),
|
|
7
|
+
drives the given MCP server over its own stdin/stdout with hand-written
|
|
8
|
+
JSON-RPC, and writes ONE JSON file per captured message into <output-dir>:
|
|
9
|
+
|
|
10
|
+
initialize.json tools-list.json roots-list-request.json
|
|
11
|
+
navigate.json snapshot.json snapshot-filename.json
|
|
12
|
+
snapshot-meta-json.json snapshot-meta-raw.json find.json
|
|
13
|
+
evaluate.json screenshot.json close.json
|
|
14
|
+
raw-stdout.bin framing.json page.html
|
|
15
|
+
|
|
16
|
+
The page puts the sentinel `ZZQP-SENTINEL-7980` ONLY in credential-named inputs
|
|
17
|
+
(password, Token) and `ZZQP-BENIGN-7980` in Notes, so a redaction assertion can
|
|
18
|
+
count both halves. Every `@playwright/mcp` bump re-runs this driver so the suite
|
|
19
|
+
tests captures from the pinned surface, never from the author (ADR-213 round 3).
|
|
20
|
+
|
|
21
|
+
Framing assertion (row 12): every server stdout message is one `\n`-terminated
|
|
22
|
+
line that parses as a JSON object, no embedded newline, no `Content-Length`.
|
|
23
|
+
|
|
24
|
+
stdlib only. Exit 0 on a complete capture, 1 on a framing violation, 2 on usage.
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import contextlib
|
|
29
|
+
import http.server
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import selectors
|
|
33
|
+
import subprocess
|
|
34
|
+
import sys
|
|
35
|
+
import threading
|
|
36
|
+
import time
|
|
37
|
+
|
|
38
|
+
PAGE_HTML = """<!doctype html>
|
|
39
|
+
<html><head><meta charset="utf-8"><title>probe 7980</title></head>
|
|
40
|
+
<body>
|
|
41
|
+
<form>
|
|
42
|
+
<label>Enter your password <input type="password" value="ZZQP-SENTINEL-7980"></label>
|
|
43
|
+
<label>Token <input type="text" readonly value="ZZQP-SENTINEL-7980"></label>
|
|
44
|
+
<label>Email address <input type="email" value="probe-user@example.invalid"></label>
|
|
45
|
+
<label>Notes <textarea>ZZQP-BENIGN-7980</textarea></label>
|
|
46
|
+
<button type="button">Go</button>
|
|
47
|
+
</form>
|
|
48
|
+
</body></html>
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _Quiet(http.server.SimpleHTTPRequestHandler):
|
|
53
|
+
def log_message(self, *_args): # noqa: D401 - silence the server
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def do_GET(self): # noqa: N802 - http.server API
|
|
57
|
+
body = PAGE_HTML.encode()
|
|
58
|
+
self.send_response(200)
|
|
59
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
60
|
+
self.send_header("Content-Length", str(len(body)))
|
|
61
|
+
self.end_headers()
|
|
62
|
+
self.wfile.write(body)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def serve_page():
|
|
66
|
+
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), _Quiet)
|
|
67
|
+
threading.Thread(target=srv.serve_forever, daemon=True).start()
|
|
68
|
+
return srv, f"http://127.0.0.1:{srv.server_address[1]}/page.html"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Driver:
|
|
72
|
+
def __init__(self, argv, out):
|
|
73
|
+
self.out = out
|
|
74
|
+
self.child = subprocess.Popen(
|
|
75
|
+
argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr, bufsize=0
|
|
76
|
+
)
|
|
77
|
+
self.sel = selectors.DefaultSelector()
|
|
78
|
+
self.sel.register(self.child.stdout, selectors.EVENT_READ)
|
|
79
|
+
self.buf = bytearray()
|
|
80
|
+
self.raw = bytearray()
|
|
81
|
+
self.next_id = 1
|
|
82
|
+
self.violations = []
|
|
83
|
+
|
|
84
|
+
def send(self, obj):
|
|
85
|
+
line = json.dumps(obj, separators=(",", ":")).encode() + b"\n"
|
|
86
|
+
self.child.stdin.write(line)
|
|
87
|
+
self.child.stdin.flush()
|
|
88
|
+
|
|
89
|
+
def request(self, method, params=None):
|
|
90
|
+
rid = self.next_id
|
|
91
|
+
self.next_id += 1
|
|
92
|
+
msg = {"jsonrpc": "2.0", "id": rid, "method": method}
|
|
93
|
+
if params is not None:
|
|
94
|
+
msg["params"] = params
|
|
95
|
+
self.send(msg)
|
|
96
|
+
return rid
|
|
97
|
+
|
|
98
|
+
def read_line(self, timeout=60.0):
|
|
99
|
+
deadline = time.monotonic() + timeout
|
|
100
|
+
while b"\n" not in self.buf:
|
|
101
|
+
remaining = deadline - time.monotonic()
|
|
102
|
+
if remaining <= 0:
|
|
103
|
+
raise TimeoutError("no line from server within timeout")
|
|
104
|
+
if not self.sel.select(timeout=remaining):
|
|
105
|
+
continue
|
|
106
|
+
chunk = os.read(self.child.stdout.fileno(), 65536)
|
|
107
|
+
if not chunk:
|
|
108
|
+
raise EOFError("server closed stdout")
|
|
109
|
+
self.buf += chunk
|
|
110
|
+
self.raw += chunk
|
|
111
|
+
line, _, rest = bytes(self.buf).partition(b"\n")
|
|
112
|
+
self.buf = bytearray(rest)
|
|
113
|
+
return line
|
|
114
|
+
|
|
115
|
+
def wait_response(self, rid, name):
|
|
116
|
+
"""Read until the response for `rid` arrives; capture server requests on the way."""
|
|
117
|
+
while True:
|
|
118
|
+
line = self.read_line()
|
|
119
|
+
if line.startswith(b"Content-Length"):
|
|
120
|
+
self.violations.append("Content-Length header on stdout")
|
|
121
|
+
try:
|
|
122
|
+
obj = json.loads(line.decode("utf-8", errors="replace"))
|
|
123
|
+
except json.JSONDecodeError:
|
|
124
|
+
self.violations.append(f"non-JSON stdout line ({len(line)} bytes)")
|
|
125
|
+
continue
|
|
126
|
+
if not isinstance(obj, dict):
|
|
127
|
+
self.violations.append("stdout line is not a JSON object")
|
|
128
|
+
continue
|
|
129
|
+
if "method" in obj and "id" in obj:
|
|
130
|
+
# server -> client request (roots/list); answer it and record it.
|
|
131
|
+
self.save("roots-list-request" if obj["method"] == "roots/list" else f"server-request-{obj['method'].replace('/', '-')}", obj)
|
|
132
|
+
self.send({"jsonrpc": "2.0", "id": obj["id"], "result": {"roots": []}})
|
|
133
|
+
continue
|
|
134
|
+
if "method" in obj:
|
|
135
|
+
self.save(f"notification-{obj['method'].replace('/', '-')}", obj)
|
|
136
|
+
continue
|
|
137
|
+
if obj.get("id") == rid:
|
|
138
|
+
self.save(name, obj)
|
|
139
|
+
return obj
|
|
140
|
+
self.save(f"unexpected-{obj.get('id')}", obj)
|
|
141
|
+
|
|
142
|
+
def save(self, name, obj):
|
|
143
|
+
path = os.path.join(self.out, f"{name}.json")
|
|
144
|
+
with open(path, "w", encoding="utf-8") as fh:
|
|
145
|
+
json.dump(obj, fh, indent=2, ensure_ascii=False)
|
|
146
|
+
fh.write("\n")
|
|
147
|
+
|
|
148
|
+
def call(self, name, tool, arguments=None):
|
|
149
|
+
rid = self.request("tools/call", {"name": tool, "arguments": arguments or {}})
|
|
150
|
+
return self.wait_response(rid, name)
|
|
151
|
+
|
|
152
|
+
def finish(self):
|
|
153
|
+
with contextlib.suppress(OSError):
|
|
154
|
+
self.child.stdin.close()
|
|
155
|
+
try:
|
|
156
|
+
self.child.wait(timeout=30)
|
|
157
|
+
except subprocess.TimeoutExpired:
|
|
158
|
+
self.child.kill()
|
|
159
|
+
with open(os.path.join(self.out, "raw-stdout.bin"), "wb") as fh:
|
|
160
|
+
fh.write(bytes(self.raw))
|
|
161
|
+
lines = bytes(self.raw).split(b"\n")
|
|
162
|
+
trailing = lines[-1]
|
|
163
|
+
if trailing:
|
|
164
|
+
self.violations.append("stdout did not end with a newline")
|
|
165
|
+
framing = {
|
|
166
|
+
"messages": len(lines) - 1,
|
|
167
|
+
"content_length_header": any(l.startswith(b"Content-Length") for l in lines),
|
|
168
|
+
"every_line_is_json_object": all(
|
|
169
|
+
isinstance(json.loads(l.decode("utf-8", errors="replace")), dict) for l in lines[:-1]
|
|
170
|
+
),
|
|
171
|
+
"violations": self.violations,
|
|
172
|
+
}
|
|
173
|
+
self.save("framing", framing)
|
|
174
|
+
return framing
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def main(argv):
|
|
178
|
+
if "--" not in argv or argv.index("--") != 1 or len(argv) < 3:
|
|
179
|
+
sys.stderr.write(__doc__)
|
|
180
|
+
return 2
|
|
181
|
+
out = argv[0]
|
|
182
|
+
server_argv = argv[2:]
|
|
183
|
+
os.makedirs(out, exist_ok=True)
|
|
184
|
+
with open(os.path.join(out, "page.html"), "w", encoding="utf-8") as fh:
|
|
185
|
+
fh.write(PAGE_HTML)
|
|
186
|
+
srv, url = serve_page()
|
|
187
|
+
try:
|
|
188
|
+
d = Driver(server_argv, out)
|
|
189
|
+
rid = d.request(
|
|
190
|
+
"initialize",
|
|
191
|
+
{
|
|
192
|
+
"protocolVersion": "2025-06-18",
|
|
193
|
+
"capabilities": {"roots": {"listChanged": True}},
|
|
194
|
+
"clientInfo": {"name": "capture-playwright-mcp-fixtures", "version": "7980"},
|
|
195
|
+
},
|
|
196
|
+
)
|
|
197
|
+
d.wait_response(rid, "initialize")
|
|
198
|
+
d.send({"jsonrpc": "2.0", "method": "notifications/initialized"})
|
|
199
|
+
d.wait_response(d.request("tools/list"), "tools-list")
|
|
200
|
+
d.call("navigate", "browser_navigate", {"url": url})
|
|
201
|
+
d.call("snapshot", "browser_snapshot")
|
|
202
|
+
d.call("snapshot-filename", "browser_snapshot", {"filename": "explicit-snap.yml"})
|
|
203
|
+
d.call("snapshot-meta-json", "browser_snapshot", {"_meta": {"json": True}})
|
|
204
|
+
d.call("snapshot-meta-raw", "browser_snapshot", {"_meta": {"raw": True}})
|
|
205
|
+
d.call("find", "browser_find", {"text": "Token"})
|
|
206
|
+
d.call("evaluate", "browser_evaluate", {"function": "() => ({a: 1, b: [1,2]})"})
|
|
207
|
+
d.call("screenshot", "browser_take_screenshot", {"type": "png"})
|
|
208
|
+
d.call("close", "browser_close")
|
|
209
|
+
framing = d.finish()
|
|
210
|
+
finally:
|
|
211
|
+
srv.shutdown()
|
|
212
|
+
print(json.dumps(framing))
|
|
213
|
+
return 1 if framing["violations"] else 0
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Known-NEGATIVE relay for the proxy suite (#7980, QG5): spawn the server,
|
|
3
|
+
pump bytes both ways, rewrite nothing. Every redaction/withhold/refuse row must
|
|
4
|
+
be RED against this file and every must-PASS row GREEN; it is the pre-fix
|
|
5
|
+
artefact, never shipped."""
|
|
6
|
+
import os, selectors, subprocess, sys # noqa: E401
|
|
7
|
+
|
|
8
|
+
child = subprocess.Popen(sys.argv[sys.argv.index("--") + 1:], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0, start_new_session=True)
|
|
9
|
+
sys.stderr.write(f"fake-passthrough-proxy: child pgid {os.getpgid(child.pid)}\n"); sys.stderr.flush()
|
|
10
|
+
sel = selectors.DefaultSelector()
|
|
11
|
+
sel.register(sys.stdin.buffer, selectors.EVENT_READ, "in")
|
|
12
|
+
sel.register(child.stdout, selectors.EVENT_READ, "out")
|
|
13
|
+
while sel.get_map():
|
|
14
|
+
for key, _ in sel.select():
|
|
15
|
+
data = os.read(key.fileobj.fileno(), 65536)
|
|
16
|
+
if key.data == "in":
|
|
17
|
+
if not data:
|
|
18
|
+
sel.unregister(key.fileobj); child.stdin.close(); continue
|
|
19
|
+
child.stdin.write(data); child.stdin.flush()
|
|
20
|
+
else:
|
|
21
|
+
if not data:
|
|
22
|
+
sel.unregister(key.fileobj); continue
|
|
23
|
+
sys.stdout.buffer.write(data); sys.stdout.buffer.flush()
|
|
24
|
+
sys.exit(child.wait())
|