@1aboveio/skills 0.10.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/LICENSE +3 -0
- package/README.md +90 -0
- package/bin/1aboveio-skills.mjs +18 -0
- package/package.json +28 -0
- package/runtime/skills/distribution/generated/recipes.json +1189 -0
- package/runtime/skills/distribution/scripts/bundles.mjs +280 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
- package/skills/backend/airflow-dag-develop/LICENSE +3 -0
- package/skills/backend/airflow-dag-develop/SKILL.md +111 -0
- package/skills/backend/app-debug/LICENSE +3 -0
- package/skills/backend/app-debug/SKILL.md +109 -0
- package/skills/backend/app-debug/references/common-errors.md +128 -0
- package/skills/backend/python-backend/LICENSE +3 -0
- package/skills/backend/python-backend/SKILL.md +326 -0
- package/skills/cicd-pipeline/cloud-build/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-build/SKILL.md +707 -0
- package/skills/cicd-pipeline/cloud-debug/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-debug/SKILL.md +316 -0
- package/skills/cicd-pipeline/cloud-debug/references/build-failures.md +238 -0
- package/skills/cicd-pipeline/cloud-debug/references/deploy-failures.md +376 -0
- package/skills/cicd-pipeline/cloud-debug/references/pipeline-failures.md +378 -0
- package/skills/cicd-pipeline/cloud-deploy/LICENSE +3 -0
- package/skills/cicd-pipeline/cloud-deploy/SKILL.md +229 -0
- package/skills/cicd-pipeline/cloud-deploy/references/config-templates.md +257 -0
- package/skills/cicd-pipeline/docker/LICENSE +3 -0
- package/skills/cicd-pipeline/docker/SKILL.md +126 -0
- package/skills/cicd-pipeline/google-cloud/LICENSE +3 -0
- package/skills/cicd-pipeline/google-cloud/SKILL.md +118 -0
- package/skills/cicd-pipeline/google-cloud/references/gcs.md +469 -0
- package/skills/cicd-pipeline/google-cloud/references/iam.md +451 -0
- package/skills/cicd-pipeline/google-cloud/references/project.md +349 -0
- package/skills/cicd-pipeline/google-cloud/references/secrets.md +336 -0
- package/skills/cicd-pipeline/google-cloud/references/vpc.md +312 -0
- package/skills/cicd-pipeline/google-cloud/scripts/create-sa.sh +36 -0
- package/skills/cicd-pipeline/google-cloud/scripts/gcp-config.sh +31 -0
- package/skills/cicd-pipeline/google-cloud/scripts/grant-iap.sh +41 -0
- package/skills/cicd-pipeline/google-cloud/scripts/setup-secrets.sh +48 -0
- package/skills/cicd-pipeline/mergify/LICENSE +3 -0
- package/skills/cicd-pipeline/mergify/SKILL.md +138 -0
- package/skills/cicd-pipeline/mergify/assets/templates/mergify.yml +237 -0
- package/skills/cicd-pipeline/mergify/assets/templates/ruleset.json +46 -0
- package/skills/cicd-pipeline/mergify/references/branch-protection.md +277 -0
- package/skills/cicd-pipeline/mergify/references/configuration.md +183 -0
- package/skills/cicd-pipeline/mergify/references/diagnosis.md +73 -0
- package/skills/cicd-pipeline/mergify/references/traps.md +78 -0
- package/skills/cicd-pipeline/mergify/references/watch-contract.md +218 -0
- package/skills/cicd-pipeline/mergify/scripts/audit-core.mjs +131 -0
- package/skills/cicd-pipeline/mergify/scripts/audit.mjs +4 -0
- package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery-core.mjs +663 -0
- package/skills/cicd-pipeline/mergify/scripts/watch-pr-delivery.mjs +4 -0
- package/skills/cicd-pipeline/podman/LICENSE +3 -0
- package/skills/cicd-pipeline/podman/SKILL.md +70 -0
- package/skills/cicd-pipeline/podman/agents/openai.yaml +4 -0
- package/skills/cicd-pipeline/podman/assets/templates/podman-compose-socket-directory.yml +6 -0
- package/skills/cicd-pipeline/podman/assets/templates/podman-service-override.conf +3 -0
- package/skills/cicd-pipeline/podman/references/compose-compatibility.md +70 -0
- package/skills/cicd-pipeline/podman/references/networking-and-ports.md +74 -0
- package/skills/cicd-pipeline/podman/references/rootless-services-and-sockets.md +156 -0
- package/skills/cicd-pipeline/podman/references/troubleshooting.md +98 -0
- package/skills/engineering/e2e-test/LICENSE +3 -0
- package/skills/engineering/e2e-test/SKILL.md +156 -0
- package/skills/engineering/e2e-test/assets/ci-gates.cloudbuild.yaml +272 -0
- package/skills/engineering/e2e-test/assets/ci-gates.github.yml +451 -0
- package/skills/engineering/e2e-test/assets/e2e-workflow.yml +282 -0
- package/skills/engineering/e2e-test/references/authoring/auth-flows.md +159 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-config.md +71 -0
- package/skills/engineering/e2e-test/references/authoring/playwright-patterns.md +219 -0
- package/skills/engineering/e2e-test/references/authoring/test-skipping.md +44 -0
- package/skills/engineering/e2e-test/references/ci-integration.md +121 -0
- package/skills/engineering/e2e-test/references/ci-playwright-container.md +280 -0
- package/skills/engineering/e2e-test/references/debugging.md +36 -0
- package/skills/engineering/e2e-test/references/presentation-sweep.md +131 -0
- package/skills/engineering/e2e-test/references/reviewing.md +39 -0
- package/skills/engineering/e2e-test/references/route-discovery.md +50 -0
- package/skills/engineering/e2e-test/references/route-manifest.md +44 -0
- package/skills/engineering/e2e-test/scripts/detect-routes-fastapi.py +290 -0
- package/skills/engineering/e2e-test/scripts/detect-routes-nextjs.mjs +200 -0
- package/skills/engineering/e2e-test/scripts/post-visual-evidence.mjs +158 -0
- package/skills/engineering/e2e-test/scripts/presentation-checks.mjs +171 -0
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.mjs +179 -0
- package/skills/engineering/e2e-test/scripts/presentation-reachability.mjs +154 -0
- package/skills/engineering/e2e-test/scripts/presentation-render-health.mjs +141 -0
- package/skills/engineering/e2e-test/scripts/presentation-sweep.mjs +148 -0
- package/skills/engineering/e2e-test/scripts/presentation-temporal.mjs +127 -0
- package/skills/engineering/e2e-test/scripts/presentation-visual.mjs +84 -0
- package/skills/engineering/e2e-test/scripts/project-route-manifest.mjs +75 -0
- package/skills/engineering/e2e-test/scripts/validate-manifest.mjs +106 -0
- package/skills/engineering/engineering-runtime/LICENSE +3 -0
- package/skills/engineering/engineering-runtime/SKILL.md +10 -0
- package/skills/engineering/engineering-runtime/agents/openai.yaml +6 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +553 -0
- package/skills/engineering/engineering-runtime/scripts/exact-head-artifact.mjs +131 -0
- package/skills/engineering/engineering-runtime/scripts/head-check-set.mjs +398 -0
- package/skills/engineering/engineering-runtime/scripts/main-module.mjs +80 -0
- package/skills/engineering/engineering-runtime/scripts/mergify-yaml.mjs +11 -0
- package/skills/engineering/engineering-runtime/scripts/package-lock.json +43 -0
- package/skills/engineering/engineering-runtime/scripts/package.json +10 -0
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.mjs +223 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +576 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +166 -0
- package/skills/engineering/ensure-coverage/LICENSE +3 -0
- package/skills/engineering/ensure-coverage/SKILL.md +136 -0
- package/skills/engineering/ensure-coverage/evals/evals.json +125 -0
- package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +91 -0
- package/skills/engineering/ensure-coverage/references/breadth/inventory-contract.md +83 -0
- package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +44 -0
- package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +16 -0
- package/skills/engineering/ensure-coverage/references/depth/characterization.md +68 -0
- package/skills/engineering/ensure-coverage/references/depth/coverage.config.example.json +25 -0
- package/skills/engineering/ensure-coverage/references/depth/grading.md +35 -0
- package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +87 -0
- package/skills/engineering/ensure-coverage/references/depth/test-smells.md +23 -0
- package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +164 -0
- package/skills/engineering/ensure-coverage/references/enforcement/hooks.md +85 -0
- package/skills/engineering/ensure-coverage/references/examples/coverage-ledger.md +109 -0
- package/skills/engineering/ensure-coverage/references/examples/refund-flow.md +33 -0
- package/skills/engineering/ensure-coverage/references/presentation/axis.md +78 -0
- package/skills/engineering/ensure-coverage/references/presentation/runner-contract.md +74 -0
- package/skills/engineering/ensure-coverage/references/process/audit-mode.md +33 -0
- package/skills/engineering/ensure-coverage/references/process/output-template.md +139 -0
- package/skills/engineering/ensure-coverage/references/process/review-contract-template.md +119 -0
- package/skills/engineering/ensure-coverage/references/process/scope-class.md +178 -0
- package/skills/engineering/ensure-coverage/references/process/test-strategy.md +55 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-config.schema.json +45 -0
- package/skills/engineering/ensure-coverage/schemas/coverage-file.schema.json +93 -0
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.mjs +178 -0
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.mjs +101 -0
- package/skills/engineering/ensure-coverage/scripts/ci-audit.mjs +358 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-checklist.mjs +494 -0
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.mjs +663 -0
- package/skills/engineering/ensure-coverage/scripts/design-parity.mjs +591 -0
- package/skills/engineering/ensure-coverage/scripts/evidence-block.mjs +367 -0
- package/skills/engineering/ensure-coverage/scripts/lint-tests.mjs +269 -0
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.mjs +176 -0
- package/skills/engineering/ensure-coverage/scripts/package-lock.json +76 -0
- package/skills/engineering/ensure-coverage/scripts/package.json +19 -0
- package/skills/engineering/ensure-coverage/scripts/scope-class.mjs +554 -0
- package/skills/engineering/harness-runtime/LICENSE +3 -0
- package/skills/engineering/harness-runtime/SKILL.md +18 -0
- package/skills/engineering/harness-runtime/agents/openai.yaml +6 -0
- package/skills/engineering/harness-runtime/bin/discover-models.mjs +4 -0
- package/skills/engineering/harness-runtime/bin/model-catalog.mjs +4 -0
- package/skills/engineering/harness-runtime/contracts.md +15 -0
- package/skills/engineering/harness-runtime/discover-models.mjs +392 -0
- package/skills/engineering/harness-runtime/fixtures/native-question-schemas.json +33 -0
- package/skills/engineering/harness-runtime/fixtures/question-responses.json +54 -0
- package/skills/engineering/harness-runtime/index.mjs +767 -0
- package/skills/engineering/harness-runtime/model-catalog.mjs +787 -0
- package/skills/engineering/harness-runtime/native-question-contracts.md +37 -0
- package/skills/engineering/harness-runtime/references/model-catalog-seed.json +159 -0
- package/skills/engineering/harness-runtime/references/model-catalog.md +57 -0
- package/skills/engineering/implement-and-pr/LICENSE +3 -0
- package/skills/engineering/implement-and-pr/SKILL.md +176 -0
- package/skills/engineering/implement-and-pr/references/ci-iteration.md +10 -0
- package/skills/engineering/implement-and-pr/references/closeout.md +27 -0
- package/skills/engineering/implement-and-pr/references/contract-complete-fix-rounds.md +34 -0
- package/skills/engineering/implement-and-pr/references/evidence-rules.md +39 -0
- package/skills/engineering/implement-and-pr/references/incremental-plan.md +16 -0
- package/skills/engineering/implement-and-pr/references/self-review.md +23 -0
- package/skills/engineering/implement-and-pr/references/tdd-mode.md +18 -0
- package/skills/engineering/resolve-issues/LICENSE +3 -0
- package/skills/engineering/resolve-issues/SKILL.md +167 -0
- package/skills/engineering/resolve-issues/generated/workflow-repair-policy.json +448 -0
- package/skills/engineering/resolve-issues/references/breaker.md +82 -0
- package/skills/engineering/resolve-issues/references/deliverables.md +27 -0
- package/skills/engineering/resolve-issues/references/delivery.md +108 -0
- package/skills/engineering/resolve-issues/references/evidence-lane.md +21 -0
- package/skills/engineering/resolve-issues/references/exact-head-ci.md +287 -0
- package/skills/engineering/resolve-issues/references/fan-out.md +33 -0
- package/skills/engineering/resolve-issues/references/finalization.md +68 -0
- package/skills/engineering/resolve-issues/references/guarantees.md +10 -0
- package/skills/engineering/resolve-issues/references/high-risk.md +29 -0
- package/skills/engineering/resolve-issues/references/incidents/848/README.md +156 -0
- package/skills/engineering/resolve-issues/references/intake.md +86 -0
- package/skills/engineering/resolve-issues/references/integration-gate.md +53 -0
- package/skills/engineering/resolve-issues/references/interference.md +87 -0
- package/skills/engineering/resolve-issues/references/loop.md +134 -0
- package/skills/engineering/resolve-issues/references/model-catalog.md +9 -0
- package/skills/engineering/resolve-issues/references/postmortem.md +27 -0
- package/skills/engineering/resolve-issues/references/pre-flight-model-slots.md +41 -0
- package/skills/engineering/resolve-issues/references/pre-flight-recording-and-checkout.md +48 -0
- package/skills/engineering/resolve-issues/references/pre-flight.md +41 -0
- package/skills/engineering/resolve-issues/references/regression-checklist.md +26 -0
- package/skills/engineering/resolve-issues/references/run-state.md +288 -0
- package/skills/engineering/resolve-issues/references/sandboxed-testing.md +48 -0
- package/skills/engineering/resolve-issues/references/spawn-contract.md +96 -0
- package/skills/engineering/resolve-issues/references/terminal-evidence-journal.md +40 -0
- package/skills/engineering/resolve-issues/references/why.md +653 -0
- package/skills/engineering/resolve-issues/schemas/fix-round.schema.json +49 -0
- package/skills/engineering/resolve-issues/scripts/combine-and-verify.mjs +721 -0
- package/skills/engineering/resolve-issues/scripts/component-candidate.mjs +962 -0
- package/skills/engineering/resolve-issues/scripts/contract-revision.mjs +220 -0
- package/skills/engineering/resolve-issues/scripts/detect-delivery-mode.mjs +420 -0
- package/skills/engineering/resolve-issues/scripts/detect-target-branch.mjs +256 -0
- package/skills/engineering/resolve-issues/scripts/detect-workspace-mode.mjs +168 -0
- package/skills/engineering/resolve-issues/scripts/discover-models.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/doctrine.mjs +62 -0
- package/skills/engineering/resolve-issues/scripts/evidence-lifecycle-contract.mjs +191 -0
- package/skills/engineering/resolve-issues/scripts/exact-head-ci.mjs +413 -0
- package/skills/engineering/resolve-issues/scripts/exact-head-github-provider.mjs +332 -0
- package/skills/engineering/resolve-issues/scripts/finalize.mjs +488 -0
- package/skills/engineering/resolve-issues/scripts/fix-rounds.mjs +3307 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-circular-1001.json +16 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/evidence-lifecycle-valid-sequencing.json +51 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/fmm-express-830-component-candidate.json +17 -0
- package/skills/engineering/resolve-issues/scripts/fixtures/head-check-set-1081.json +166 -0
- package/skills/engineering/resolve-issues/scripts/gate-value-series.mjs +92 -0
- package/skills/engineering/resolve-issues/scripts/guide-index.mjs +73 -0
- package/skills/engineering/resolve-issues/scripts/head-check-set.mjs +159 -0
- package/skills/engineering/resolve-issues/scripts/interference.mjs +427 -0
- package/skills/engineering/resolve-issues/scripts/model-catalog.mjs +9 -0
- package/skills/engineering/resolve-issues/scripts/next-operations.mjs +419 -0
- package/skills/engineering/resolve-issues/scripts/postmortem.mjs +909 -0
- package/skills/engineering/resolve-issues/scripts/preflight-questions.mjs +322 -0
- package/skills/engineering/resolve-issues/scripts/reconcile-contained-unit-prs.mjs +415 -0
- package/skills/engineering/resolve-issues/scripts/release-state-contract.mjs +697 -0
- package/skills/engineering/resolve-issues/scripts/report.mjs +494 -0
- package/skills/engineering/resolve-issues/scripts/required-check-plan.mjs +172 -0
- package/skills/engineering/resolve-issues/scripts/round-metadata.mjs +79 -0
- package/skills/engineering/resolve-issues/scripts/run-state-review6-cases.mjs +334 -0
- package/skills/engineering/resolve-issues/scripts/run-state.mjs +4784 -0
- package/skills/engineering/resolve-issues/scripts/sandbox-selftest.mjs +395 -0
- package/skills/engineering/resolve-issues/scripts/spawn-contract.mjs +290 -0
- package/skills/engineering/resolve-issues/scripts/terminal-dispositions.mjs +170 -0
- package/skills/engineering/resolve-issues/scripts/terminal-evidence-journal.mjs +293 -0
- package/skills/engineering/resolve-issues/scripts/unit-kind.mjs +197 -0
- package/skills/engineering/resolve-issues/scripts/unit-lifecycle.mjs +127 -0
- package/skills/engineering/resolve-issues/scripts/watch-delivery.mjs +893 -0
- package/skills/engineering/resolve-issues/scripts/workspaces.mjs +829 -0
- package/skills/engineering/resolve-issues/workflows/independent-review.workflow.js +290 -0
- package/skills/engineering/resolve-issues/workflows/prior-art-scan.workflow.js +80 -0
- package/skills/engineering/resolve-issues/workflows/workflow-smoke.mjs +102 -0
- package/skills/engineering/resolve-release/LICENSE +3 -0
- package/skills/engineering/resolve-release/SKILL.md +112 -0
- package/skills/engineering/resolve-release/references/assembly.md +137 -0
- package/skills/engineering/resolve-release/references/auto-when-green.md +56 -0
- package/skills/engineering/resolve-release/references/candidate.md +167 -0
- package/skills/engineering/resolve-release/references/exposure.md +178 -0
- package/skills/engineering/resolve-release/references/handoff.md +24 -0
- package/skills/engineering/resolve-release/references/postmortem.md +230 -0
- package/skills/engineering/resolve-release/references/preflight.md +207 -0
- package/skills/engineering/resolve-release/references/principles.md +94 -0
- package/skills/engineering/resolve-release/references/regression-checklist.md +36 -0
- package/skills/engineering/resolve-release/references/related-skills.md +12 -0
- package/skills/engineering/resolve-release/references/routing.md +149 -0
- package/skills/engineering/resolve-release/references/verified-sha-github-flow.md +285 -0
- package/skills/engineering/resolve-release/references/versioning.md +202 -0
- package/skills/engineering/resolve-release/references/why.md +53 -0
- package/skills/engineering/resolve-release/scripts/adapter-completion-artifact.mjs +389 -0
- package/skills/engineering/resolve-release/scripts/build-changes.mjs +209 -0
- package/skills/engineering/resolve-release/scripts/candidate-hygiene.mjs +407 -0
- package/skills/engineering/resolve-release/scripts/candidate-identity.mjs +904 -0
- package/skills/engineering/resolve-release/scripts/candidate-traffic.mjs +81 -0
- package/skills/engineering/resolve-release/scripts/checked-adapter-loader.mjs +612 -0
- package/skills/engineering/resolve-release/scripts/close-attempt.mjs +135 -0
- package/skills/engineering/resolve-release/scripts/closeout-release.mjs +161 -0
- package/skills/engineering/resolve-release/scripts/doctrine.mjs +106 -0
- package/skills/engineering/resolve-release/scripts/durable-processing.mjs +522 -0
- package/skills/engineering/resolve-release/scripts/ensure-target-green.mjs +659 -0
- package/skills/engineering/resolve-release/scripts/evidence-bundle.mjs +1014 -0
- package/skills/engineering/resolve-release/scripts/finalize-release.mjs +526 -0
- package/skills/engineering/resolve-release/scripts/fixtures/durable-processing-adapter.mjs +169 -0
- package/skills/engineering/resolve-release/scripts/green-gate.mjs +599 -0
- package/skills/engineering/resolve-release/scripts/isolated-adapter-evaluator.mjs +752 -0
- package/skills/engineering/resolve-release/scripts/metadata-pr-status.mjs +56 -0
- package/skills/engineering/resolve-release/scripts/metadata-sync.mjs +1538 -0
- package/skills/engineering/resolve-release/scripts/postmortem.mjs +381 -0
- package/skills/engineering/resolve-release/scripts/preflight-probes.mjs +498 -0
- package/skills/engineering/resolve-release/scripts/production-endpoints.mjs +326 -0
- package/skills/engineering/resolve-release/scripts/rc-circuit-breaker.mjs +272 -0
- package/skills/engineering/resolve-release/scripts/report.mjs +417 -0
- package/skills/engineering/resolve-release/scripts/reprobe-credentials.mjs +114 -0
- package/skills/engineering/resolve-release/scripts/revalidate-candidate.mjs +238 -0
- package/skills/engineering/resolve-release/scripts/review-packet.mjs +503 -0
- package/skills/engineering/resolve-release/scripts/rollback-floor.mjs +263 -0
- package/skills/engineering/resolve-release/scripts/version-assert.mjs +339 -0
- package/skills/engineering/resolve-release/scripts/version-postmortem.mjs +485 -0
- package/skills/engineering/resolve-release/scripts/version.mjs +1199 -0
- package/skills/engineering/resolve-release/scripts/watch-candidate-delivery.mjs +449 -0
- package/skills/engineering/resolve-release/vendor/ACORN-LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/README.md +60 -0
- package/skills/engineering/resolve-release/vendor/acorn.mjs +6233 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.md +341 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/README.template.md +70 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-TAV5CUKK.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs +7 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/chunk-V2S4ZYJR.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.mts +2033 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.d.ts +2033 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js +7 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-ES6BEMUI.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/dist/module-asyncify-2EFITU5U.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/core/package.json +49 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/LICENSE +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/README.md +5 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.mts +549 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.d.ts +549 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/ffi-types/package.json +36 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/LICENSE +47 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/README.md +82 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.browser.mjs +22 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cjs +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.cjs +21 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.cloudflare.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.d.ts +11 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.mjs +25 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/emscripten-module.wasm +0 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.mts +85 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.d.ts +85 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/ffi.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.mts +20 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.d.ts +20 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.js.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs +2 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/dist/index.mjs.map +1 -0
- package/skills/engineering/resolve-release/vendor/quickjs/release-sync/package.json +61 -0
- package/skills/engineering/review-pr/LICENSE +3 -0
- package/skills/engineering/review-pr/SKILL.md +123 -0
- package/skills/engineering/review-pr/references/adversarial-reviewer-prompt.md +42 -0
- package/skills/engineering/review-pr/references/code-correctness.md +5 -0
- package/skills/engineering/review-pr/references/contract-freshness.md +9 -0
- package/skills/engineering/review-pr/references/coordination.md +18 -0
- package/skills/engineering/review-pr/references/domain-hazards.md +123 -0
- package/skills/engineering/review-pr/references/finding-themes.md +7 -0
- package/skills/engineering/review-pr/references/github-posting.md +98 -0
- package/skills/engineering/review-pr/references/golden-path-smoke.md +5 -0
- package/skills/engineering/review-pr/references/incremental-output.md +16 -0
- package/skills/engineering/review-pr/references/inputs-and-discovery.md +31 -0
- package/skills/engineering/review-pr/references/output-format.md +99 -0
- package/skills/engineering/review-pr/references/over-mock-screen.md +7 -0
- package/skills/engineering/review-pr/references/promotion-prs.md +16 -0
- package/skills/engineering/review-pr/references/re-review.md +18 -0
- package/skills/engineering/review-pr/references/review-method.md +199 -0
- package/skills/engineering/review-pr/references/review-mode.md +30 -0
- package/skills/engineering/review-pr/references/review-posture.md +53 -0
- package/skills/engineering/review-pr/references/round1-depth.md +62 -0
- package/skills/engineering/review-pr/references/scripts.md +17 -0
- package/skills/engineering/review-pr/references/workflow.md +16 -0
- package/skills/engineering/review-pr/schemas/findings.schema.json +282 -0
- package/skills/engineering/review-pr/scripts/finding-contract.mjs +285 -0
- package/skills/engineering/review-pr/scripts/post-review.mjs +405 -0
- package/skills/engineering/review-pr/scripts/pr-context.mjs +207 -0
- package/skills/engineering/review-pr/scripts/scan-diff.mjs +365 -0
- package/skills/engineering/review-pr/scripts/theme-contract.mjs +57 -0
- package/skills/engineering/smoke/LICENSE +3 -0
- package/skills/engineering/smoke/SKILL.md +131 -0
- package/skills/engineering/smoke/assets/smoke.manifest.example.json +53 -0
- package/skills/engineering/smoke/references/manifest.md +192 -0
- package/skills/engineering/smoke/scripts/smoke.mjs +713 -0
- package/skills/fullstack/better-auth/LICENSE +3 -0
- package/skills/fullstack/better-auth/SKILL.md +601 -0
- package/skills/fullstack/better-auth/references/feishu-api.md +270 -0
- package/skills/fullstack/monorepo/LICENSE +3 -0
- package/skills/fullstack/monorepo/SKILL.md +465 -0
- package/skills/fullstack/nextjs-fullstack/LICENSE +3 -0
- package/skills/fullstack/nextjs-fullstack/SKILL.md +210 -0
- package/skills/fullstack/nextjs-fullstack/conventions.md +318 -0
- package/skills/fullstack/nextjs-fullstack/frontend-conventions.md +61 -0
- package/skills/fullstack/nextjs-fullstack/nextjs16.md +287 -0
- package/skills/fullstack/nextjs-fullstack/server-actions.md +409 -0
- package/skills/fullstack/prisma-setup/LICENSE +3 -0
- package/skills/fullstack/prisma-setup/SKILL.md +180 -0
- package/skills/fullstack/prisma-setup/nextjs.md +258 -0
- package/skills/fullstack/prisma-setup/turborepo.md +301 -0
- package/skills/fullstack/shadcn/LICENSE +3 -0
- package/skills/fullstack/shadcn/SKILL.md +119 -0
- package/skills/fullstack/shadcn/assets/shadcn-small.png +0 -0
- package/skills/fullstack/shadcn/assets/shadcn.png +0 -0
- package/skills/fullstack/shadcn/cli.md +411 -0
- package/skills/fullstack/shadcn/customization.md +224 -0
- package/skills/fullstack/shadcn/evals/evals.json +90 -0
- package/skills/fullstack/shadcn/mcp.md +101 -0
- package/skills/fullstack/shadcn/rules/base-vs-radix.md +323 -0
- package/skills/fullstack/shadcn/rules/component-selection.md +67 -0
- package/skills/fullstack/shadcn/rules/composition.md +195 -0
- package/skills/fullstack/shadcn/rules/data-table.md +201 -0
- package/skills/fullstack/shadcn/rules/forms.md +255 -0
- package/skills/fullstack/shadcn/rules/icons.md +103 -0
- package/skills/fullstack/shadcn/rules/styling.md +167 -0
- package/skills/fullstack/zod-v4/LICENSE +3 -0
- package/skills/fullstack/zod-v4/SKILL.md +287 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Detect all FastAPI routes via static analysis and compare against endpoint manifest.
|
|
4
|
+
Fails CI if any route exists in the app but not in the manifest.
|
|
5
|
+
|
|
6
|
+
Scans Python files for:
|
|
7
|
+
- @router.get("/path") @router.post("/path")
|
|
8
|
+
- @app.get("/path") @app.post("/path")
|
|
9
|
+
- @api.get("/path") @api.post("/path")
|
|
10
|
+
- app.include_router(router, prefix="/prefix")
|
|
11
|
+
|
|
12
|
+
Handles:
|
|
13
|
+
- Decorator-based route definitions
|
|
14
|
+
- Router prefixes from include_router calls
|
|
15
|
+
- String literals and f-strings (partial — extracts static prefix)
|
|
16
|
+
- Commented-out routes (skipped)
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
python detect-routes-fastapi.py [options]
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--src-dir <path> Path to scan for Python files (default: .)
|
|
23
|
+
--manifest <path> Path to endpoint-manifest.json (default: tests/e2e/endpoint-manifest.json)
|
|
24
|
+
--exclude <pattern> Glob pattern to exclude (repeatable, default: tests/*, **/test_*)
|
|
25
|
+
--strict Exit 1 if any route missing from manifest (default)
|
|
26
|
+
--no-strict Only warn, don't fail
|
|
27
|
+
|
|
28
|
+
Exit codes:
|
|
29
|
+
0 = all routes accounted for
|
|
30
|
+
1 = routes missing from manifest (strict mode)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import argparse
|
|
34
|
+
import ast
|
|
35
|
+
import json
|
|
36
|
+
import os
|
|
37
|
+
import re
|
|
38
|
+
import sys
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from fnmatch import fnmatch
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_args():
|
|
44
|
+
parser = argparse.ArgumentParser(description="Detect FastAPI routes")
|
|
45
|
+
parser.add_argument("--src-dir", default=".", help="Source directory to scan")
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--manifest",
|
|
48
|
+
default="tests/e2e/endpoint-manifest.json",
|
|
49
|
+
help="Path to endpoint manifest",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--exclude",
|
|
53
|
+
action="append",
|
|
54
|
+
default=None,
|
|
55
|
+
help="Glob patterns to exclude (repeatable)",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--strict",
|
|
59
|
+
dest="strict",
|
|
60
|
+
action="store_true",
|
|
61
|
+
default=True,
|
|
62
|
+
help="Fail on missing routes (default)",
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--no-strict", dest="strict", action="store_false", help="Warn only"
|
|
66
|
+
)
|
|
67
|
+
return parser.parse_args()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ─── Route extraction ────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
HTTP_METHODS = {"get", "post", "put", "delete", "patch", "head", "options"}
|
|
73
|
+
|
|
74
|
+
# Pattern: @something.method("path") or @something.method("path", ...)
|
|
75
|
+
ROUTE_DECORATOR_RE = re.compile(
|
|
76
|
+
r"^\s*@(\w+)\.(get|post|put|delete|patch|head|options)\(\s*"
|
|
77
|
+
r'["\']([^"\']*)["\']',
|
|
78
|
+
re.MULTILINE,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Pattern: app.include_router(var, prefix="path")
|
|
82
|
+
INCLUDE_ROUTER_RE = re.compile(
|
|
83
|
+
r'\.include_router\(\s*(\w+)(?:.*?prefix\s*=\s*["\']([^"\']*)["\'])?',
|
|
84
|
+
re.DOTALL,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def extract_routes_from_file(filepath: str) -> list[dict]:
|
|
89
|
+
"""Extract route definitions from a Python file using regex.
|
|
90
|
+
|
|
91
|
+
Returns list of { method, path, file, line }
|
|
92
|
+
"""
|
|
93
|
+
routes = []
|
|
94
|
+
try:
|
|
95
|
+
content = Path(filepath).read_text(encoding="utf-8")
|
|
96
|
+
except Exception:
|
|
97
|
+
return routes
|
|
98
|
+
|
|
99
|
+
lines = content.split("\n")
|
|
100
|
+
for i, line in enumerate(lines, 1):
|
|
101
|
+
# Skip comments
|
|
102
|
+
stripped = line.lstrip()
|
|
103
|
+
if stripped.startswith("#"):
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
match = ROUTE_DECORATOR_RE.match(stripped)
|
|
107
|
+
if match:
|
|
108
|
+
_var_name, method, path = match.groups()
|
|
109
|
+
routes.append(
|
|
110
|
+
{
|
|
111
|
+
"method": method.upper(),
|
|
112
|
+
"path": path,
|
|
113
|
+
"file": filepath,
|
|
114
|
+
"line": i,
|
|
115
|
+
"router_var": _var_name,
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return routes
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def find_router_prefixes(filepath: str) -> dict[str, str]:
|
|
123
|
+
"""Find include_router calls and their prefixes.
|
|
124
|
+
|
|
125
|
+
Returns { router_var_name: prefix }
|
|
126
|
+
"""
|
|
127
|
+
prefixes = {}
|
|
128
|
+
try:
|
|
129
|
+
content = Path(filepath).read_text(encoding="utf-8")
|
|
130
|
+
except Exception:
|
|
131
|
+
return prefixes
|
|
132
|
+
|
|
133
|
+
for match in INCLUDE_ROUTER_RE.finditer(content):
|
|
134
|
+
var_name = match.group(1)
|
|
135
|
+
prefix = match.group(2) or ""
|
|
136
|
+
if prefix:
|
|
137
|
+
prefixes[var_name] = prefix
|
|
138
|
+
|
|
139
|
+
return prefixes
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def scan_directory(src_dir: str, excludes: list[str]) -> tuple[list[dict], dict]:
|
|
143
|
+
"""Scan all Python files for routes and router prefixes."""
|
|
144
|
+
all_routes = []
|
|
145
|
+
all_prefixes = {}
|
|
146
|
+
|
|
147
|
+
for root, dirs, files in os.walk(src_dir):
|
|
148
|
+
# Skip common non-source directories
|
|
149
|
+
dirs[:] = [
|
|
150
|
+
d
|
|
151
|
+
for d in dirs
|
|
152
|
+
if d not in {"__pycache__", ".git", "node_modules", ".venv", "venv"}
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
for fname in files:
|
|
156
|
+
if not fname.endswith(".py"):
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
filepath = os.path.join(root, fname)
|
|
160
|
+
rel_path = os.path.relpath(filepath, src_dir)
|
|
161
|
+
|
|
162
|
+
# Check excludes
|
|
163
|
+
if any(fnmatch(rel_path, pat) for pat in excludes):
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
routes = extract_routes_from_file(filepath)
|
|
167
|
+
if routes:
|
|
168
|
+
all_routes.extend(routes)
|
|
169
|
+
|
|
170
|
+
prefixes = find_router_prefixes(filepath)
|
|
171
|
+
all_prefixes.update(prefixes)
|
|
172
|
+
|
|
173
|
+
return all_routes, all_prefixes
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def resolve_full_paths(
|
|
177
|
+
routes: list[dict], prefixes: dict[str, str]
|
|
178
|
+
) -> list[dict]:
|
|
179
|
+
"""Apply router prefixes to route paths to get full paths."""
|
|
180
|
+
resolved = []
|
|
181
|
+
for route in routes:
|
|
182
|
+
prefix = prefixes.get(route["router_var"], "")
|
|
183
|
+
full_path = prefix.rstrip("/") + "/" + route["path"].lstrip("/")
|
|
184
|
+
# Normalize double slashes
|
|
185
|
+
full_path = re.sub(r"//+", "/", full_path)
|
|
186
|
+
if not full_path.startswith("/"):
|
|
187
|
+
full_path = "/" + full_path
|
|
188
|
+
resolved.append(
|
|
189
|
+
{
|
|
190
|
+
"method": route["method"],
|
|
191
|
+
"path": full_path,
|
|
192
|
+
"file": route["file"],
|
|
193
|
+
"line": route["line"],
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
return resolved
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ─── Manifest comparison ─────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def load_manifest(path: str) -> set[tuple[str, str]]:
|
|
203
|
+
"""Load endpoint manifest. Returns set of (METHOD, path) tuples."""
|
|
204
|
+
if not os.path.exists(path):
|
|
205
|
+
print(f"❌ Manifest not found: {path}")
|
|
206
|
+
print(f' Create it with: {{ "version": 1, "endpoints": [] }}')
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
with open(path) as f:
|
|
211
|
+
data = json.load(f)
|
|
212
|
+
except json.JSONDecodeError as e:
|
|
213
|
+
print(f"❌ Invalid JSON in manifest: {e}")
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
endpoints = data.get("endpoints", [])
|
|
217
|
+
return {(ep.get("method", "GET").upper(), ep["path"]) for ep in endpoints}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# ─── Main ────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def main():
|
|
224
|
+
args = parse_args()
|
|
225
|
+
src_dir = os.path.abspath(args.src_dir)
|
|
226
|
+
manifest_path = os.path.abspath(args.manifest)
|
|
227
|
+
excludes = args.exclude or ["tests/*", "**/test_*", "**/*_test.py"]
|
|
228
|
+
|
|
229
|
+
print(f"🔍 Scanning: {os.path.relpath(src_dir)}/")
|
|
230
|
+
print(f"📋 Manifest: {os.path.relpath(manifest_path)}")
|
|
231
|
+
print(f"🚫 Excluding: {', '.join(excludes)}\n")
|
|
232
|
+
|
|
233
|
+
# Scan
|
|
234
|
+
raw_routes, prefixes = scan_directory(src_dir, excludes)
|
|
235
|
+
routes = resolve_full_paths(raw_routes, prefixes)
|
|
236
|
+
|
|
237
|
+
# Deduplicate
|
|
238
|
+
seen = set()
|
|
239
|
+
unique_routes = []
|
|
240
|
+
for r in routes:
|
|
241
|
+
key = (r["method"], r["path"])
|
|
242
|
+
if key not in seen:
|
|
243
|
+
seen.add(key)
|
|
244
|
+
unique_routes.append(r)
|
|
245
|
+
|
|
246
|
+
# Load manifest
|
|
247
|
+
manifest_entries = load_manifest(manifest_path)
|
|
248
|
+
|
|
249
|
+
# Compare
|
|
250
|
+
missing = []
|
|
251
|
+
accounted = []
|
|
252
|
+
for route in unique_routes:
|
|
253
|
+
key = (route["method"], route["path"])
|
|
254
|
+
if key in manifest_entries:
|
|
255
|
+
accounted.append(route)
|
|
256
|
+
else:
|
|
257
|
+
missing.append(route)
|
|
258
|
+
|
|
259
|
+
print(
|
|
260
|
+
f"Found {len(unique_routes)} routes "
|
|
261
|
+
f"({len(set(r['method'] for r in unique_routes))} methods)"
|
|
262
|
+
)
|
|
263
|
+
print(f"Manifest has {len(manifest_entries)} entries")
|
|
264
|
+
print(f"Accounted: {len(accounted)} | Missing: {len(missing)}\n")
|
|
265
|
+
|
|
266
|
+
if missing:
|
|
267
|
+
print("─" * 60)
|
|
268
|
+
print("❌ Routes in app but NOT in manifest:\n")
|
|
269
|
+
for route in sorted(missing, key=lambda r: r["path"]):
|
|
270
|
+
print(f" {route['method']} {route['path']}")
|
|
271
|
+
print(f" └─ {os.path.relpath(route['file'])}:{route['line']}")
|
|
272
|
+
print(f"\n{'─' * 60}")
|
|
273
|
+
print(f"\nAdd these to {os.path.relpath(manifest_path)}:")
|
|
274
|
+
print(
|
|
275
|
+
' { "method": "<METHOD>", "path": "<path>", "tier": 1, "specs": [] }\n'
|
|
276
|
+
)
|
|
277
|
+
print("Endpoints with specs: [] will pass CI but show as uncovered")
|
|
278
|
+
print("in the E2E PR comment.\n")
|
|
279
|
+
|
|
280
|
+
if args.strict:
|
|
281
|
+
print("🚫 Failing CI (strict mode). Use --no-strict to warn only.")
|
|
282
|
+
sys.exit(1)
|
|
283
|
+
else:
|
|
284
|
+
print("⚠️ Warning only (non-strict mode).")
|
|
285
|
+
else:
|
|
286
|
+
print("✅ All API routes are accounted for in the manifest.")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
if __name__ == "__main__":
|
|
290
|
+
main()
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
detect-routes-nextjs.mjs
|
|
5
|
+
|
|
6
|
+
Detect all Next.js App Router routes and compare against route manifest.
|
|
7
|
+
Fails CI if any route exists in the app but not in the manifest.
|
|
8
|
+
|
|
9
|
+
Scans for page routes (page.tsx) and API routes (route.ts).
|
|
10
|
+
|
|
11
|
+
Handles Next.js conventions:
|
|
12
|
+
Route groups (auth)/ -- stripped from path
|
|
13
|
+
Dynamic segments [id]/ -- preserved
|
|
14
|
+
Catch-all [...slug]/ -- preserved
|
|
15
|
+
Parallel routes @panel/ -- skipped
|
|
16
|
+
Intercepting routes (.)/ -- skipped
|
|
17
|
+
Private folders _components/ -- skipped
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
node detect-routes-nextjs.mjs [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--app-dir <path> Path to app/ directory (default: src/app or app)
|
|
24
|
+
--manifest <path> Path to route-manifest.json (default: e2e/route-manifest.json)
|
|
25
|
+
--strict Exit 1 if any route is missing (default: true)
|
|
26
|
+
--no-strict Only warn, don't fail
|
|
27
|
+
|
|
28
|
+
Exit codes:
|
|
29
|
+
0 -- all routes accounted for
|
|
30
|
+
1 -- routes missing from manifest (strict mode)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readdirSync, existsSync, readFileSync, statSync } from 'fs';
|
|
34
|
+
import { join, resolve, relative, sep } from 'path';
|
|
35
|
+
|
|
36
|
+
// ─── CLI args ───────────────────────────────────────────────
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
function getArg(name, defaultVal) {
|
|
39
|
+
const idx = args.indexOf(`--${name}`);
|
|
40
|
+
return idx >= 0 && args[idx + 1] ? args[idx + 1] : defaultVal;
|
|
41
|
+
}
|
|
42
|
+
const hasFlag = (name) => args.includes(`--${name}`);
|
|
43
|
+
const strict = !hasFlag('no-strict');
|
|
44
|
+
|
|
45
|
+
// Auto-detect app directory
|
|
46
|
+
function findAppDir() {
|
|
47
|
+
const explicit = getArg('app-dir', '');
|
|
48
|
+
if (explicit) return resolve(explicit);
|
|
49
|
+
if (existsSync('src/app')) return resolve('src/app');
|
|
50
|
+
if (existsSync('app')) return resolve('app');
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const appDir = findAppDir();
|
|
55
|
+
const manifestPath = resolve(getArg('manifest', 'e2e/route-manifest.json'));
|
|
56
|
+
|
|
57
|
+
if (!appDir || !existsSync(appDir)) {
|
|
58
|
+
console.error(`❌ App directory not found. Use --app-dir <path>`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── Route scanning ─────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Recursively scan the app directory for page.tsx/ts and route.ts/tsx files.
|
|
66
|
+
* Returns an array of { path: string, type: 'page' | 'api', file: string }
|
|
67
|
+
*/
|
|
68
|
+
function scanRoutes(dir, segments = []) {
|
|
69
|
+
const routes = [];
|
|
70
|
+
let entries;
|
|
71
|
+
try {
|
|
72
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
73
|
+
} catch {
|
|
74
|
+
return routes;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check for route files in current directory
|
|
78
|
+
const pageFile = entries.find(
|
|
79
|
+
(e) => e.isFile() && /^page\.(tsx?|jsx?)$/.test(e.name)
|
|
80
|
+
);
|
|
81
|
+
const routeFile = entries.find(
|
|
82
|
+
(e) => e.isFile() && /^route\.(tsx?|jsx?)$/.test(e.name)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (pageFile) {
|
|
86
|
+
const routePath = '/' + segments.join('/') || '/';
|
|
87
|
+
routes.push({
|
|
88
|
+
path: routePath === '/' ? '/' : routePath,
|
|
89
|
+
type: 'page',
|
|
90
|
+
file: relative(process.cwd(), join(dir, pageFile.name)),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (routeFile) {
|
|
95
|
+
const routePath = '/' + segments.join('/') || '/';
|
|
96
|
+
routes.push({
|
|
97
|
+
path: routePath,
|
|
98
|
+
type: 'api',
|
|
99
|
+
file: relative(process.cwd(), join(dir, routeFile.name)),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Recurse into subdirectories
|
|
104
|
+
for (const entry of entries) {
|
|
105
|
+
if (!entry.isDirectory()) continue;
|
|
106
|
+
const name = entry.name;
|
|
107
|
+
|
|
108
|
+
// Skip private folders (Next.js convention: _name)
|
|
109
|
+
if (name.startsWith('_')) continue;
|
|
110
|
+
|
|
111
|
+
// Skip parallel routes (@panel, @modal — not independent routes)
|
|
112
|
+
if (name.startsWith('@')) continue;
|
|
113
|
+
|
|
114
|
+
// Skip intercepting routes ((.), (..), (..)(..))
|
|
115
|
+
if (/^\(\.+\)/.test(name)) continue;
|
|
116
|
+
|
|
117
|
+
// Route groups: (groupName) => strip from path
|
|
118
|
+
if (/^\([^)]+\)$/.test(name) && !name.startsWith('(.)')) {
|
|
119
|
+
routes.push(...scanRoutes(join(dir, name), segments));
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Normal segment (including [dynamic], [...catchAll], [[...optional]])
|
|
124
|
+
routes.push(...scanRoutes(join(dir, name), [...segments, name]));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return routes;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Manifest comparison ────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
// Load manifest
|
|
133
|
+
let manifest = { routes: [] };
|
|
134
|
+
if (existsSync(manifestPath)) {
|
|
135
|
+
try {
|
|
136
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error(`❌ Invalid JSON in manifest: ${e.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
console.error(`❌ Manifest not found: ${manifestPath}`);
|
|
143
|
+
console.error(` Create it with at minimum: { "version": 1, "routes": [] }`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const manifestRoutes = new Set(
|
|
148
|
+
(manifest.routes || []).map((r) => r.path)
|
|
149
|
+
);
|
|
150
|
+
// Also check endpoints (for API routes in combined manifests)
|
|
151
|
+
const manifestEndpoints = new Set(
|
|
152
|
+
(manifest.endpoints || []).map((r) => r.path)
|
|
153
|
+
);
|
|
154
|
+
const allManifestPaths = new Set([...manifestRoutes, ...manifestEndpoints]);
|
|
155
|
+
|
|
156
|
+
// Scan
|
|
157
|
+
console.log(`🔍 Scanning: ${relative(process.cwd(), appDir)}/`);
|
|
158
|
+
console.log(`📋 Manifest: ${relative(process.cwd(), manifestPath)}\n`);
|
|
159
|
+
|
|
160
|
+
const appRoutes = scanRoutes(appDir);
|
|
161
|
+
const missing = [];
|
|
162
|
+
const accounted = [];
|
|
163
|
+
|
|
164
|
+
for (const route of appRoutes) {
|
|
165
|
+
if (allManifestPaths.has(route.path)) {
|
|
166
|
+
accounted.push(route);
|
|
167
|
+
} else {
|
|
168
|
+
missing.push(route);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ─── Report ─────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
console.log(`Found ${appRoutes.length} routes in app (${appRoutes.filter(r => r.type === 'page').length} pages, ${appRoutes.filter(r => r.type === 'api').length} API routes)`);
|
|
175
|
+
console.log(`Manifest has ${allManifestPaths.size} entries`);
|
|
176
|
+
console.log(`Accounted: ${accounted.length} | Missing: ${missing.length}\n`);
|
|
177
|
+
|
|
178
|
+
if (missing.length > 0) {
|
|
179
|
+
console.log(`${'─'.repeat(60)}`);
|
|
180
|
+
console.log(`❌ Routes in app but NOT in manifest:\n`);
|
|
181
|
+
for (const route of missing) {
|
|
182
|
+
const tag = route.type === 'api' ? ' [API]' : '';
|
|
183
|
+
console.log(` ${route.path}${tag}`);
|
|
184
|
+
console.log(` └─ ${route.file}`);
|
|
185
|
+
}
|
|
186
|
+
console.log(`\n${'─'.repeat(60)}`);
|
|
187
|
+
console.log(`\nAdd these to ${relative(process.cwd(), manifestPath)}:`);
|
|
188
|
+
console.log(` { "path": "<route>", "tier": 1, "specs": [] }\n`);
|
|
189
|
+
console.log(`Routes with specs: [] will pass CI but show as uncovered`);
|
|
190
|
+
console.log(`in the E2E PR comment.\n`);
|
|
191
|
+
|
|
192
|
+
if (strict) {
|
|
193
|
+
console.log('🚫 Failing CI (strict mode). Use --no-strict to warn only.');
|
|
194
|
+
process.exit(1);
|
|
195
|
+
} else {
|
|
196
|
+
console.log('⚠️ Warning only (non-strict mode).');
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
console.log('✅ All app routes are accounted for in the manifest.');
|
|
200
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Evidence-posting step for `presentation:visual` (runs after `visual-baseline-regen`).
|
|
2
|
+
//
|
|
3
|
+
// Closes the visual evidence loop: after baselines are regenerated, persist the
|
|
4
|
+
// rendered evidence back to the work item — MECHANIZED, not a prose reminder (a
|
|
5
|
+
// described step degrades to a lookalike that silently no-ops).
|
|
6
|
+
//
|
|
7
|
+
// It UPSERTS a single marker-tagged comment (never appends) to BOTH the source
|
|
8
|
+
// issue and the PR, carrying:
|
|
9
|
+
// - the DURABLE committed-baseline link at the regenerated SHA (canonical —
|
|
10
|
+
// survives artifact expiry), and
|
|
11
|
+
// - the EPHEMERAL diff/preview artifact URL with its expiry noted.
|
|
12
|
+
// The same two URLs are RETURNED as a value (`--evidence-out <file>`, and always on
|
|
13
|
+
// stdout behind the `visual-evidence:` prefix) for the resolve-issues orchestrator to
|
|
14
|
+
// record as the unit's visual proof-of-execution. This step runs inside a spawn/CI
|
|
15
|
+
// job, and a spawn never writes the run manifest — the orchestrator is its only
|
|
16
|
+
// writer (ADR 0001 decision 5, issue #579). It used to shell out to the orchestrator's
|
|
17
|
+
// manifest CLI from here — the read-modify-write nothing guards once producers stop
|
|
18
|
+
// being serialized.
|
|
19
|
+
//
|
|
20
|
+
// Idempotent upsert is what keeps CI re-runs from spamming the work item — the
|
|
21
|
+
// marker `<!-- visual-evidence:<unit> -->` is matched and the comment edited in
|
|
22
|
+
// place. Posting a comment is outward-facing, but it is a CI bot writing an
|
|
23
|
+
// evidence comment this lane explicitly defines.
|
|
24
|
+
//
|
|
25
|
+
// Contract: ensure-coverage references/enforcement/ci-contract.md → "Evidence-
|
|
26
|
+
// posting step (after baseline-regen)".
|
|
27
|
+
|
|
28
|
+
import fs from 'node:fs'
|
|
29
|
+
import { execFileSync } from 'node:child_process'
|
|
30
|
+
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
31
|
+
|
|
32
|
+
// ---- pure, unit-testable core (no gh / no network) ----
|
|
33
|
+
|
|
34
|
+
export const MARKER = (unit) => `<!-- visual-evidence:${unit} -->`
|
|
35
|
+
|
|
36
|
+
// Build the durable, SHA-pinned link to a committed baseline PNG. Survives artifact
|
|
37
|
+
// expiry because it points at the file in git, not a CI artifact store.
|
|
38
|
+
export function durableBaselineLink({ repo, sha, path }) {
|
|
39
|
+
return `https://github.com/${repo}/blob/${sha}/${path}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Render the evidence comment body. Durable link is canonical (always present);
|
|
43
|
+
// the diff artifact is ephemeral and annotated with its expiry. `cells` is the
|
|
44
|
+
// list of regenerated (route, viewport, baselinePath) plus optional diffArtifact.
|
|
45
|
+
export function renderComment({ unit, repo, sha, cells, diffArtifact, diffExpiry }) {
|
|
46
|
+
const lines = [MARKER(unit), '', '### 🖼️ Visual baselines', '']
|
|
47
|
+
lines.push('**Durable** (committed at the regenerated SHA — survives artifact expiry):', '')
|
|
48
|
+
for (const c of cells) {
|
|
49
|
+
lines.push(`- \`${c.route}\` @${c.viewport} — [baseline](${durableBaselineLink({ repo, sha, path: c.baselinePath })})`)
|
|
50
|
+
}
|
|
51
|
+
if (diffArtifact) {
|
|
52
|
+
lines.push('', `**Preview** (ephemeral diff/render artifact — expires ${diffExpiry || 'per CI retention'}): ${diffArtifact}`)
|
|
53
|
+
}
|
|
54
|
+
lines.push('', '_A baseline change is reviewable evidence — it must correspond to a declared intended UI change, not a silent `--update-snapshots` to green._')
|
|
55
|
+
return lines.join('\n')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The two URLs this step RETURNS, for the orchestrator to record as the unit's
|
|
59
|
+
// visual proof-of-execution. Named for the return, not the manifest: nothing in a
|
|
60
|
+
// leaf skill may write the manifest, and a helper called `manifestEvidence` invites
|
|
61
|
+
// exactly the call that was removed here.
|
|
62
|
+
export function returnedEvidence({ unit, repo, sha, cells, diffArtifact }) {
|
|
63
|
+
return {
|
|
64
|
+
unit,
|
|
65
|
+
visual: {
|
|
66
|
+
durableBaselines: cells.map((c) => ({
|
|
67
|
+
route: c.route, viewport: c.viewport,
|
|
68
|
+
url: durableBaselineLink({ repo, sha, path: c.baselinePath }),
|
|
69
|
+
})),
|
|
70
|
+
diffArtifact: diffArtifact || null,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Decide upsert vs create from the existing comments on a target. Returns the id
|
|
76
|
+
// to edit, or null to create. `comments` is [{ id, body }].
|
|
77
|
+
export function findUpsertTarget(comments, unit) {
|
|
78
|
+
const marker = MARKER(unit)
|
|
79
|
+
const hit = (comments || []).find((c) => (c.body || '').includes(marker))
|
|
80
|
+
return hit ? hit.id : null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Pure: extract the visual (route, viewport, baselinePath) cells from a
|
|
84
|
+
// presentation-results.json the sweep emitted (runner-contract.md result shape).
|
|
85
|
+
// Only `presentation:visual` rows carry a `baseline`; the floor levels don't.
|
|
86
|
+
export function cellsFromResults(results) {
|
|
87
|
+
const rows = (results && Array.isArray(results.results)) ? results.results : []
|
|
88
|
+
return rows
|
|
89
|
+
.filter((r) => r.level === 'presentation:visual' && r.baseline)
|
|
90
|
+
.map((r) => ({ route: r.route, viewport: r.viewport, baselinePath: r.baseline }))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Pure: parse argv into the options the CLI needs. Kept pure so it's tested.
|
|
94
|
+
export function parseArgs(argv) {
|
|
95
|
+
const o = {}
|
|
96
|
+
for (let i = 0; i < argv.length; i++) {
|
|
97
|
+
const a = argv[i]
|
|
98
|
+
if (a.startsWith('--')) o[a.slice(2)] = (argv[i + 1] && !argv[i + 1].startsWith('--')) ? argv[++i] : 'true'
|
|
99
|
+
}
|
|
100
|
+
return o
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---- gh + fs wrapper (thin; the pure core above is what doctrine pins) ----
|
|
104
|
+
|
|
105
|
+
function gh(args) {
|
|
106
|
+
return execFileSync('gh', args, { encoding: 'utf8' })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Upsert the marker comment on one target (issue or PR — both are "issues" to gh).
|
|
110
|
+
// Edits in place when the marker already exists (idempotent across CI re-runs),
|
|
111
|
+
// else creates. Listing is via `gh api` so we can match the hidden marker.
|
|
112
|
+
function upsertComment(repo, number, unit, body) {
|
|
113
|
+
let existing = []
|
|
114
|
+
try {
|
|
115
|
+
existing = JSON.parse(gh(['api', `repos/${repo}/issues/${number}/comments`, '--paginate',
|
|
116
|
+
'--jq', '[.[] | {id: .id, body: .body}]']))
|
|
117
|
+
} catch { existing = [] }
|
|
118
|
+
const id = findUpsertTarget(existing, unit)
|
|
119
|
+
if (id) gh(['api', '--method', 'PATCH', `repos/${repo}/issues/comments/${id}`, '-f', `body=${body}`])
|
|
120
|
+
else gh(['issue', 'comment', String(number), '--repo', repo, '--body', body])
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function main() {
|
|
124
|
+
const o = parseArgs(process.argv.slice(2))
|
|
125
|
+
const required = ['unit', 'repo', 'sha', 'results']
|
|
126
|
+
const missing = required.filter((k) => !o[k])
|
|
127
|
+
if (missing.length) { console.error(`post-visual-evidence: missing --${missing.join(', --')}`); process.exit(2) }
|
|
128
|
+
|
|
129
|
+
const results = JSON.parse(fs.readFileSync(o.results, 'utf8'))
|
|
130
|
+
const cells = cellsFromResults(results)
|
|
131
|
+
|
|
132
|
+
// Zero cells is a real answer ("this unit has no `presentation:visual` surface"), not
|
|
133
|
+
// an absence of one — so it still travels the return channel. Bailing before the
|
|
134
|
+
// marker and the file made the two indistinguishable to the orchestrator, and left
|
|
135
|
+
// the lane's `if: always()` artifact upload warning on a path that had simply
|
|
136
|
+
// finished. Only the outward-facing comment is skipped: there is nothing to say.
|
|
137
|
+
if (cells.length > 0) {
|
|
138
|
+
const body = renderComment({
|
|
139
|
+
unit: o.unit, repo: o.repo, sha: o.sha, cells,
|
|
140
|
+
diffArtifact: o['diff-artifact'], diffExpiry: o['diff-expiry'],
|
|
141
|
+
})
|
|
142
|
+
for (const target of [o.issue, o.pr].filter(Boolean)) upsertComment(o.repo, target, o.unit, body)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Return the same URLs as a value. The orchestrator records them on the unit; this
|
|
146
|
+
// step does not touch the run manifest, because the orchestrator is its only writer
|
|
147
|
+
// (ADR 0001 decision 5). Always on stdout (so a spawn can quote it back in its
|
|
148
|
+
// result), and to a file when the caller wants it as a CI artifact.
|
|
149
|
+
const evidence = returnedEvidence({ unit: o.unit, repo: o.repo, sha: o.sha, cells, diffArtifact: o['diff-artifact'] })
|
|
150
|
+
console.log(`visual-evidence: ${JSON.stringify(evidence)}`)
|
|
151
|
+
if (o['evidence-out']) fs.writeFileSync(o['evidence-out'], `${JSON.stringify(evidence, null, 2)}\n`)
|
|
152
|
+
|
|
153
|
+
console.log(cells.length === 0
|
|
154
|
+
? 'post-visual-evidence: no presentation:visual cells in results — nothing to comment; returned an empty evidence value'
|
|
155
|
+
: `post-visual-evidence: upserted visual evidence for ${cells.length} cell(s)`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (isMainModule(import.meta.url)) main()
|