@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,631 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# bsky-setup.sh -- Bluesky AT Protocol credential setup and validation
|
|
3
|
+
#
|
|
4
|
+
# SECURITY: Credentials MUST be in environment variables.
|
|
5
|
+
# Never pass tokens as CLI arguments (visible in ps/history).
|
|
6
|
+
#
|
|
7
|
+
# Usage: bsky-setup.sh <command>
|
|
8
|
+
# Commands:
|
|
9
|
+
# write-env - Write credentials to .env with chmod 600
|
|
10
|
+
# verify - Source .env and verify via session creation + profile fetch
|
|
11
|
+
#
|
|
12
|
+
# Environment variables (required):
|
|
13
|
+
# BSKY_HANDLE - Bluesky handle (e.g., soleur.bsky.social)
|
|
14
|
+
# BSKY_APP_PASSWORD - App password (generate at bsky.app/settings/app-passwords)
|
|
15
|
+
#
|
|
16
|
+
# Exit codes:
|
|
17
|
+
# 0 - Success
|
|
18
|
+
# 1 - General error, including missing credentials and a write-env value outside the .env
|
|
19
|
+
# allow-list, or an operational write-env fault (a held lock, an unreadable or dangling .env).
|
|
20
|
+
# Told apart by the value-free stderr marker `SOLEUR_CREDENTIAL_REFUSED`: `reason=token_shape` or
|
|
21
|
+
# `control_char` with `var=<NAME>` is the allow-list (fix the value, do not retry); `reason=env_*`
|
|
22
|
+
# with `var=ENV_FILE phase=write-env` is a write fault (diagnose, retry once). Exit 1 with no marker
|
|
23
|
+
# is a missing credential.
|
|
24
|
+
#
|
|
25
|
+
# Output: JSON to stdout
|
|
26
|
+
# Errors: Messages to stderr, exit 1. The exception is `write-env`, which has no stdout payload and
|
|
27
|
+
# whose human text is therefore all on STDOUT (agent runtimes surface stdout and swallow
|
|
28
|
+
# stderr): the missing-credentials diagnostic, the allow-list line, and the "Wrote N
|
|
29
|
+
# variables" confirmation. Only the allow-list marker stays on stderr.
|
|
30
|
+
|
|
31
|
+
set -euo pipefail
|
|
32
|
+
|
|
33
|
+
# (#7797) Xtrace refusal -- the FIRST thing after `set …`, so nothing above it is
|
|
34
|
+
# traced. This script ships to installed Soleur CLIs: the terminal it runs in
|
|
35
|
+
# belongs to the founder, and `bash -x` would print their live Bluesky
|
|
36
|
+
# credentials. UNCONDITIONAL, and that is measured rather than inherited:
|
|
37
|
+
# `cmd_verify` does `set -a; source "$env_file"; set +a`, loading the founder's
|
|
38
|
+
# .env at RUNTIME, AFTER this prologue. A conditional hatch keyed on whether the
|
|
39
|
+
# credential is already set would therefore be empty at guard time and open, and
|
|
40
|
+
# `source` under xtrace echoes EVERY assignment in the file -- so the leak would
|
|
41
|
+
# be the whole .env, not one token. (Written without the literal parameter
|
|
42
|
+
# expansion on purpose: a bare-token grep for it cannot tell this comment from
|
|
43
|
+
# real code, and the 4-vs-11 arm split is asserted by exactly such a grep.)
|
|
44
|
+
# Refusal goes to STDOUT, not stderr: agent runtimes surface stdout and swallow
|
|
45
|
+
# stderr (constitution.md > Code Style > Always), and a swallowed security refusal
|
|
46
|
+
# leaves the user with a bare `exit 78` and no text at all.
|
|
47
|
+
case "$-" in
|
|
48
|
+
*x*)
|
|
49
|
+
printf 'Refusing to run under `bash -x`: this script loads BSKY_HANDLE, BSKY_APP_PASSWORD from your .env at runtime, and tracing would print them to your terminal. Unsetting them first does not help -- the .env is the source. Re-run without `bash -x`.\n'
|
|
50
|
+
exit 78
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
# (#7873) `--disable` closes ~/.curlrc and `--noproxy '*'` closes the proxy
|
|
55
|
+
# variables, but NEITHER touches the trust store or the TLS session-key log. On an
|
|
56
|
+
# installed CLI the environment belongs to someone who is not us, so
|
|
57
|
+
# a substituted `CURL_CA_BUNDLE` is a clean MITM of the founder's platform
|
|
58
|
+
# token with every other guard fully intact, and `SSLKEYLOGFILE` is passive
|
|
59
|
+
# decryption with no MITM at all. Matches the line in scripts/betterstack-query.sh.
|
|
60
|
+
unset SSLKEYLOGFILE CURL_CA_BUNDLE SSL_CERT_FILE SSL_CERT_DIR CURL_HOME \
|
|
61
|
+
HOSTALIASES LOCALDOMAIN RES_OPTIONS
|
|
62
|
+
|
|
63
|
+
# --- Transport-confinement diagnostics (#7873) --------------------------------
|
|
64
|
+
# Every credentialed `curl` below carries `--disable --noproxy '*'` and discards
|
|
65
|
+
# curl's own stderr (which can carry the URL). A failed request then has four
|
|
66
|
+
# competing causes the founder cannot tell apart: the xtrace refusal, the proxy
|
|
67
|
+
# this script deliberately bypassed, a ~/.curlrc that `--disable` dropped, or an
|
|
68
|
+
# ordinary network failure. So each failure emits ONE structured marker carrying
|
|
69
|
+
# all four discriminators, beside a human line that names the bypass instead of
|
|
70
|
+
# blaming the founder's connectivity.
|
|
71
|
+
SOLEUR_TRANSPORT_SCRIPT="bsky-setup.sh"
|
|
72
|
+
SOLEUR_TRANSPORT_PLATFORM="Bluesky"
|
|
73
|
+
|
|
74
|
+
# The request helpers below run inside `$(...)`, where fd 1 is the capture pipe.
|
|
75
|
+
# Save the script's real stdout so the marker reaches the founder's terminal (and
|
|
76
|
+
# the always-on PostToolUse Bash extractor) instead of being swallowed into a
|
|
77
|
+
# variable. The marker is on stdout BY DESIGN -- the call sites' `2>/dev/null` is
|
|
78
|
+
# what keeps curl's URL-carrying stderr out of the transcript, and it must not be
|
|
79
|
+
# able to suppress the diagnostic too.
|
|
80
|
+
exec 3>&1
|
|
81
|
+
|
|
82
|
+
# Which proxy variables are non-empty, or `none`.
|
|
83
|
+
proxy_env_names() {
|
|
84
|
+
local names="" n
|
|
85
|
+
for n in HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY http_proxy https_proxy all_proxy no_proxy; do
|
|
86
|
+
if [[ -n "${!n-}" ]]; then
|
|
87
|
+
names="${names},${n}"
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
if [[ -z "$names" ]]; then
|
|
91
|
+
printf 'none'
|
|
92
|
+
else
|
|
93
|
+
printf '%s' "${names#,}"
|
|
94
|
+
fi
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# True when a proxy this script deliberately bypasses is configured. NO_PROXY
|
|
98
|
+
# alone is not one -- it only ever narrows proxying, so it cannot be the cause.
|
|
99
|
+
proxy_bypassed() {
|
|
100
|
+
[[ -n "${HTTP_PROXY:-}${HTTPS_PROXY:-}${ALL_PROXY:-}${http_proxy:-}${https_proxy:-}${all_proxy:-}" ]]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# One line, eight fields, on the saved stdout.
|
|
104
|
+
#
|
|
105
|
+
# Two fields were literals in the first cut and are now MEASURED (#7898 review):
|
|
106
|
+
# `noproxy_applied` was the constant "true", which ASSERTS the property instead of
|
|
107
|
+
# observing it -- dropping --noproxy from a call site left the diagnostic still
|
|
108
|
+
# claiming it was applied. It now reports whether a proxy was actually configured
|
|
109
|
+
# for this request to bypass, which is the fact a reader needs. `refusal` was the
|
|
110
|
+
# constant "none" at every call site, so the cause it exists to discriminate could
|
|
111
|
+
# never appear; it now carries `env-rebind-refused` and `xtrace-credential-bound`.
|
|
112
|
+
#
|
|
113
|
+
# `tls_env_cleared` is new. The prologue unsets CURL_CA_BUNDLE/SSL_CERT_FILE et al,
|
|
114
|
+
# which is correct against an attacker and BREAKS a founder whose corporate CA
|
|
115
|
+
# arrives that way -- curl then exits 60 and, without this field, the event is
|
|
116
|
+
# indistinguishable from an ordinary network failure.
|
|
117
|
+
emit_transport_diag() {
|
|
118
|
+
local curl_exit="$1" refusal="$2" surface="installed-cli" curlrc="false" noproxy="false"
|
|
119
|
+
# The hosted sandbox always sets this (agent-env.ts > AGENT_ENV_OVERRIDES); an
|
|
120
|
+
# installed CLI does not. A label on the event, not a gate on behaviour.
|
|
121
|
+
if [[ -n "${CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:-}" ]]; then
|
|
122
|
+
surface="in-sandbox"
|
|
123
|
+
fi
|
|
124
|
+
if [[ -f "${HOME:-}/.curlrc" ]]; then
|
|
125
|
+
curlrc="true"
|
|
126
|
+
fi
|
|
127
|
+
if proxy_bypassed; then
|
|
128
|
+
noproxy="true"
|
|
129
|
+
fi
|
|
130
|
+
printf 'SOLEUR_TRANSPORT_DIAG surface=%s script=%s curl_exit=%s refusal=%s proxy_env=%s curlrc_present=%s noproxy_applied=%s tls_env_cleared=%s\n' \
|
|
131
|
+
"$surface" "$SOLEUR_TRANSPORT_SCRIPT" "$curl_exit" "$refusal" \
|
|
132
|
+
"$(proxy_env_names)" "$curlrc" "$noproxy" "true" >&3
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Replaces the bare "Check your network connection and try again." on a
|
|
136
|
+
# credentialed-curl failure. With a proxy set, that line blames the founder for a
|
|
137
|
+
# bypass this script chose.
|
|
138
|
+
report_transport_failure() {
|
|
139
|
+
local curl_exit="${1:-unknown}"
|
|
140
|
+
local detail="${2:-Failed to connect to the ${SOLEUR_TRANSPORT_PLATFORM} API.}"
|
|
141
|
+
emit_transport_diag "$curl_exit" "none"
|
|
142
|
+
echo "Error: ${detail}" >&2
|
|
143
|
+
# Ordered by how specific the evidence is. Each arm names something THIS script
|
|
144
|
+
# did, so the founder is never told to go debug their own network for a choice
|
|
145
|
+
# made here. The bare "check your connection" line is the LAST resort, not the
|
|
146
|
+
# default -- it was the default in the first cut, which meant a founder whose
|
|
147
|
+
# corporate CA or curlrc we had just discarded was blamed for it (#7898 review).
|
|
148
|
+
if [[ "$curl_exit" == "60" || "$curl_exit" == "35" || "$curl_exit" == "77" ]]; then
|
|
149
|
+
# 60 peer-certificate, 35 TLS handshake, 77 CA-bundle unreadable.
|
|
150
|
+
echo "This looks like a TLS trust failure. This script deliberately clears CURL_CA_BUNDLE, SSL_CERT_FILE, SSL_CERT_DIR and SSLKEYLOGFILE before the request, because those variables can redirect or expose a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} credential." >&2
|
|
151
|
+
echo "If your machine needs a corporate CA to reach ${SOLEUR_TRANSPORT_PLATFORM}, that is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
|
|
152
|
+
elif proxy_bypassed; then
|
|
153
|
+
echo "This request deliberately bypasses your proxy ($(proxy_env_names)), because a proxy can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token." >&2
|
|
154
|
+
echo "If you need Soleur to reach ${SOLEUR_TRANSPORT_PLATFORM} through your proxy, that is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
|
|
155
|
+
elif [[ -f "${HOME:-}/.curlrc" ]]; then
|
|
156
|
+
echo "You have a ~/.curlrc, and this request deliberately ignores it (--disable), because a curlrc can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token. If it configures a proxy or a CA bundle you need, that is why this failed." >&2
|
|
157
|
+
echo "That is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
|
|
158
|
+
else
|
|
159
|
+
echo "Check your network connection and try again." >&2
|
|
160
|
+
fi
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
164
|
+
source "$SCRIPT_DIR/../../../scripts/resolve-git-root.sh"
|
|
165
|
+
|
|
166
|
+
# readonly (#7898 review): `cmd_verify` and friends run `set -a; source "$env_file"; set +a`
|
|
167
|
+
# BELOW this line, so a plain assignment here is rebindable by the repo's .env --
|
|
168
|
+
# which retargets the credentialed request while every transport flag stays intact.
|
|
169
|
+
# That is the "--disable and --noproxy are intact and irrelevant" shape this change
|
|
170
|
+
# exists to close, one layer up. readonly makes the destination non-rebindable.
|
|
171
|
+
readonly BSKY_API="https://bsky.social/xrpc"
|
|
172
|
+
|
|
173
|
+
# (#7843) The session token is parsed out of an API reply, so it is RESPONSE-DERIVED:
|
|
174
|
+
# untrusted bytes. It rides curl's STDIN config channel (`--config -`), never its
|
|
175
|
+
# argument list (readable by every local user in /proc/<pid>/cmdline). That channel is
|
|
176
|
+
# line-oriented, so a token holding a quote and a newline could append a `url = "..."`
|
|
177
|
+
# directive and make curl issue a second request. This guard refuses anything outside the
|
|
178
|
+
# JWT/base64url alphabet BEFORE the value is formatted into the stream, and never echoes it.
|
|
179
|
+
_bearer_ok() { local LC_ALL=C; case "${1:-}" in ''|*[!A-Za-z0-9._~+/=-]*) return 1 ;; esac; }
|
|
180
|
+
|
|
181
|
+
# --- createSession body transport (#9597) ---
|
|
182
|
+
# The createSession body carries the app password and a user-supplied handle. Neither may
|
|
183
|
+
# ride curl's argument list (readable by every local user in /proc/<pid>/cmdline). The
|
|
184
|
+
# createSession call has no bearer config, so curl's stdin is free: `jq` builds the body and
|
|
185
|
+
# pipes it to `curl --data-binary @-` (NOT `-d @-`, which strips CR/LF). The values reach
|
|
186
|
+
# `jq` through its ENVIRONMENT as an inline assignment prefix (`$ENV.X`), never `jq --arg`
|
|
187
|
+
# (jq's own argv is world-readable too). The BSKY_ID / BSKY_PW aliases are never `export`ed:
|
|
188
|
+
# they live on the one jq child only. RESIDUAL: `verify` sources the .env under `set -a`, so
|
|
189
|
+
# every OTHER variable in that file is exported to later children; the two credential names
|
|
190
|
+
# are un-exported again right after (`export -n`), but that is the only thing narrowed.
|
|
191
|
+
# No file, no trap: the secret exists only in the processes' memory and the pipe.
|
|
192
|
+
|
|
193
|
+
# A control character can never be part of a handle or an app password. Refusing one
|
|
194
|
+
# before anything is sent keeps garbage off the wire; jq escapes everything else (a handle
|
|
195
|
+
# holding a double quote is escaped, never injected into the JSON).
|
|
196
|
+
_bsky_cred_ok() { local LC_ALL=C; case "${1:-}" in ''|*[[:cntrl:]]*) return 1 ;; esac; }
|
|
197
|
+
|
|
198
|
+
# A fixed, value-free marker plus ONE human line on stderr, naming only which FIELD was
|
|
199
|
+
# refused (never the handle or the password), and a non-zero exit. $2 is the variable name.
|
|
200
|
+
# Exit 1 (never 0): this script has no `post`; the hosted content-publisher path lives in
|
|
201
|
+
# bsky-community.sh, which carries the same refusal shape.
|
|
202
|
+
bsky_refuse() {
|
|
203
|
+
printf 'SOLEUR_CREDENTIAL_REFUSED script=%s reason=%s\n' "$SOLEUR_TRANSPORT_SCRIPT" "$1" >&2
|
|
204
|
+
echo "Error: ${2} is empty or contains a control character (for example a stray carriage return or newline from a copy-paste or a CRLF .env file), so nothing was sent. Remove it and retry. The value is not shown." >&2
|
|
205
|
+
exit 1
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# (#9597) write-env value allow-list. `.env` values are written UNQUOTED and later SOURCED
|
|
209
|
+
# (`verify`, the community scripts), so a value holding a command substitution, a backtick, a
|
|
210
|
+
# quote, a semicolon, a space or a newline would execute or split on the next source, and a leading
|
|
211
|
+
# tilde would silently change a stored credential (tilde expansion on source). Validation is an
|
|
212
|
+
# allow-list, fail-closed, and runs for EVERY value BEFORE the first write: a refused value prints
|
|
213
|
+
# the value-free marker plus ONE human line naming only the VARIABLE, writes nothing, exits 1 and
|
|
214
|
+
# leaves any existing .env untouched. A value outside the list that is legitimate is added to .env
|
|
215
|
+
# by hand. The glob runs under LC_ALL=C and is not grep, which is line-oriented and lets a
|
|
216
|
+
# multi-line value through.
|
|
217
|
+
_wenv_class() { # <value>: 0 allowed, 1 outside the allow-list or empty, 2 holds a control character
|
|
218
|
+
local LC_ALL=C
|
|
219
|
+
case "${1-}" in
|
|
220
|
+
'') return 1 ;;
|
|
221
|
+
*[[:cntrl:]]*) return 2 ;;
|
|
222
|
+
*[!A-Za-z0-9._:/@%+=,-]*) return 1 ;;
|
|
223
|
+
esac
|
|
224
|
+
return 0
|
|
225
|
+
}
|
|
226
|
+
_wenv_refuse() { # <reason> <VARIABLE>
|
|
227
|
+
printf 'SOLEUR_CREDENTIAL_REFUSED script=%s reason=%s var=%s phase=write-env\n' "$SOLEUR_TRANSPORT_SCRIPT" "$1" "$2" >&2
|
|
228
|
+
echo "Error: ${2} holds a character this script does not write to .env (allowed: letters, digits and . _ : / @ % + = , -), so nothing was written and your existing .env is unchanged. The value is not shown. If the value is legitimate, add it to .env by hand."
|
|
229
|
+
exit 1
|
|
230
|
+
}
|
|
231
|
+
# _wenv_validate <VARIABLE>...: every NON-EMPTY named variable passes the allow-list. The required
|
|
232
|
+
# variables are checked non-empty by the caller before this runs; an empty optional one is skipped.
|
|
233
|
+
_wenv_validate() {
|
|
234
|
+
local _wn _wrc
|
|
235
|
+
for _wn in "$@"; do
|
|
236
|
+
[[ -n "${!_wn:-}" ]] || continue
|
|
237
|
+
_wrc=0
|
|
238
|
+
_wenv_class "${!_wn}" || _wrc=$?
|
|
239
|
+
case "$_wrc" in
|
|
240
|
+
0) ;;
|
|
241
|
+
2) _wenv_refuse control_char "$_wn" ;;
|
|
242
|
+
*) _wenv_refuse token_shape "$_wn" ;;
|
|
243
|
+
esac
|
|
244
|
+
done
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
# (#10091) write-env commit: ONE helper, byte-identical in the four setup scripts (a drift row pins
|
|
248
|
+
# it, from this comment line through the closing brace of `_wenv_write`). `_wenv_write <env_file>
|
|
249
|
+
# <remove-ERE> <KEY=VALUE>...` replaces the old remove-then-append, which raced (two writers each read
|
|
250
|
+
# the file, each rewrote it, the later mv lost the earlier's keys), was not atomic (the append hit the
|
|
251
|
+
# live file after the mv) and widened a new file before the chmod.
|
|
252
|
+
# The edit is built in a temp file `<.env>.wenv.XXXXXX` beside the RESOLVED .env (same filesystem, so the
|
|
253
|
+
# final `mv -f` is one atomic rename; a namespace of its own, so the sweep below touches only that
|
|
254
|
+
# namespace (a file there that is not a regular file this user owns is left alone), never the operator
|
|
255
|
+
# library's `.env.tmp.*` temps) under `umask 077` (0600 from birth), serialized by
|
|
256
|
+
# ONE mutual-exclusion mechanism on every platform: an owner-pid `mkdir` lock directory
|
|
257
|
+
# (`<.env>.lock.d`) that is held until AFTER that rename. No flock (a flock run and a mkdir run exclude
|
|
258
|
+
# nothing against each other, flock without a timeout blocks forever, and `exec {fd}<` needs bash 4.1
|
|
259
|
+
# while macOS ships 3.2).
|
|
260
|
+
# Stale-lock recovery is serialized as well, by a second `mkdir` mutex `<.env>.lock.d.recover`. A waiter
|
|
261
|
+
# judges a lock stale when its owner pid is dead (or its owner file stayed empty for _WENV_EMPTY_POLLS
|
|
262
|
+
# passes), then tries to take the mutex; a waiter that loses it keeps waiting. The winner RE-READS the
|
|
263
|
+
# owner of the lock UNDER the mutex (the snapshot it judged on is never trusted; the re-read narrows the
|
|
264
|
+
# window to the few instructions between the read and the removal, it does not close it), removes the lock
|
|
265
|
+
# only if the owner is still a dead pid, or if the snapshot AND the re-read (after a short pause) both found
|
|
266
|
+
# no pid, ALWAYS releases the mutex, and goes round the loop again. A lock whose owner turned from a dead pid
|
|
267
|
+
# into nothing has changed hands and is left alone. A recovering waiter never writes while it recovers,
|
|
268
|
+
# nothing is renamed away and put back, and the lock path is never empty while a live holder exists, so two
|
|
269
|
+
# waiters cannot both hold the lock. A SOLEUR_WENV_LOCK_TICKS below 2 never attempts recovery.
|
|
270
|
+
# The wait is ONE bounded loop. Every failed `mkdir` pass either sleeps one tick (0.1 s; 1 s where
|
|
271
|
+
# `sleep` takes no fraction) or fails at once, and every pass counts against SOLEUR_WENV_LOCK_TICKS
|
|
272
|
+
# (digits only, at most _WENV_TICKS_MAX, else _WENV_TICKS_DEFAULT): about 10 s by default with a
|
|
273
|
+
# fractional `sleep`, about 100 s without one. It FAILS CLOSED: no write, a value-free marker, exit 1,
|
|
274
|
+
# .env untouched. A lock path that exists but is not a directory fails at once (env_lock_not_dir). grep
|
|
275
|
+
# exit 1 (nothing left) is normal; exit 2 (an unreadable .env) aborts, because swallowing it would
|
|
276
|
+
# install an empty file over the credentials. A symlinked .env is resolved first (the real target is
|
|
277
|
+
# rewritten, the link survives); a dangling link or a non-regular target is refused.
|
|
278
|
+
# Signals: this helper owns the EXIT, INT, TERM and HUP traps for its duration and restores nothing (no
|
|
279
|
+
# caller installs one today; a caller that needs its own must chain them). On those four it removes its
|
|
280
|
+
# temp and its recovery mutex and releases the lock, and it removes the lock ONLY when the owner file
|
|
281
|
+
# reads exactly this process. Residual, stated: SIGKILL leaves its lock (recovered by the next run
|
|
282
|
+
# through the dead pid), its temp (swept by the next run, under the lock) and, if it died mid-recovery,
|
|
283
|
+
# `<.env>.lock.d.recover`, which the next runs report after the bounded wait ("remove that directory");
|
|
284
|
+
# a recycled pid can keep a dead owner looking live until the wait times out; the owner check uses
|
|
285
|
+
# `kill -0`, so a lock held by another user reads as dead; a holder stalled between its `mkdir` and its
|
|
286
|
+
# owner write for longer than the empty-owner window can be treated as ownerless and recovered.
|
|
287
|
+
_WENV_TICK=0.1 # seconds per wait tick (a `sleep` without fractions falls back to 1 s)
|
|
288
|
+
_WENV_TICKS_DEFAULT=100 # the default bound: about 10 s
|
|
289
|
+
_WENV_TICKS_MAX=1000 # the largest SOLEUR_WENV_LOCK_TICKS accepted: about 100 s
|
|
290
|
+
_WENV_EMPTY_POLLS=30 # passes an ownerless lock directory is waited on before recovery is tried: about 3 s
|
|
291
|
+
_WENV_EMPTY_PAUSE=0.3 # pause between the two owner reads under the recovery mutex when the owner is not a pid
|
|
292
|
+
_WENV_LOCK="" _WENV_TMP="" _WENV_REC=""
|
|
293
|
+
_wenv_unlock() { # idempotent: remove our temp and recovery mutex; release the lock ONLY if its owner file reads exactly this process
|
|
294
|
+
local _wo=""
|
|
295
|
+
if [[ -n "$_WENV_TMP" ]]; then rm -f -- "$_WENV_TMP"; _WENV_TMP=""; fi
|
|
296
|
+
if [[ -n "$_WENV_REC" ]]; then rmdir -- "$_WENV_REC" 2>/dev/null || true; _WENV_REC=""; fi
|
|
297
|
+
if [[ -n "$_WENV_LOCK" ]]; then
|
|
298
|
+
{ read -r _wo < "$_WENV_LOCK/owner"; } 2>/dev/null || _wo=""
|
|
299
|
+
if [[ "$_wo" == "$$" ]]; then rm -rf -- "$_WENV_LOCK"; fi
|
|
300
|
+
_WENV_LOCK=""
|
|
301
|
+
fi
|
|
302
|
+
}
|
|
303
|
+
_wenv_fail() { # <reason> <human text>: value-free marker on stderr, one human line on stdout, exit 1
|
|
304
|
+
printf 'SOLEUR_CREDENTIAL_REFUSED script=%s reason=%s var=ENV_FILE phase=write-env\n' "$SOLEUR_TRANSPORT_SCRIPT" "$1" >&2
|
|
305
|
+
echo "Error: $2 Nothing was written and your existing .env is unchanged."
|
|
306
|
+
exit 1
|
|
307
|
+
}
|
|
308
|
+
_wenv_write() {
|
|
309
|
+
local _wf="$1" _wre="$2" _wr _wl _wm _wd _wi=0 _ws=0 _wt _wown _wo2 _wsnap="" _wstale _wgone _wwhy="" _wn _wrc _wum
|
|
310
|
+
shift 2
|
|
311
|
+
if _wr="$(readlink -f -- "$_wf" 2>/dev/null)" && [[ -n "$_wr" ]]; then
|
|
312
|
+
:
|
|
313
|
+
elif [[ -L "$_wf" ]]; then
|
|
314
|
+
_wenv_fail env_unresolvable "The .env link at ${_wf} could not be resolved."
|
|
315
|
+
else
|
|
316
|
+
_wr="$_wf"
|
|
317
|
+
fi
|
|
318
|
+
if [[ -L "$_wf" && ! -e "$_wr" ]]; then
|
|
319
|
+
_wenv_fail env_dangling_link "The .env at ${_wf} is a link to a file that does not exist."
|
|
320
|
+
fi
|
|
321
|
+
if [[ -e "$_wr" && ! -f "$_wr" ]]; then
|
|
322
|
+
_wenv_fail env_not_regular "The .env at ${_wf} is not a regular file."
|
|
323
|
+
fi
|
|
324
|
+
_wl="${_wr}.lock.d"
|
|
325
|
+
_wm="${_wl}.recover"
|
|
326
|
+
_wt="${SOLEUR_WENV_LOCK_TICKS:-$_WENV_TICKS_DEFAULT}"
|
|
327
|
+
if [[ "$_wt" =~ ^[0-9]{1,4}$ ]] && (( 10#$_wt <= _WENV_TICKS_MAX )); then _wt=$((10#$_wt)); else _wt=$_WENV_TICKS_DEFAULT; fi
|
|
328
|
+
_wum="$(umask)"
|
|
329
|
+
umask 077
|
|
330
|
+
trap '_wenv_unlock' EXIT
|
|
331
|
+
trap '_wenv_unlock; exit 130' INT
|
|
332
|
+
trap '_wenv_unlock; exit 143' TERM
|
|
333
|
+
trap '_wenv_unlock; exit 129' HUP
|
|
334
|
+
while :; do
|
|
335
|
+
if mkdir -- "$_wl" 2>/dev/null; then
|
|
336
|
+
_WENV_LOCK="$_wl"
|
|
337
|
+
if ! printf '%s\n' "$$" > "$_wl/owner"; then
|
|
338
|
+
rm -rf -- "$_wl"; _WENV_LOCK=""
|
|
339
|
+
_wenv_fail env_write_failed "The lock at ${_wl} could not be recorded."
|
|
340
|
+
fi
|
|
341
|
+
break
|
|
342
|
+
fi
|
|
343
|
+
if [[ -L "$_wl" || -f "$_wl" ]]; then
|
|
344
|
+
_wenv_fail env_lock_not_dir "The lock path ${_wl} exists but is not a directory; remove it."
|
|
345
|
+
fi
|
|
346
|
+
_wi=$((_wi + 1))
|
|
347
|
+
_wstale=0
|
|
348
|
+
if [[ ! -d "$_wl" ]]; then
|
|
349
|
+
# mkdir failed with the path absent: the lock was released in between, or mkdir fails for another
|
|
350
|
+
# reason (a name too long, no space): a pass that made no progress, so it sleeps below like any other.
|
|
351
|
+
_ws=0; _wwhy=""
|
|
352
|
+
_wd="${_wr%/*}"; [[ "$_wd" != "$_wr" ]] || _wd=.
|
|
353
|
+
[[ -d "$_wd" && -w "$_wd" ]] || _wenv_fail env_dir_unwritable "The directory of ${_wf} does not allow a lock to be created."
|
|
354
|
+
else
|
|
355
|
+
_wown=""
|
|
356
|
+
{ read -r _wown < "$_wl/owner"; } 2>/dev/null || _wown=""
|
|
357
|
+
if [[ "$_wown" =~ ^[0-9]+$ ]]; then
|
|
358
|
+
_ws=0; _wsnap=pid
|
|
359
|
+
if [[ "$_wown" == "$$" ]] || ! kill -0 "$_wown" 2>/dev/null; then _wstale=1; _wwhy=stale; else _wwhy=live; fi
|
|
360
|
+
else
|
|
361
|
+
_ws=$((_ws + 1)); _wwhy=""; _wsnap=empty
|
|
362
|
+
if (( _ws > _WENV_EMPTY_POLLS )); then _wstale=1; fi
|
|
363
|
+
fi
|
|
364
|
+
fi
|
|
365
|
+
if (( _wstale == 1 && _wi < _wt )); then
|
|
366
|
+
if mkdir -- "$_wm" 2>/dev/null; then
|
|
367
|
+
_WENV_REC="$_wm"
|
|
368
|
+
_wo2=""
|
|
369
|
+
{ read -r _wo2 < "$_wl/owner"; } 2>/dev/null || _wo2=""
|
|
370
|
+
if [[ "$_wsnap" == empty && ! "$_wo2" =~ ^[0-9]+$ ]]; then
|
|
371
|
+
sleep "$_WENV_EMPTY_PAUSE" 2>/dev/null || sleep 1
|
|
372
|
+
{ read -r _wo2 < "$_wl/owner"; } 2>/dev/null || _wo2=""
|
|
373
|
+
fi
|
|
374
|
+
_wgone=0
|
|
375
|
+
if [[ "$_wo2" =~ ^[0-9]+$ ]]; then
|
|
376
|
+
if [[ "$_wo2" == "$$" ]] || ! kill -0 "$_wo2" 2>/dev/null; then rm -rf -- "$_wl"; _ws=0; _wgone=1; fi
|
|
377
|
+
elif [[ "$_wsnap" == empty ]]; then
|
|
378
|
+
rm -rf -- "$_wl"; _ws=0; _wgone=1
|
|
379
|
+
fi
|
|
380
|
+
_wenv_unlock
|
|
381
|
+
if (( _wgone == 1 )); then continue; fi
|
|
382
|
+
fi
|
|
383
|
+
fi
|
|
384
|
+
if (( _wi >= _wt )); then
|
|
385
|
+
if [[ "$_wwhy" != live && -d "$_wm" ]]; then _wwhy=recover; fi
|
|
386
|
+
case "$_wwhy" in
|
|
387
|
+
live) _wenv_fail env_lock_timeout "Another write-env run is still holding the lock at ${_wl}; wait for it to finish and run this again. If no other run is active, remove that directory." ;;
|
|
388
|
+
recover) _wenv_fail env_lock_timeout "A lock recovery is in progress or was interrupted (${_wm} exists); waited and gave up. If no other run is active, remove that directory." ;;
|
|
389
|
+
*) _wenv_fail env_lock_timeout "The lock at ${_wl} could not be taken or recovered; waited and gave up. If no other run is active, remove that directory." ;;
|
|
390
|
+
esac
|
|
391
|
+
fi
|
|
392
|
+
sleep "$_WENV_TICK" 2>/dev/null || sleep 1
|
|
393
|
+
done
|
|
394
|
+
for _wn in "${_wr}".wenv.??????; do
|
|
395
|
+
if [[ -f "$_wn" && ! -L "$_wn" && -O "$_wn" ]]; then rm -f -- "$_wn"; fi
|
|
396
|
+
done
|
|
397
|
+
_WENV_TMP="$(mktemp "${_wr}.wenv.XXXXXX")" || _wenv_fail env_write_failed "A temp file could not be created beside ${_wf}."
|
|
398
|
+
if [[ -f "$_wr" ]]; then
|
|
399
|
+
_wrc=0
|
|
400
|
+
LC_ALL=C grep -a -v -E "$_wre" "$_wr" > "$_WENV_TMP" || _wrc=$?
|
|
401
|
+
if (( _wrc > 1 )); then
|
|
402
|
+
_wenv_fail env_unreadable "The existing .env at ${_wf} could not be read."
|
|
403
|
+
fi
|
|
404
|
+
fi
|
|
405
|
+
printf '%s\n' "$@" >> "$_WENV_TMP" || _wenv_fail env_write_failed "The new values could not be written beside ${_wf}."
|
|
406
|
+
chmod 600 "$_WENV_TMP" || _wenv_fail env_write_failed "The temp file beside ${_wf} could not be restricted to mode 600."
|
|
407
|
+
mv -f -- "$_WENV_TMP" "$_wr" || _wenv_fail env_write_failed "The new .env could not be moved into place at ${_wf}."
|
|
408
|
+
_WENV_TMP=""
|
|
409
|
+
_wenv_unlock
|
|
410
|
+
trap - EXIT INT TERM HUP
|
|
411
|
+
umask "$_wum"
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
# --- Dependency checks ---
|
|
415
|
+
|
|
416
|
+
require_jq() {
|
|
417
|
+
if ! command -v jq &>/dev/null; then
|
|
418
|
+
echo "Error: jq is required but not installed." >&2
|
|
419
|
+
echo "Install it: https://jqlang.github.io/jq/download/" >&2
|
|
420
|
+
exit 1
|
|
421
|
+
fi
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
# --- Credential validation ---
|
|
425
|
+
|
|
426
|
+
require_credentials() {
|
|
427
|
+
local missing=()
|
|
428
|
+
|
|
429
|
+
if [[ -z "${BSKY_HANDLE:-}" ]]; then
|
|
430
|
+
missing+=("BSKY_HANDLE")
|
|
431
|
+
fi
|
|
432
|
+
if [[ -z "${BSKY_APP_PASSWORD:-}" ]]; then
|
|
433
|
+
missing+=("BSKY_APP_PASSWORD")
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
437
|
+
echo "Error: Missing Bluesky credentials: ${missing[*]}" >&2
|
|
438
|
+
echo "" >&2
|
|
439
|
+
echo "To configure:" >&2
|
|
440
|
+
echo " 1. Create an account at https://bsky.app" >&2
|
|
441
|
+
echo " 2. Go to Settings > App Passwords > Add App Password" >&2
|
|
442
|
+
echo " 3. Export BSKY_HANDLE and BSKY_APP_PASSWORD as environment variables, without typing a value into the command (its text is kept in the transcript and the shell history): in a terminal, read -rs BSKY_APP_PASSWORD; export BSKY_APP_PASSWORD" >&2
|
|
443
|
+
exit 1
|
|
444
|
+
fi
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
# --- Commands ---
|
|
448
|
+
|
|
449
|
+
cmd_write_env() {
|
|
450
|
+
# write-env has no stdout payload, so its missing-credentials diagnostic goes to STDOUT too
|
|
451
|
+
# (require_credentials writes it to stderr, shared with the other commands; exit 1 either way).
|
|
452
|
+
require_credentials 2>&1
|
|
453
|
+
# Every value is checked BEFORE the first write (see the allow-list above).
|
|
454
|
+
_wenv_validate BSKY_HANDLE BSKY_APP_PASSWORD
|
|
455
|
+
|
|
456
|
+
local repo_root="$GIT_ROOT"
|
|
457
|
+
local env_file="${repo_root}/.env"
|
|
458
|
+
|
|
459
|
+
# One atomic, serialized, mode-600-from-birth write (see _wenv_write above).
|
|
460
|
+
_wenv_write "$env_file" '^(BSKY_HANDLE|BSKY_APP_PASSWORD)=' \
|
|
461
|
+
"BSKY_HANDLE=${BSKY_HANDLE}" \
|
|
462
|
+
"BSKY_APP_PASSWORD=${BSKY_APP_PASSWORD}"
|
|
463
|
+
|
|
464
|
+
echo "Wrote 2 variables to ${env_file} (permissions: 600)"
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
cmd_verify() {
|
|
468
|
+
local repo_root="$GIT_ROOT"
|
|
469
|
+
local env_file="${repo_root}/.env"
|
|
470
|
+
|
|
471
|
+
if [[ ! -f "$env_file" ]]; then
|
|
472
|
+
echo "Error: .env file not found at ${env_file}" >&2
|
|
473
|
+
exit 1
|
|
474
|
+
fi
|
|
475
|
+
|
|
476
|
+
# shellcheck disable=SC1090
|
|
477
|
+
set -a
|
|
478
|
+
# The API base above is `readonly` (#7898 review), so if this .env tries to
|
|
479
|
+
# rebind it `source` returns non-zero. Do NOT let `set -e` abort here: that
|
|
480
|
+
# would be a silent death on the credential-loading path. Capture the status,
|
|
481
|
+
# report it, and continue -- the credentials in the same file still load, and
|
|
482
|
+
# the pinned destination keeps its legitimate value.
|
|
483
|
+
_env_rc=0
|
|
484
|
+
source "$env_file" || _env_rc=$?
|
|
485
|
+
set +a
|
|
486
|
+
# `set -a` exported everything the .env defined; stop the two credentials riding every
|
|
487
|
+
# later child's environment (the curl calls need neither). The value stays bound.
|
|
488
|
+
export -n BSKY_HANDLE BSKY_APP_PASSWORD
|
|
489
|
+
if (( _env_rc != 0 )); then
|
|
490
|
+
printf 'Note: at least one assignment in %s was refused. A pinned request destination cannot be overridden from .env -- your credentials loaded normally and the request still goes to the real API.\n' "$env_file"
|
|
491
|
+
emit_transport_diag "none" "env-rebind-refused"
|
|
492
|
+
fi
|
|
493
|
+
|
|
494
|
+
require_credentials
|
|
495
|
+
|
|
496
|
+
# Create session to verify credentials
|
|
497
|
+
local response http_code body
|
|
498
|
+
local __curl_rc=0
|
|
499
|
+
# Refuse BEFORE the body is built so a refused value never reaches a child process.
|
|
500
|
+
_bsky_cred_ok "${BSKY_HANDLE:-}" || bsky_refuse control_char BSKY_HANDLE
|
|
501
|
+
_bsky_cred_ok "${BSKY_APP_PASSWORD:-}" || bsky_refuse control_char BSKY_APP_PASSWORD
|
|
502
|
+
# The body is built into a variable FIRST, so a jq failure is classified as what it is (a
|
|
503
|
+
# body-build failure) and never reaches curl: it does not depend on `pipefail`, and it can
|
|
504
|
+
# never read as a transport failure or as a successful empty POST. The credentials reach jq
|
|
505
|
+
# only through its inline environment prefix; `printf` is a builtin, so the body (which holds
|
|
506
|
+
# the password) is on no argv; curl reads it from stdin.
|
|
507
|
+
local req_body
|
|
508
|
+
req_body=$(BSKY_ID="$BSKY_HANDLE" BSKY_PW="$BSKY_APP_PASSWORD" \
|
|
509
|
+
jq -n '{identifier: $ENV.BSKY_ID, password: $ENV.BSKY_PW}') || {
|
|
510
|
+
echo "Error: could not build the Bluesky createSession request body (jq failed); nothing was sent." >&2
|
|
511
|
+
exit 1
|
|
512
|
+
}
|
|
513
|
+
response=$(printf '%s' "$req_body" \
|
|
514
|
+
| curl --disable --noproxy '*' -s -w "\n%{http_code}" \
|
|
515
|
+
-X POST \
|
|
516
|
+
-H "Content-Type: application/json" \
|
|
517
|
+
--data-binary @- \
|
|
518
|
+
"${BSKY_API}/com.atproto.server.createSession" 2>/dev/null) || __curl_rc=$?
|
|
519
|
+
if (( __curl_rc != 0 )); then
|
|
520
|
+
report_transport_failure "$__curl_rc" "Failed to connect to Bluesky API."
|
|
521
|
+
exit 1
|
|
522
|
+
fi
|
|
523
|
+
|
|
524
|
+
http_code=$(echo "$response" | tail -1)
|
|
525
|
+
body=$(echo "$response" | sed '$d')
|
|
526
|
+
|
|
527
|
+
case "$http_code" in
|
|
528
|
+
2[0-9][0-9])
|
|
529
|
+
if ! echo "$body" | jq . >/dev/null 2>&1; then
|
|
530
|
+
echo "Error: Bluesky API returned malformed JSON." >&2
|
|
531
|
+
exit 1
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
local did handle
|
|
535
|
+
did=$(echo "$body" | jq -r '.did // empty')
|
|
536
|
+
handle=$(echo "$body" | jq -r '.handle // empty')
|
|
537
|
+
|
|
538
|
+
if [[ -z "$did" ]]; then
|
|
539
|
+
echo "Error: Could not extract DID from session response." >&2
|
|
540
|
+
exit 1
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# Fetch profile to confirm identity
|
|
544
|
+
local access_jwt
|
|
545
|
+
access_jwt=$(echo "$body" | jq -r '.accessJwt // empty')
|
|
546
|
+
if ! _bearer_ok "$access_jwt"; then
|
|
547
|
+
echo "Error: Session response carried an accessJwt that is missing or has an unexpected shape; refusing to use it." >&2
|
|
548
|
+
exit 1
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
local profile_response profile_code profile_body
|
|
552
|
+
local __curl_rc=0
|
|
553
|
+
# The bearer rides curl's stdin config channel, never its argument list.
|
|
554
|
+
profile_response=$(curl --disable --noproxy '*' -s -w "\n%{http_code}" --config - \
|
|
555
|
+
"${BSKY_API}/app.bsky.actor.getProfile?actor=${did}" 2>/dev/null \
|
|
556
|
+
< <(printf 'header = "Authorization: Bearer %s"\n' "$access_jwt")) || __curl_rc=$?
|
|
557
|
+
if (( __curl_rc != 0 )); then
|
|
558
|
+
# A WARNING, not a failure -- the session was created, so the credentials
|
|
559
|
+
# are proven and this path still exits 0. It gets the marker and the
|
|
560
|
+
# proxy-aware line anyway: `--noproxy '*'` is as capable of cutting THIS
|
|
561
|
+
# request as the one above, and without the line the founder reads a
|
|
562
|
+
# deliberate bypass as an unexplained partial success.
|
|
563
|
+
emit_transport_diag "$__curl_rc" "none"
|
|
564
|
+
echo "Warning: Session created but profile fetch failed." >&2
|
|
565
|
+
if proxy_bypassed; then
|
|
566
|
+
echo "This request deliberately bypasses your proxy ($(proxy_env_names)), because a proxy can redirect a request carrying your ${SOLEUR_TRANSPORT_PLATFORM} token." >&2
|
|
567
|
+
echo "If you need Soleur to reach ${SOLEUR_TRANSPORT_PLATFORM} through your proxy, that is not currently supported -- please open an issue at https://github.com/jikig-ai/soleur/issues." >&2
|
|
568
|
+
fi
|
|
569
|
+
echo "$body" | jq '{did, handle}'
|
|
570
|
+
exit 0
|
|
571
|
+
fi
|
|
572
|
+
|
|
573
|
+
profile_code=$(echo "$profile_response" | tail -1)
|
|
574
|
+
profile_body=$(echo "$profile_response" | sed '$d')
|
|
575
|
+
|
|
576
|
+
if [[ "$profile_code" =~ ^2 ]]; then
|
|
577
|
+
echo "$profile_body" | jq '{did: .did, handle: .handle, displayName: .displayName, followersCount: .followersCount, followsCount: .followsCount, postsCount: .postsCount}'
|
|
578
|
+
echo "Credentials valid. Account: @${handle} (DID: ${did})" >&2
|
|
579
|
+
else
|
|
580
|
+
echo "$body" | jq '{did, handle}'
|
|
581
|
+
echo "Session created. Profile fetch returned HTTP ${profile_code}." >&2
|
|
582
|
+
fi
|
|
583
|
+
;;
|
|
584
|
+
401)
|
|
585
|
+
echo "Error: Bluesky returned 401 Unauthorized." >&2
|
|
586
|
+
echo "Your handle or app password may be incorrect." >&2
|
|
587
|
+
echo "" >&2
|
|
588
|
+
echo "To fix:" >&2
|
|
589
|
+
echo " 1. Verify your handle (e.g., yourname.bsky.social)" >&2
|
|
590
|
+
echo " 2. Generate a new app password at https://bsky.app/settings/app-passwords" >&2
|
|
591
|
+
echo " 3. Update BSKY_HANDLE and BSKY_APP_PASSWORD environment variables" >&2
|
|
592
|
+
exit 1
|
|
593
|
+
;;
|
|
594
|
+
*)
|
|
595
|
+
local message
|
|
596
|
+
message=$(echo "$body" | jq -r '.message // .error // "Unknown error"' 2>/dev/null || echo "Unknown error")
|
|
597
|
+
echo "Error: Bluesky API returned HTTP ${http_code}: ${message}" >&2
|
|
598
|
+
exit 1
|
|
599
|
+
;;
|
|
600
|
+
esac
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
# --- Main ---
|
|
604
|
+
|
|
605
|
+
main() {
|
|
606
|
+
local command="${1:-}"
|
|
607
|
+
shift || true
|
|
608
|
+
|
|
609
|
+
if [[ -z "$command" ]]; then
|
|
610
|
+
echo "Usage: bsky-setup.sh <command>" >&2
|
|
611
|
+
echo "" >&2
|
|
612
|
+
echo "Commands:" >&2
|
|
613
|
+
echo " write-env - Write credentials to .env with chmod 600" >&2
|
|
614
|
+
echo " verify - Source .env and verify via session + profile" >&2
|
|
615
|
+
exit 1
|
|
616
|
+
fi
|
|
617
|
+
|
|
618
|
+
require_jq
|
|
619
|
+
|
|
620
|
+
case "$command" in
|
|
621
|
+
write-env) cmd_write_env ;;
|
|
622
|
+
verify) cmd_verify ;;
|
|
623
|
+
*)
|
|
624
|
+
echo "Error: Unknown command '${command}'" >&2
|
|
625
|
+
echo "Run 'bsky-setup.sh' without arguments for usage." >&2
|
|
626
|
+
exit 1
|
|
627
|
+
;;
|
|
628
|
+
esac
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
main "$@"
|