@getarbiter/cli 0.5.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/CHANGELOG.md +713 -0
- package/LICENSE +202 -0
- package/NOTICE +31 -0
- package/PRIVACY.md +111 -0
- package/README.md +237 -0
- package/THIRD_PARTY_LICENSES.md +472 -0
- package/dist/.src-manifest.json +1 -0
- package/dist/capabilities/host-probe.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +87 -0
- package/dist/commands/adopt-policy.js +3 -0
- package/dist/commands/configure-interactive.js +3 -0
- package/dist/commands/configure.js +5 -0
- package/dist/commands/cross-model-review.js +3 -0
- package/dist/commands/diff.js +20 -0
- package/dist/commands/doc-set.js +3 -0
- package/dist/commands/doctor/clean.js +2 -0
- package/dist/commands/doctor/fail-open-census.js +12 -0
- package/dist/commands/doctor/health.js +11 -0
- package/dist/commands/doctor/prove-gates.js +13 -0
- package/dist/commands/doctor/recover-lock.js +12 -0
- package/dist/commands/doctor/repair-state.js +6 -0
- package/dist/commands/doctor/tool-pin-extract.js +4 -0
- package/dist/commands/doctor/tool-pins.js +9 -0
- package/dist/commands/doctor-interactive.js +2 -0
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/explain.js +19 -0
- package/dist/commands/gate-exec.js +4 -0
- package/dist/commands/gold-audit.js +14 -0
- package/dist/commands/graph.js +5 -0
- package/dist/commands/init/build-arbiter-config.js +1 -0
- package/dist/commands/init/generate.js +42 -0
- package/dist/commands/init/github-setup.js +5 -0
- package/dist/commands/init/maturity-gates.js +10 -0
- package/dist/commands/init/resolve-config.js +3 -0
- package/dist/commands/init/toolchain.js +10 -0
- package/dist/commands/init/types.js +1 -0
- package/dist/commands/init.js +17 -0
- package/dist/commands/method-interactive.js +4 -0
- package/dist/commands/method.js +11 -0
- package/dist/commands/obsidian.js +2 -0
- package/dist/commands/plugin.js +8 -0
- package/dist/commands/pr-merged.js +2 -0
- package/dist/commands/retire-policy.js +3 -0
- package/dist/commands/review-diff.js +3 -0
- package/dist/commands/settings.js +7 -0
- package/dist/commands/ship-config.js +2 -0
- package/dist/commands/ship-profile.js +2 -0
- package/dist/commands/ship-review.js +1 -0
- package/dist/commands/ship-tier.js +2 -0
- package/dist/commands/ship-train.js +2 -0
- package/dist/commands/task-mark.js +2 -0
- package/dist/commands/task-note.js +3 -0
- package/dist/commands/task-record-red.js +2 -0
- package/dist/commands/task-record-tech-debt.js +4 -0
- package/dist/commands/task-ship.js +3 -0
- package/dist/commands/task-state.js +6 -0
- package/dist/commands/task.js +59 -0
- package/dist/commands/update.js +60 -0
- package/dist/commands/upgrade-level-interactive.js +2 -0
- package/dist/commands/upgrade-level.js +7 -0
- package/dist/commands/verify-plan.js +8 -0
- package/dist/commands/verify-tdd.js +2 -0
- package/dist/commands/verify.js +7 -0
- package/dist/commands/wizard/coherence.js +1 -0
- package/dist/commands/worktree-prune.js +4 -0
- package/dist/commands/worktree.js +39 -0
- package/dist/compatibility/cross-language-matrix.json +540 -0
- package/dist/compatibility/index.d.ts +4 -0
- package/dist/compatibility/index.js +2 -0
- package/dist/compatibility/matcher.js +1 -0
- package/dist/compatibility/matrix.json +27 -0
- package/dist/compatibility/parsers.d.ts +38 -0
- package/dist/compatibility/parsers.js +1 -0
- package/dist/compatibility/probe.d.ts +41 -0
- package/dist/compatibility/probe.js +4 -0
- package/dist/compatibility/report.js +2 -0
- package/dist/compatibility/schema.d.ts +80 -0
- package/dist/compatibility/schema.js +1 -0
- package/dist/compatibility/skills-matrix.json +98 -0
- package/dist/compatibility/skills-validator.d.ts +30 -0
- package/dist/compatibility/skills-validator.js +4 -0
- package/dist/compatibility/tooling-proposals.json +5 -0
- package/dist/config/arbiter-ignore.js +3 -0
- package/dist/config/collaboration-mode-defaults.js +1 -0
- package/dist/config/diff.js +1 -0
- package/dist/config/env-overrides.js +3 -0
- package/dist/config/env-registry.js +2 -0
- package/dist/config/levels.js +1 -0
- package/dist/config/migrations/index.js +2 -0
- package/dist/config/migrations/v0-to-v1.js +2 -0
- package/dist/config/migrations/v1-to-v2.js +2 -0
- package/dist/config/migrations/v2-to-v3.js +2 -0
- package/dist/config/migrations/v3-to-v4.js +2 -0
- package/dist/config/override-resolver.js +2 -0
- package/dist/config/resolve-project-config.js +2 -0
- package/dist/config/resolve-project-name.js +2 -0
- package/dist/config/schema.d.ts +480 -0
- package/dist/config/schema.js +1 -0
- package/dist/config/test-pyramid-profiles.js +1 -0
- package/dist/config/thresholds.js +2 -0
- package/dist/conformance/dimensions.js +3 -0
- package/dist/conformance/engine.js +7 -0
- package/dist/conformance/gate-proofs.js +4 -0
- package/dist/conformance/shared.js +2 -0
- package/dist/detectors/axis.js +1 -0
- package/dist/detectors/build.js +2 -0
- package/dist/detectors/existing.js +2 -0
- package/dist/detectors/external-model.js +2 -0
- package/dist/detectors/framework.js +2 -0
- package/dist/detectors/git.js +11 -0
- package/dist/detectors/github.js +2 -0
- package/dist/detectors/lanes.js +5 -0
- package/dist/detectors/language-hooks.js +117 -0
- package/dist/detectors/language.js +2 -0
- package/dist/detectors/package-manager.js +2 -0
- package/dist/detectors/package.js +2 -0
- package/dist/detectors/scaffold-wiring.js +2 -0
- package/dist/detectors/workflow-collision.js +4 -0
- package/dist/evidence/gate-binding.js +4 -0
- package/dist/evidence/git-checks.js +3 -0
- package/dist/evidence/load.js +2 -0
- package/dist/evidence/provenance.js +2 -0
- package/dist/evidence/summary.js +2 -0
- package/dist/evidence/tdd-reexecute.js +2 -0
- package/dist/evidence/tdd.js +4 -0
- package/dist/experimental/index.js +2 -0
- package/dist/experimental/parse-argv.js +2 -0
- package/dist/experimental/registry.js +2 -0
- package/dist/generators/agents-claude.js +2 -0
- package/dist/generators/agents-md.js +2 -0
- package/dist/generators/anti-drift-validators.js +3 -0
- package/dist/generators/anti-proforma.js +2 -0
- package/dist/generators/api-e2e.js +2 -0
- package/dist/generators/api-middleware.js +2 -0
- package/dist/generators/archunit.js +2 -0
- package/dist/generators/audit-toolchain.js +2 -0
- package/dist/generators/auth.js +2 -0
- package/dist/generators/behavioral-tests.js +2 -0
- package/dist/generators/boundaries.js +2 -0
- package/dist/generators/check-all.js +2 -0
- package/dist/generators/check-stack-conformity.js +2 -0
- package/dist/generators/ci-five-lane.js +2 -0
- package/dist/generators/ci-tier.js +2 -0
- package/dist/generators/claude.js +3 -0
- package/dist/generators/codex-hooks.js +2 -0
- package/dist/generators/codex-known-limitations.js +2 -0
- package/dist/generators/codex.js +15 -0
- package/dist/generators/commit-footer.js +2 -0
- package/dist/generators/compliance-menu.js +2 -0
- package/dist/generators/compliance.js +2 -0
- package/dist/generators/conformance.js +2 -0
- package/dist/generators/contract-testing.js +2 -0
- package/dist/generators/coverage.js +2 -0
- package/dist/generators/debt-gates.js +24 -0
- package/dist/generators/debt-ratchet.js +2 -0
- package/dist/generators/derived-class.js +2 -0
- package/dist/generators/doc-set.js +4 -0
- package/dist/generators/docs.js +2 -0
- package/dist/generators/duplication.js +2 -0
- package/dist/generators/e2e-constitution.js +2 -0
- package/dist/generators/env-template.js +2 -0
- package/dist/generators/evidence-retention.js +2 -0
- package/dist/generators/feature-matrix.js +2 -0
- package/dist/generators/frontend-governance.js +2 -0
- package/dist/generators/frontend-quality.js +2 -0
- package/dist/generators/gap.js +2 -0
- package/dist/generators/gdpr.js +2 -0
- package/dist/generators/githooks.js +2 -0
- package/dist/generators/github-setup.js +2 -0
- package/dist/generators/github.js +2 -0
- package/dist/generators/gitignore.js +2 -0
- package/dist/generators/global-invariants.js +3 -0
- package/dist/generators/go-boundaries.js +2 -0
- package/dist/generators/gold-kit.js +2 -0
- package/dist/generators/infra.js +2 -0
- package/dist/generators/integration-testing.js +7 -0
- package/dist/generators/iso27001.js +2 -0
- package/dist/generators/iso9001.js +2 -0
- package/dist/generators/labels.js +2 -0
- package/dist/generators/local-wrapper.js +2 -0
- package/dist/generators/modulith.js +2 -0
- package/dist/generators/mutation.js +2 -0
- package/dist/generators/observability.js +2 -0
- package/dist/generators/operations.js +2 -0
- package/dist/generators/perf-k6.js +2 -0
- package/dist/generators/pharma.js +2 -0
- package/dist/generators/playwright-python.js +2 -0
- package/dist/generators/playwright-ts.js +2 -0
- package/dist/generators/pr-tooling.js +2 -0
- package/dist/generators/python-boundaries.js +2 -0
- package/dist/generators/quality.js +2 -0
- package/dist/generators/registry.js +2 -0
- package/dist/generators/regulated.js +2 -0
- package/dist/generators/resilience.js +2 -0
- package/dist/generators/risk-register.js +2 -0
- package/dist/generators/root.js +2 -0
- package/dist/generators/rust-boundaries.js +2 -0
- package/dist/generators/safety-class.js +2 -0
- package/dist/generators/security.js +2 -0
- package/dist/generators/seed.js +2 -0
- package/dist/generators/self-validation.js +2 -0
- package/dist/generators/ship-driver.js +2 -0
- package/dist/generators/skill-names.json +17 -0
- package/dist/generators/skills.js +2 -0
- package/dist/generators/smoke-journeys.js +2 -0
- package/dist/generators/solo-exception.js +2 -0
- package/dist/generators/ssot.js +2 -0
- package/dist/generators/stride-enforcement.js +2 -0
- package/dist/generators/suppressions.js +2 -0
- package/dist/generators/test-pyramid-manifest.js +3 -0
- package/dist/generators/test-taxonomy.js +2 -0
- package/dist/generators/wiki.js +2 -0
- package/dist/github/branch-protection.js +2 -0
- package/dist/github/classify-gh-error.js +2 -0
- package/dist/github/labels.js +2 -0
- package/dist/github/project-board.js +2 -0
- package/dist/graph/builders/adr.js +4 -0
- package/dist/graph/builders/ast.js +3 -0
- package/dist/graph/builders/canon.js +2 -0
- package/dist/graph/builders/evidence.js +2 -0
- package/dist/graph/builders/inv.js +1 -0
- package/dist/graph/builders/req.js +3 -0
- package/dist/graph/builders/test-nodes.js +3 -0
- package/dist/graph/builders/utils.js +2 -0
- package/dist/graph/load.js +2 -0
- package/dist/graph/model.js +2 -0
- package/dist/graph/query.js +3 -0
- package/dist/graph/store.js +1 -0
- package/dist/i18n/en.json +293 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/types.js +2 -0
- package/dist/integrations/companions.js +2 -0
- package/dist/integrations/external-review.js +10 -0
- package/dist/integrations/plugin-schema.js +2 -0
- package/dist/integrations/skill-detector.js +7 -0
- package/dist/integrations/skills-matrix.js +2 -0
- package/dist/integrations/types.d.ts +23 -0
- package/dist/integrations/types.js +2 -0
- package/dist/internal/cli-deprecation-registry.js +2 -0
- package/dist/internal/deprecate.js +3 -0
- package/dist/internal/experimental-warn.js +3 -0
- package/dist/invariants/catalog.d.ts +2 -0
- package/dist/invariants/catalog.js +1 -0
- package/dist/invariants/filter.d.ts +28 -0
- package/dist/invariants/filter.js +1 -0
- package/dist/invariants/index.d.ts +4 -0
- package/dist/invariants/index.js +2 -0
- package/dist/invariants/tiers.js +1 -0
- package/dist/invariants/types.d.ts +1 -0
- package/dist/invariants/types.js +1 -0
- package/dist/kit/applicability.js +3 -0
- package/dist/kit/audit-report.js +3 -0
- package/dist/kit/brownfield-detect.js +4 -0
- package/dist/kit/canonical-mapping.json +1 -0
- package/dist/kit/catalog.js +2 -0
- package/dist/kit/catalog.json +1 -0
- package/dist/kit/derived.json +1 -0
- package/dist/kit/emit-issues.js +8 -0
- package/dist/kit/measure.js +5 -0
- package/dist/kit/redaction.js +3 -0
- package/dist/kit/schema.js +2 -0
- package/dist/kit/taxonomy.js +2 -0
- package/dist/kit/thresholds.d.ts +12 -0
- package/dist/kit/thresholds.js +2 -0
- package/dist/kit/wave-engine.js +1 -0
- package/dist/recipes/loader.js +6 -0
- package/dist/recipes/schema.js +2 -0
- package/dist/risk/classifier.js +2 -0
- package/dist/risk/sha-check.js +2 -0
- package/dist/state/backups.js +2 -0
- package/dist/state/envelope.js +2 -0
- package/dist/state/generated-manifest.js +3 -0
- package/dist/state/manifest-key.js +2 -0
- package/dist/state/migrations/index.js +1 -0
- package/dist/state/migrations/v0-to-v1.js +2 -0
- package/dist/state/retired-renders.js +2 -0
- package/dist/templates/.nvmrc.ejs +1 -0
- package/dist/templates/HANDOFF.template.md +45 -0
- package/dist/templates/__tests__/contract/error-shape.contract.test.ts.ejs +47 -0
- package/dist/templates/agents-md/AGENTS.md.ejs +517 -0
- package/dist/templates/api-e2e/README.md.ejs +33 -0
- package/dist/templates/api-e2e/go-httptest.go.ejs +43 -0
- package/dist/templates/api-e2e/java-restassured.java.ejs +28 -0
- package/dist/templates/api-e2e/kotlin-restassured.kt.ejs +30 -0
- package/dist/templates/api-e2e/manifest.json.ejs +8 -0
- package/dist/templates/api-e2e/postman.collection.json.ejs +30 -0
- package/dist/templates/api-e2e/python-httpx.py.ejs +17 -0
- package/dist/templates/api-e2e/run.sh.ejs +61 -0
- package/dist/templates/api-e2e/ts-supertest.test.ts.ejs +18 -0
- package/dist/templates/archunit/AntiCyclicTest.java.ejs +29 -0
- package/dist/templates/archunit/AntiProformaExempt.java.ejs +35 -0
- package/dist/templates/archunit/AntiProformaTest.java.ejs +104 -0
- package/dist/templates/archunit/ArchitectureTest.java.ejs +87 -0
- package/dist/templates/archunit/DependencyFlowTest.java.ejs +55 -0
- package/dist/templates/archunit/DomainPurityTest.java.ejs +47 -0
- package/dist/templates/archunit/NamingConventionsTest.java.ejs +54 -0
- package/dist/templates/archunit/NoH2ArchTest.java.ejs +30 -0
- package/dist/templates/archunit/NoMockMvcTest.java.ejs +71 -0
- package/dist/templates/archunit/PortsIndependenceTest.java.ejs +55 -0
- package/dist/templates/archunit/RestAssuredArchTest.java.ejs +32 -0
- package/dist/templates/archunit/RestAssuredBaseIT.java.ejs +66 -0
- package/dist/templates/archunit/TestCoverageArchTest.java.ejs +29 -0
- package/dist/templates/archunit/arch-test-deps-maven.md.ejs +42 -0
- package/dist/templates/archunit/arch-test-deps.gradle.ejs +14 -0
- package/dist/templates/audit/gdpr/check-gdpr-controls.mjs.ejs +102 -0
- package/dist/templates/audit/gdpr/controls-to-gates.md.ejs +42 -0
- package/dist/templates/audit/gdpr/data-flow-map.md.ejs +30 -0
- package/dist/templates/audit/gdpr/dpia.md.ejs +45 -0
- package/dist/templates/audit/generic/audit-gate-rules.md.ejs +24 -0
- package/dist/templates/audit/generic/audit-trail-policy.md.ejs +38 -0
- package/dist/templates/audit/iso27001/iso27001-controls-gate-map.md.ejs +60 -0
- package/dist/templates/auth/setup.md.ejs +661 -0
- package/dist/templates/behavioral-tests/ExampleBehavioralTest.java.ejs +67 -0
- package/dist/templates/behavioral-tests/TESTING_POLICY.md.ejs +116 -0
- package/dist/templates/behavioral-tests/bdd/ExampleBddIT.java.ejs +19 -0
- package/dist/templates/behavioral-tests/bdd/ExampleSteps.java.ejs +61 -0
- package/dist/templates/behavioral-tests/bdd/example.feature.ejs +14 -0
- package/dist/templates/behavioral-tests/bdd/example.steps.ts.ejs +37 -0
- package/dist/templates/behavioral-tests/bdd/example_bdd_test.rs.ejs +64 -0
- package/dist/templates/behavioral-tests/bdd/example_test.go.ejs +80 -0
- package/dist/templates/behavioral-tests/bdd/test_example_bdd.py.ejs +47 -0
- package/dist/templates/behavioral-tests/eslint-playwright.json.ejs +10 -0
- package/dist/templates/behavioral-tests/eslint.config.playwright.mjs.ejs +29 -0
- package/dist/templates/behavioral-tests/example.behavioral.test.ts.ejs +47 -0
- package/dist/templates/behavioral-tests/example_behavioral_test.go.ejs +51 -0
- package/dist/templates/behavioral-tests/example_behavioral_test.rs.ejs +47 -0
- package/dist/templates/behavioral-tests/test_example_behavioral.py.ejs +40 -0
- package/dist/templates/boundaries/.eslintrc-boundaries.cjs.ejs +81 -0
- package/dist/templates/boundaries/.eslintrc-frontend-spa.cjs.ejs +79 -0
- package/dist/templates/boundaries/.importlinter.ejs +48 -0
- package/dist/templates/boundaries/check-boundaries-go.mjs.ejs +94 -0
- package/dist/templates/boundaries/check-boundaries-python.mjs.ejs +118 -0
- package/dist/templates/boundaries/check-boundaries-rust.mjs.ejs +111 -0
- package/dist/templates/boundaries/check-boundaries.mjs.ejs +34 -0
- package/dist/templates/boundaries/clippy.toml.ejs +50 -0
- package/dist/templates/boundaries/deny.toml.ejs +56 -0
- package/dist/templates/boundaries/eslint.config.boundaries.mjs.ejs +112 -0
- package/dist/templates/boundaries/eslint.config.frontend-spa.mjs.ejs +93 -0
- package/dist/templates/boundaries/golangci-boundaries.yml.ejs +70 -0
- package/dist/templates/boundaries/ruff-boundaries.toml.ejs +32 -0
- package/dist/templates/claude/CLAUDE.md.ejs +78 -0
- package/dist/templates/claude/agents/agent-write-classes.json +30 -0
- package/dist/templates/claude/agents/bridge-reviewer.md.ejs +68 -0
- package/dist/templates/claude/agents/codebase-scanner.md.ejs +69 -0
- package/dist/templates/claude/agents/context-checker.md.ejs +73 -0
- package/dist/templates/claude/agents/red-team.md.ejs +134 -0
- package/dist/templates/claude/commands/close-gold-gap.md.ejs +43 -0
- package/dist/templates/claude/commands/drain.md.ejs +81 -0
- package/dist/templates/claude/commands/gold-audit.md.ejs +43 -0
- package/dist/templates/claude/commands/impact.md.ejs +39 -0
- package/dist/templates/claude/commands/levelup.md.ejs +61 -0
- package/dist/templates/claude/commands/ship.md.ejs +902 -0
- package/dist/templates/claude/commands/tabletop.md.ejs +46 -0
- package/dist/templates/claude/commands/task.md.ejs +87 -0
- package/dist/templates/claude/commands/wt-close.md.ejs +58 -0
- package/dist/templates/claude/commands/wt-list.md.ejs +48 -0
- package/dist/templates/claude/commands/wt-open.md.ejs +61 -0
- package/dist/templates/claude/commands/wt-prune.md.ejs +38 -0
- package/dist/templates/claude/hooks/check-circular-deps.mjs.ejs +56 -0
- package/dist/templates/claude/hooks/check-no-orphan-todo.mjs.ejs +45 -0
- package/dist/templates/claude/hooks/check-no-pii.mjs.ejs +121 -0
- package/dist/templates/claude/hooks/check-no-placeholders.mjs.ejs +71 -0
- package/dist/templates/claude/hooks/check-no-skipped-tests.mjs +80 -0
- package/dist/templates/claude/hooks/check-no-unused-exports.mjs +62 -0
- package/dist/templates/claude/hooks/closer-mode-guard.mjs.ejs +102 -0
- package/dist/templates/claude/hooks/debug-state-on-failure.mjs.ejs +98 -0
- package/dist/templates/claude/hooks/enforce-gate-before-pr.mjs +142 -0
- package/dist/templates/claude/hooks/enforce-read-only.mjs +28 -0
- package/dist/templates/claude/hooks/exitplanmode-banner.mjs.ejs +43 -0
- package/dist/templates/claude/hooks/guard-done-evidence.mjs.ejs +85 -0
- package/dist/templates/claude/hooks/guard-task-completion.mjs.ejs +56 -0
- package/dist/templates/claude/hooks/hooks.mjs.ejs +127 -0
- package/dist/templates/claude/hooks/lib.mjs.ejs +523 -0
- package/dist/templates/claude/hooks/post-brainstorm-stop.mjs +48 -0
- package/dist/templates/claude/hooks/post-commit-check.mjs.ejs +65 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/backend.ejs +5 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/frontend.ejs +5 -0
- package/dist/templates/claude/hooks/post-edit-dispatch.mjs.ejs +73 -0
- package/dist/templates/claude/hooks/post-subagent-release.mjs +80 -0
- package/dist/templates/claude/hooks/pre-compact.mjs.ejs +79 -0
- package/dist/templates/claude/hooks/pre-edit-plan-anchor.mjs.ejs +225 -0
- package/dist/templates/claude/hooks/pre-edit-ssot-guard.mjs +204 -0
- package/dist/templates/claude/hooks/pre-spawn-worktree-guard.mjs +129 -0
- package/dist/templates/claude/hooks/skill-forced-eval.mjs.ejs +281 -0
- package/dist/templates/claude/hooks/stop-dangerous.mjs +86 -0
- package/dist/templates/claude/hooks/stop-evidence-guard.mjs.ejs +245 -0
- package/dist/templates/claude/hooks/stop-finding-loss.mjs.ejs +168 -0
- package/dist/templates/claude/hooks/wiki-on-commit.mjs.ejs +38 -0
- package/dist/templates/claude/knowledge-map.json.ejs +26 -0
- package/dist/templates/claude/rules/05-agent-lifecycle.md +7 -0
- package/dist/templates/claude/rules/25-todo-folder-policy.md +5 -0
- package/dist/templates/claude/rules/40-context-economy.md +32 -0
- package/dist/templates/claude/rules/45-mcp-fallback.md +25 -0
- package/dist/templates/claude/rules/50-batch-execution.md +75 -0
- package/dist/templates/claude/rules/55-brainstorm-terminal-state.md +44 -0
- package/dist/templates/claude/rules/60-incidental-capture.md +43 -0
- package/dist/templates/claude/rules/75-impact-vault-reading.md +43 -0
- package/dist/templates/claude/rules/90-exec-protocol.md.ejs +40 -0
- package/dist/templates/claude/rules/95-closer-mode.md +45 -0
- package/dist/templates/claude/settings.json.ejs +165 -0
- package/dist/templates/claude/skills/architect-review/SKILL.md.ejs +82 -0
- package/dist/templates/claude/skills/brainstorming/SKILL.md.ejs +86 -0
- package/dist/templates/claude/skills/clean-code/SKILL.md.ejs +39 -0
- package/dist/templates/claude/skills/close-gold-gap/SKILL.md.ejs +58 -0
- package/dist/templates/claude/skills/codebase-audit/SKILL.md.ejs +88 -0
- package/dist/templates/claude/skills/configure/SKILL.md.ejs +72 -0
- package/dist/templates/claude/skills/epic-decompose/SKILL.md.ejs +86 -0
- package/dist/templates/claude/skills/gold-audit/SKILL.md.ejs +91 -0
- package/dist/templates/claude/skills/impact/SKILL.md.ejs +104 -0
- package/dist/templates/claude/skills/levelup/SKILL.md.ejs +146 -0
- package/dist/templates/claude/skills/tabletop/SKILL.md.ejs +118 -0
- package/dist/templates/claude/skills/tdd/SKILL.md.ejs +157 -0
- package/dist/templates/claude/skills/understand-code/SKILL.md.ejs +93 -0
- package/dist/templates/claude/skills/verification/SKILL.md.ejs +68 -0
- package/dist/templates/claude/skills/wave-drain/SKILL.md.ejs +491 -0
- package/dist/templates/codex/CODEX.md.ejs +140 -0
- package/dist/templates/codex/codex-adapter.mjs +132 -0
- package/dist/templates/codex/config.toml.ejs +189 -0
- package/dist/templates/compliance/compliance-mapping.md.ejs +124 -0
- package/dist/templates/compliance/compliance-menu.md.ejs +89 -0
- package/dist/templates/contract-testing/CONTRACTS_POLICY.md.ejs +30 -0
- package/dist/templates/contract-testing/api-snapshots/config-keys-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/config-response-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/enum-values-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/error-shape-baseline.json.ejs +6 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-baseline.json.ejs +12 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-content-types-baseline.json.ejs +3 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-paths-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-required-fields-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-response-status-baseline.json.ejs +3 -0
- package/dist/templates/contract-testing/api-snapshots/test-snapshot.json.ejs +4 -0
- package/dist/templates/contract-testing/env/.env.pact.ejs +4 -0
- package/dist/templates/contract-testing/graphql/GraphqlSchemaTest.java.ejs +29 -0
- package/dist/templates/contract-testing/graphql/graphql-inspector.test.ts.ejs +20 -0
- package/dist/templates/contract-testing/graphql/graphql_schema_test.go.ejs +27 -0
- package/dist/templates/contract-testing/graphql/graphql_schema_test.rs.ejs +27 -0
- package/dist/templates/contract-testing/graphql/test_graphql_schema.py.ejs +21 -0
- package/dist/templates/contract-testing/grpc/GrpcContractTest.java.ejs +23 -0
- package/dist/templates/contract-testing/grpc/buf-breaking.yml.ejs +6 -0
- package/dist/templates/contract-testing/grpc/buf.yaml.ejs +11 -0
- package/dist/templates/contract-testing/grpc/grpc-contract.test.ts.ejs +16 -0
- package/dist/templates/contract-testing/grpc/grpc_contract_test.go.ejs +22 -0
- package/dist/templates/contract-testing/grpc/grpc_contract_test.rs.ejs +22 -0
- package/dist/templates/contract-testing/grpc/test_grpc_contract.py.ejs +18 -0
- package/dist/templates/contract-testing/message-queue/SchemaRegistryCheckIT.java.ejs +44 -0
- package/dist/templates/contract-testing/message-queue/schema-registry-check.ts.ejs +42 -0
- package/dist/templates/contract-testing/message-queue/schema_registry_test.go.ejs +71 -0
- package/dist/templates/contract-testing/message-queue/schema_registry_test.rs.ejs +61 -0
- package/dist/templates/contract-testing/message-queue/test_schema_registry.py.ejs +31 -0
- package/dist/templates/contract-testing/pact-samples/assignment-response.json.ejs +7 -0
- package/dist/templates/contract-testing/pact-samples/availability-response.json.ejs +13 -0
- package/dist/templates/contract-testing/pact-samples/availability-rule-response.json.ejs +8 -0
- package/dist/templates/contract-testing/pact-samples/capacity-response.json.ejs +11 -0
- package/dist/templates/contract-testing/pact-samples/fully-booked-response.json.ejs +6 -0
- package/dist/templates/contract-testing/pact-samples/schedule-override-response.json.ejs +7 -0
- package/dist/templates/contract-testing/rest-owned/PactVerificationIT.java.ejs +31 -0
- package/dist/templates/contract-testing/rest-owned/pact-consumer.test.ts.ejs +36 -0
- package/dist/templates/contract-testing/rest-owned/pact-deps.gradle.ejs +15 -0
- package/dist/templates/contract-testing/rest-owned/pact_consumer_test.go.ejs +48 -0
- package/dist/templates/contract-testing/rest-owned/pact_consumer_test.rs.ejs +23 -0
- package/dist/templates/contract-testing/rest-owned/test_pact_consumer.py.ejs +35 -0
- package/dist/templates/contract-testing/rest-public/OpenApiDiffIT.java.ejs +44 -0
- package/dist/templates/contract-testing/rest-public/export-openapi-java.gradle.ejs +13 -0
- package/dist/templates/contract-testing/rest-public/export-openapi.mjs.ejs +21 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.go.ejs +30 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.py.ejs +27 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.rs.ejs +32 -0
- package/dist/templates/contract-testing/rest-public/openapi-diff.ts.ejs +42 -0
- package/dist/templates/contract-testing/rest-public/openapi_diff_test.go.ejs +35 -0
- package/dist/templates/contract-testing/rest-public/openapi_diff_test.rs.ejs +36 -0
- package/dist/templates/contract-testing/rest-public/test_openapi_diff.py.ejs +32 -0
- package/dist/templates/coverage/.coveragerc.ejs +26 -0
- package/dist/templates/coverage/.tarpaulin.toml.ejs +30 -0
- package/dist/templates/coverage/Cargo.toml.profile.release.ejs +24 -0
- package/dist/templates/coverage/jacoco-maven-setup.md.ejs +72 -0
- package/dist/templates/coverage/jacoco.gradle.ejs +49 -0
- package/dist/templates/coverage/kover.gradle.ejs +35 -0
- package/dist/templates/coverage/vitest.browser.config.ts.ejs +47 -0
- package/dist/templates/coverage/vitest.config.ts.ejs +47 -0
- package/dist/templates/docs/CODING_STANDARDS.md.ejs +81 -0
- package/dist/templates/docs/FEATURE_MATRIX.md.ejs +84 -0
- package/dist/templates/docs/GAP.md.ejs +58 -0
- package/dist/templates/docs/ISO27001_ANNEX_A.md.ejs +144 -0
- package/dist/templates/docs/MASTER_TEST_PLAN.md.ejs +52 -0
- package/dist/templates/docs/POST_MERGE_REVIEW_TEMPLATE.md.ejs +63 -0
- package/dist/templates/docs/SECURE_CODING_CHECKLIST.md.ejs +61 -0
- package/dist/templates/docs/adr/ADR-000_template.md.ejs +28 -0
- package/dist/templates/docs/bugs/bug-analysis.md.ejs +47 -0
- package/dist/templates/docs/bugs/bug-report.md.ejs +51 -0
- package/dist/templates/docs/bugs/bug-verification.md.ejs +42 -0
- package/dist/templates/docs/runbooks/deployment.md.ejs +119 -0
- package/dist/templates/docs/runbooks/prod-checklist.md.ejs +90 -0
- package/dist/templates/docs/runbooks/rollback.md.ejs +124 -0
- package/dist/templates/docs/runbooks/troubleshooting.md.ejs +130 -0
- package/dist/templates/docs/skeletons/arc42-canvas.md.ejs +46 -0
- package/dist/templates/docs/skeletons/arc42-full.md.ejs +59 -0
- package/dist/templates/docs/skeletons/decision-registry.md.ejs +38 -0
- package/dist/templates/docs/skeletons/er-model.md.ejs +19 -0
- package/dist/templates/docs/skeletons/glossary.md.ejs +8 -0
- package/dist/templates/docs/skeletons/governance.md.ejs +13 -0
- package/dist/templates/docs/skeletons/slo.md.ejs +21 -0
- package/dist/templates/docs/skeletons/technical-debt.md.ejs +8 -0
- package/dist/templates/docs/skeletons/test-strategy.md.ejs +14 -0
- package/dist/templates/docs/skeletons/threat-model-4q.md.ejs +20 -0
- package/dist/templates/docs/skeletons/threat-model-stride.md.ejs +22 -0
- package/dist/templates/docs/specs/atomic-task-list.md.ejs +57 -0
- package/dist/templates/docs/steering/product.md.ejs +44 -0
- package/dist/templates/docs/steering/structure.md.ejs +49 -0
- package/dist/templates/docs/steering/tech.md.ejs +53 -0
- package/dist/templates/documentation/cli-catalog.md.ejs +21 -0
- package/dist/templates/e2e/E2E_CONSTITUTION.md.ejs +77 -0
- package/dist/templates/e2e/playwright-python/conftest.py.ejs +22 -0
- package/dist/templates/e2e/playwright-python/run_axe.py.ejs +99 -0
- package/dist/templates/e2e/playwright-python/test_a11y.py.ejs +38 -0
- package/dist/templates/e2e/playwright-python/test_smoke.py.ejs +28 -0
- package/dist/templates/e2e/playwright-ts/a11y.spec.ts.ejs +20 -0
- package/dist/templates/e2e/playwright-ts/render-smoke.spec.ts.ejs +90 -0
- package/dist/templates/e2e/playwright-ts/run-axe.ts.ejs +74 -0
- package/dist/templates/e2e/smoke-journeys/journeys.spec.ts.ejs +53 -0
- package/dist/templates/evidence-files.json.ejs +13 -0
- package/dist/templates/frontend/FE_DESIGN_PRINCIPLES.md.ejs +115 -0
- package/dist/templates/frontend/FRONTEND_CONSTITUTION.md.ejs +170 -0
- package/dist/templates/frontend/design-tokens.json.ejs +107 -0
- package/dist/templates/frontend/vrt-setup.md.ejs +83 -0
- package/dist/templates/githooks/commit-msg.ejs +60 -0
- package/dist/templates/githooks/pre-commit.ejs +95 -0
- package/dist/templates/githooks/pre-push.ejs +251 -0
- package/dist/templates/githooks/setup-hooks.sh.ejs +8 -0
- package/dist/templates/github/PULL_REQUEST_TEMPLATE.md +37 -0
- package/dist/templates/github/PULL_REQUEST_TEMPLATE.md.ejs +52 -0
- package/dist/templates/github/actions/build-cache/action.yml.ejs +84 -0
- package/dist/templates/github/actions/nas-ssh/action.yml.ejs +53 -0
- package/dist/templates/github/actions/setup-java-maven/action.yml.ejs +53 -0
- package/dist/templates/github/actions/setup-node-pnpm/action.yml.ejs +39 -0
- package/dist/templates/github/actions/sign-and-attest/action.yml.ejs +53 -0
- package/dist/templates/github/dependabot.yml.ejs +66 -0
- package/dist/templates/github/extended-ci-paths.txt.ejs +38 -0
- package/dist/templates/github/issue-templates/bug-report.yml +72 -0
- package/dist/templates/github/issue-templates/compliance-item.yml.ejs +75 -0
- package/dist/templates/github/issue-templates/config.yml +2 -0
- package/dist/templates/github/issue-templates/epic.yml +66 -0
- package/dist/templates/github/issue-templates/feature-request.yml +24 -0
- package/dist/templates/github/issue-templates/task-brief.yml.ejs +125 -0
- package/dist/templates/github/labels.yml.ejs +161 -0
- package/dist/templates/github/scripts/sticky-failure-issue.sh.ejs +61 -0
- package/dist/templates/github/workflows/01-pr-fast.yml.ejs +1089 -0
- package/dist/templates/github/workflows/02-pr-extended.yml.ejs +512 -0
- package/dist/templates/github/workflows/03-human-approval.yml.ejs +76 -0
- package/dist/templates/github/workflows/04-deploy-test.yml.ejs +425 -0
- package/dist/templates/github/workflows/05-release.yml.ejs +813 -0
- package/dist/templates/github/workflows/06-nightly-lite.yml.ejs +125 -0
- package/dist/templates/github/workflows/06-nightly.yml.ejs +25 -0
- package/dist/templates/github/workflows/07-weekly-lite.yml.ejs +221 -0
- package/dist/templates/github/workflows/07-weekly.yml.ejs +23 -0
- package/dist/templates/github/workflows/08-monthly.yml.ejs +23 -0
- package/dist/templates/github/workflows/09-heartbeat.yml.ejs +268 -0
- package/dist/templates/github/workflows/10-deploy-prod.yml.ejs +144 -0
- package/dist/templates/github/workflows/11-k6-on-demand.yml.ejs +43 -0
- package/dist/templates/github/workflows/12-mutation-scheduled.yml.ejs +100 -0
- package/dist/templates/github/workflows/13-archunit-extended.yml.ejs +33 -0
- package/dist/templates/github/workflows/14-license-scan.yml.ejs +45 -0
- package/dist/templates/github/workflows/15-codeql.yml.ejs +117 -0
- package/dist/templates/github/workflows/16-frontend-quality.yml.ejs +154 -0
- package/dist/templates/github/workflows/17-ossf-scorecard.yml.ejs +46 -0
- package/dist/templates/github/workflows/18-frontend-lane.yml.ejs +50 -0
- package/dist/templates/github/workflows/_ai-draft-check.yml.ejs +44 -0
- package/dist/templates/github/workflows/_contract-postman.yml.ejs +93 -0
- package/dist/templates/github/workflows/_cosign-copy/aws-ecs.ejs +90 -0
- package/dist/templates/github/workflows/_cosign-copy/azure-container-app.ejs +97 -0
- package/dist/templates/github/workflows/_cosign-copy/gcp-cloud-run.ejs +102 -0
- package/dist/templates/github/workflows/_cosign-copy/ghcr.ejs +76 -0
- package/dist/templates/github/workflows/_cosign-copy/nas-compose.ejs +69 -0
- package/dist/templates/github/workflows/_deploy/aws-ecs.ejs +78 -0
- package/dist/templates/github/workflows/_deploy/azure-container-app.ejs +59 -0
- package/dist/templates/github/workflows/_deploy/gcp-cloud-run.ejs +71 -0
- package/dist/templates/github/workflows/_deploy/ghcr.ejs +19 -0
- package/dist/templates/github/workflows/_deploy/nas-compose.ejs +45 -0
- package/dist/templates/github/workflows/_k6-runner.yml.ejs +107 -0
- package/dist/templates/github/workflows/_label-on-approve.yml.ejs +44 -0
- package/dist/templates/github/workflows/_label-sync.yml.ejs +58 -0
- package/dist/templates/github/workflows/_monthly.yml.ejs +343 -0
- package/dist/templates/github/workflows/_nightly.yml.ejs +826 -0
- package/dist/templates/github/workflows/_notify.yml.ejs +96 -0
- package/dist/templates/github/workflows/_partials/macos-keychain-bypass.ejs +6 -0
- package/dist/templates/github/workflows/_partials/macos-keychain-restore.ejs +6 -0
- package/dist/templates/github/workflows/_partials/scheduled-heavy-jobs.ejs +117 -0
- package/dist/templates/github/workflows/_partials/sigstore-preflight.ejs +8 -0
- package/dist/templates/github/workflows/_partials/trivy-fs-java.ejs +27 -0
- package/dist/templates/github/workflows/_post-merge-notify.yml.ejs +228 -0
- package/dist/templates/github/workflows/_pr-staleness.yml.ejs +85 -0
- package/dist/templates/github/workflows/_shared-security.yml.ejs +132 -0
- package/dist/templates/github/workflows/_sigstore-retry-sign.yml.ejs +102 -0
- package/dist/templates/github/workflows/_weekly.yml.ejs +313 -0
- package/dist/templates/github/workflows/drift-shadow.yml.ejs +51 -0
- package/dist/templates/github/workflows/five-lane/ci.yml.ejs +54 -0
- package/dist/templates/github/workflows/five-lane/nightly.yml.ejs +70 -0
- package/dist/templates/github/workflows/five-lane/release.yml.ejs +53 -0
- package/dist/templates/github/workflows/five-lane/weekly.yml.ejs +72 -0
- package/dist/templates/github/workflows/issue-state.yml.ejs +93 -0
- package/dist/templates/global-invariants/GLOBAL_INVARIANTS.md.ejs +34 -0
- package/dist/templates/governance/RACI.md.ejs +21 -0
- package/dist/templates/governance/ci-mental-model.md.ejs +268 -0
- package/dist/templates/governance/contract-integrity-policy.md.ejs +116 -0
- package/dist/templates/governance/enterprise-compliance-baseline.md.ejs +111 -0
- package/dist/templates/governance/evidence-retention.md.ejs +43 -0
- package/dist/templates/governance/gdpr-erasure-runbook.md.ejs +165 -0
- package/dist/templates/governance/qa-audit-phases.md.ejs +129 -0
- package/dist/templates/governance/risk-assessment-template.md.ejs +105 -0
- package/dist/templates/governance/risk-register.md.ejs +76 -0
- package/dist/templates/governance/solo-dev-exception.md.ejs +96 -0
- package/dist/templates/governance/validation-evidence.md.ejs +131 -0
- package/dist/templates/infra/azure/containerapp.tpl.yaml.ejs +74 -0
- package/dist/templates/infra/nas-compose/deploy.sh.ejs +114 -0
- package/dist/templates/integration-testing/AbstractIntegrationTest.java.ejs +36 -0
- package/dist/templates/integration-testing/NoH2ArchTest.java.ejs +24 -0
- package/dist/templates/integration-testing/conftest.py.ejs +20 -0
- package/dist/templates/integration-testing/db_fixture.rs.ejs +48 -0
- package/dist/templates/integration-testing/eslint-no-fake-db.json.ejs +19 -0
- package/dist/templates/integration-testing/eslint.config.no-fake-db.mjs.ejs +46 -0
- package/dist/templates/integration-testing/main_test.go.ejs +53 -0
- package/dist/templates/integration-testing/main_test_sqlite.go.ejs +37 -0
- package/dist/templates/integration-testing/test-setup.ts.ejs +26 -0
- package/dist/templates/integration-testing/testcontainers-deps.gradle.ejs +9 -0
- package/dist/templates/java/DeprecationInterceptor.java.ejs +50 -0
- package/dist/templates/java/archunit-hexagonal.ejs +69 -0
- package/dist/templates/java/modulith/ApplicationModulesTest.java.ejs +21 -0
- package/dist/templates/java/modulith/modulith-deps.gradle.ejs +13 -0
- package/dist/templates/java/modulith/modulith-maven-setup.md.ejs +21 -0
- package/dist/templates/java/pharma/AuditEvent.java.ejs +138 -0
- package/dist/templates/java/pharma/AuditMapper.java.ejs +60 -0
- package/dist/templates/java/pharma/archunit-pharma.ejs +128 -0
- package/dist/templates/kit/GLOBAL_KIT.md.ejs +11 -0
- package/dist/templates/kit/dim.md.ejs +33 -0
- package/dist/templates/llms-txt.config.json.ejs +22 -0
- package/dist/templates/local-wrapper/.env.example.ejs +15 -0
- package/dist/templates/local-wrapper/Makefile.ejs +44 -0
- package/dist/templates/local-wrapper/run.sh.ejs +5 -0
- package/dist/templates/middleware/410-gone-handler.ts.ejs +41 -0
- package/dist/templates/middleware/correlation-id.ts.ejs +32 -0
- package/dist/templates/middleware/deprecation.ts.ejs +45 -0
- package/dist/templates/middleware/error-handler.ts.ejs +73 -0
- package/dist/templates/middleware/payload-size-limit.ts.ejs +51 -0
- package/dist/templates/mutation/README.md.ejs +67 -0
- package/dist/templates/mutation/cargo-mutants.toml.ejs +9 -0
- package/dist/templates/mutation/mutmut-config.toml.ejs +7 -0
- package/dist/templates/mutation/parse-mutants.mjs.ejs +44 -0
- package/dist/templates/mutation/parse-mutmut.py.ejs +44 -0
- package/dist/templates/mutation/pitest-l2-setup.md.ejs +66 -0
- package/dist/templates/mutation/pitest-maven-setup.md.ejs +64 -0
- package/dist/templates/mutation/pitest.gradle.ejs +23 -0
- package/dist/templates/mutation/stryker.conf.json.ejs +17 -0
- package/dist/templates/observability/setup.md.ejs +495 -0
- package/dist/templates/operations/handbook.md.ejs +217 -0
- package/dist/templates/perf/bundle-budget.json.ejs +29 -0
- package/dist/templates/perf/k6/reports/csv-report.py.ejs +91 -0
- package/dist/templates/perf/k6/reports/html-report.py.ejs +114 -0
- package/dist/templates/perf/k6/reports/json-report.py.ejs +74 -0
- package/dist/templates/perf/k6/scenarios/breakpoint.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/burst.js.ejs +43 -0
- package/dist/templates/perf/k6/scenarios/endurance.js.ejs +36 -0
- package/dist/templates/perf/k6/scenarios/load.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/ramp-down.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/ramp-up.js.ejs +32 -0
- package/dist/templates/perf/k6/scenarios/smoke.js.ejs +23 -0
- package/dist/templates/perf/k6/scenarios/soak.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/spike.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/steady-state.js.ejs +31 -0
- package/dist/templates/perf/k6/scenarios/stress.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/volume.js.ejs +36 -0
- package/dist/templates/perf/k6/seed/test-data.sql.ejs +59 -0
- package/dist/templates/perf/lighthouserc.json.ejs +41 -0
- package/dist/templates/quality/iso9001/CAPA_LOG.md.ejs +36 -0
- package/dist/templates/quality/iso9001/DOCUMENT_CONTROL.md.ejs +36 -0
- package/dist/templates/quality/iso9001/REQUIREMENTS_TRACEABILITY.md.ejs +61 -0
- package/dist/templates/regulated/overlay.json.ejs +26 -0
- package/dist/templates/regulated/regulated-overlay.md.ejs +40 -0
- package/dist/templates/resilience/RESILIENCE.md.ejs +310 -0
- package/dist/templates/root/.gitignore.ejs +85 -0
- package/dist/templates/root/CODEOWNERS.ejs +18 -0
- package/dist/templates/root/CONTRIBUTING.md.ejs +62 -0
- package/dist/templates/root/SECURITY.md.ejs +19 -0
- package/dist/templates/root/TEST_TAXONOMY.md.ejs +100 -0
- package/dist/templates/root/commitlint.config.js.ejs +4 -0
- package/dist/templates/root/docs/METHOD/CANONICAL_PATHS.md.ejs +24 -0
- package/dist/templates/root/docs/METHOD/ENGINEERING_DEFAULTS.md.ejs +136 -0
- package/dist/templates/root/docs/METHOD/KNOWLEDGE_MAP.md.ejs +90 -0
- package/dist/templates/root/docs/METHOD/PATTERNS_CATALOG.md.ejs +51 -0
- package/dist/templates/root/docs/METHOD/REUSE_REGISTRY.md.ejs +26 -0
- package/dist/templates/root/docs/METHOD/REUSE_REGISTRY_SPEC.md.ejs +80 -0
- package/dist/templates/root/docs/METHOD/SSOT_CORE_SET.md.ejs +62 -0
- package/dist/templates/root/docs/METHOD/TRACK_ROUTER.md.ejs +39 -0
- package/dist/templates/root/docs/SYSTEM/CANON.md.ejs +359 -0
- package/dist/templates/root/docs/SYSTEM/FAIL_CLOSED.md.ejs +171 -0
- package/dist/templates/root/editorconfig.ejs +39 -0
- package/dist/templates/root/tsconfig.json.ejs +21 -0
- package/dist/templates/rust/clippy.toml.ejs +14 -0
- package/dist/templates/schemas/milestone.schema.json.ejs +164 -0
- package/dist/templates/schemas/rtm-verdict.schema.json.ejs +52 -0
- package/dist/templates/schemas/source-record.schema.json.ejs +91 -0
- package/dist/templates/schemas/tabletop-evidence.schema.json.ejs +53 -0
- package/dist/templates/schemas/use-case.schema.json.ejs +59 -0
- package/dist/templates/scripts/apply-branch-protection.mjs.ejs +70 -0
- package/dist/templates/scripts/audit-toolchain.mjs.ejs +112 -0
- package/dist/templates/scripts/bg-run.sh.ejs +59 -0
- package/dist/templates/scripts/capacity-probe.mjs.ejs +147 -0
- package/dist/templates/scripts/capture-debt-baseline.mjs.ejs +99 -0
- package/dist/templates/scripts/check-acceptance.mjs.ejs +298 -0
- package/dist/templates/scripts/check-action-pins.mjs.ejs +215 -0
- package/dist/templates/scripts/check-agent-return.mjs.ejs +177 -0
- package/dist/templates/scripts/check-all.mjs.ejs +1746 -0
- package/dist/templates/scripts/check-anti-fake-green.mjs.ejs +80 -0
- package/dist/templates/scripts/check-anti-proforma.mjs.ejs +547 -0
- package/dist/templates/scripts/check-api-e2e.mjs.ejs +106 -0
- package/dist/templates/scripts/check-arc42-slots.mjs.ejs +25 -0
- package/dist/templates/scripts/check-assertion-delta.mjs.ejs +287 -0
- package/dist/templates/scripts/check-audit-dry-pass.mjs.ejs +176 -0
- package/dist/templates/scripts/check-bundle-size.mjs.ejs +151 -0
- package/dist/templates/scripts/check-canonical-paths.mjs.ejs +39 -0
- package/dist/templates/scripts/check-ci-tiers.mjs.ejs +111 -0
- package/dist/templates/scripts/check-claude-md-lint.mjs.ejs +142 -0
- package/dist/templates/scripts/check-collab-mode-wired.mjs.ejs +60 -0
- package/dist/templates/scripts/check-commit-footer-rationale.mjs.ejs +170 -0
- package/dist/templates/scripts/check-constraint-scan.mjs.ejs +681 -0
- package/dist/templates/scripts/check-consumer-audit.mjs.ejs +414 -0
- package/dist/templates/scripts/check-continue-on-error.mjs.ejs +227 -0
- package/dist/templates/scripts/check-cross-model-review.mjs.ejs +458 -0
- package/dist/templates/scripts/check-decision-registry.mjs.ejs +101 -0
- package/dist/templates/scripts/check-doc-freshness.mjs.ejs +17 -0
- package/dist/templates/scripts/check-doc-links.mjs.ejs +98 -0
- package/dist/templates/scripts/check-doc-set.mjs.ejs +18 -0
- package/dist/templates/scripts/check-docs.mjs.ejs +71 -0
- package/dist/templates/scripts/check-domain-api-surface.mjs.ejs +80 -0
- package/dist/templates/scripts/check-drift.mjs.ejs +167 -0
- package/dist/templates/scripts/check-duplication.mjs.ejs +45 -0
- package/dist/templates/scripts/check-e2e-escalation.mjs.ejs +199 -0
- package/dist/templates/scripts/check-e2e-quarantine.mjs.ejs +68 -0
- package/dist/templates/scripts/check-emission-parity.mjs.ejs +91 -0
- package/dist/templates/scripts/check-exit-code-contract.mjs.ejs +138 -0
- package/dist/templates/scripts/check-fail-closed-audit.mjs.ejs +389 -0
- package/dist/templates/scripts/check-fe-boundaries.mjs.ejs +184 -0
- package/dist/templates/scripts/check-feature-matrix.mjs.ejs +835 -0
- package/dist/templates/scripts/check-frontend-lane.mjs.ejs +73 -0
- package/dist/templates/scripts/check-grace-window.mjs.ejs +102 -0
- package/dist/templates/scripts/check-handoff-doc.mjs.ejs +192 -0
- package/dist/templates/scripts/check-hook-routing.mjs.ejs +224 -0
- package/dist/templates/scripts/check-image-pins.mjs.ejs +90 -0
- package/dist/templates/scripts/check-inline-suppressions.mjs.ejs +153 -0
- package/dist/templates/scripts/check-iso9001.mjs.ejs +146 -0
- package/dist/templates/scripts/check-knowledge-map.mjs.ejs +49 -0
- package/dist/templates/scripts/check-local-ci-parity.mjs.ejs +382 -0
- package/dist/templates/scripts/check-m16-handoff.mjs.ejs +112 -0
- package/dist/templates/scripts/check-merge-method.mjs.ejs +92 -0
- package/dist/templates/scripts/check-milestones.mjs.ejs +678 -0
- package/dist/templates/scripts/check-min-test-execution.mjs.ejs +225 -0
- package/dist/templates/scripts/check-muted-test.mjs.ejs +270 -0
- package/dist/templates/scripts/check-no-stub-redirects.mjs.ejs +154 -0
- package/dist/templates/scripts/check-no-tracked-artifacts.mjs.ejs +134 -0
- package/dist/templates/scripts/check-oracle-discrimination.mjs.ejs +244 -0
- package/dist/templates/scripts/check-pii-scan.mjs.ejs +67 -0
- package/dist/templates/scripts/check-pipe-tee-hazard.mjs.ejs +85 -0
- package/dist/templates/scripts/check-pr-size-gate.mjs.ejs +85 -0
- package/dist/templates/scripts/check-refutation-verdicts.mjs.ejs +256 -0
- package/dist/templates/scripts/check-regulated-overlay.mjs.ejs +158 -0
- package/dist/templates/scripts/check-render-smoke.mjs.ejs +100 -0
- package/dist/templates/scripts/check-reuse-registry.mjs.ejs +44 -0
- package/dist/templates/scripts/check-review-completion.mjs.ejs +708 -0
- package/dist/templates/scripts/check-safety-adopt-ratchet.mjs.ejs +142 -0
- package/dist/templates/scripts/check-secret-presence.mjs.ejs +191 -0
- package/dist/templates/scripts/check-secret-scan.mjs.ejs +82 -0
- package/dist/templates/scripts/check-skip-critical-e2e.mjs.ejs +142 -0
- package/dist/templates/scripts/check-smoke-journeys.mjs.ejs +272 -0
- package/dist/templates/scripts/check-solo-reactivation.mjs.ejs +102 -0
- package/dist/templates/scripts/check-sources.mjs.ejs +245 -0
- package/dist/templates/scripts/check-ssot-core.mjs.ejs +45 -0
- package/dist/templates/scripts/check-stack-conformity.mjs.ejs +160 -0
- package/dist/templates/scripts/check-stride-traceability.mjs.ejs +101 -0
- package/dist/templates/scripts/check-suppression-expiry.mjs.ejs +79 -0
- package/dist/templates/scripts/check-suppression-rationale.mjs.ejs +76 -0
- package/dist/templates/scripts/check-suppressions.mjs.ejs +150 -0
- package/dist/templates/scripts/check-tabletop-evidence.mjs.ejs +499 -0
- package/dist/templates/scripts/check-tdd-evidence.mjs.ejs +381 -0
- package/dist/templates/scripts/check-test-naming.mjs.ejs +151 -0
- package/dist/templates/scripts/check-test-pyramid.mjs.ejs +170 -0
- package/dist/templates/scripts/check-test-scope-tier.mjs.ejs +208 -0
- package/dist/templates/scripts/check-tier-coverage.mjs.ejs +71 -0
- package/dist/templates/scripts/check-todo-max-age.mjs.ejs +205 -0
- package/dist/templates/scripts/check-touched-vs-manifest.mjs.ejs +179 -0
- package/dist/templates/scripts/check-unwired-guards.mjs.ejs +367 -0
- package/dist/templates/scripts/check-use-cases.mjs.ejs +345 -0
- package/dist/templates/scripts/check-validator-helptext.mjs.ejs +80 -0
- package/dist/templates/scripts/check-wiki-lint.mjs.ejs +162 -0
- package/dist/templates/scripts/check-workflow-docs-sync.mjs.ejs +91 -0
- package/dist/templates/scripts/check-workflow-job-naming.mjs.ejs +102 -0
- package/dist/templates/scripts/check-workflow-perms.mjs.ejs +54 -0
- package/dist/templates/scripts/check-workflow-runners.mjs.ejs +87 -0
- package/dist/templates/scripts/check-workflow-sha-pinning.mjs.ejs +89 -0
- package/dist/templates/scripts/check-workflow-test-integrity.mjs.ejs +235 -0
- package/dist/templates/scripts/checks/check-rust-no-unsafe.mjs.ejs +58 -0
- package/dist/templates/scripts/checks/check-rust-no-unwrap.mjs.ejs +61 -0
- package/dist/templates/scripts/ci-classify-changes.mjs.ejs +119 -0
- package/dist/templates/scripts/conformance.mjs.ejs +37 -0
- package/dist/templates/scripts/constraint-map.json.ejs +25 -0
- package/dist/templates/scripts/debt-lib.mjs.ejs +1015 -0
- package/dist/templates/scripts/debt-report.mjs.ejs +199 -0
- package/dist/templates/scripts/domain-api-surface.json.ejs +23 -0
- package/dist/templates/scripts/done-evidence.mjs.ejs +359 -0
- package/dist/templates/scripts/evidence-collect.mjs.ejs +264 -0
- package/dist/templates/scripts/evidence-prune.mjs.ejs +96 -0
- package/dist/templates/scripts/evidence-rotate.mjs.ejs +53 -0
- package/dist/templates/scripts/evidence-writer.sh.ejs +220 -0
- package/dist/templates/scripts/gate-registry.yml.ejs +286 -0
- package/dist/templates/scripts/gen-doc-index.mjs.ejs +19 -0
- package/dist/templates/scripts/gen-gap.mjs.ejs +153 -0
- package/dist/templates/scripts/gen-llms-txt.mjs.ejs +18 -0
- package/dist/templates/scripts/gen-wiki.mjs.ejs +196 -0
- package/dist/templates/scripts/gh-cleanup-expired-artifacts.mjs.ejs +134 -0
- package/dist/templates/scripts/gold-audit.mjs.ejs +19 -0
- package/dist/templates/scripts/i18n-literal-scanner.mjs.ejs +175 -0
- package/dist/templates/scripts/ingest-zap-report.mjs.ejs +107 -0
- package/dist/templates/scripts/inject-pact-samples.sh.ejs +96 -0
- package/dist/templates/scripts/issue-readiness.mjs.ejs +84 -0
- package/dist/templates/scripts/knowledge-map-update.mjs.ejs +32 -0
- package/dist/templates/scripts/lib/acceptance-criteria.mjs.ejs +213 -0
- package/dist/templates/scripts/lib/agent-return-validate.mjs.ejs +481 -0
- package/dist/templates/scripts/lib/coverage-gate.mjs.ejs +54 -0
- package/dist/templates/scripts/lib/e2e-reliability.mjs.ejs +207 -0
- package/dist/templates/scripts/lib/ephemeral-server.mjs.ejs +148 -0
- package/dist/templates/scripts/lib/evidence-binding.mjs.ejs +140 -0
- package/dist/templates/scripts/lib/exact-sha-policy.mjs.ejs +233 -0
- package/dist/templates/scripts/lib/gate-args.mjs.ejs +20 -0
- package/dist/templates/scripts/lib/gate-evidence.mjs.ejs +622 -0
- package/dist/templates/scripts/lib/gate-mutex.mjs.ejs +340 -0
- package/dist/templates/scripts/lib/glob-walk.mjs.ejs +148 -0
- package/dist/templates/scripts/lib/run-helpers.mjs.ejs +416 -0
- package/dist/templates/scripts/lib/seed-common.sh.ejs +105 -0
- package/dist/templates/scripts/lib/waiter-count.mjs.ejs +62 -0
- package/dist/templates/scripts/muted-tests-baseline.json.ejs +4 -0
- package/dist/templates/scripts/optional-emissions.json.ejs +117 -0
- package/dist/templates/scripts/oracle-discrimination-baseline.json.ejs +6 -0
- package/dist/templates/scripts/pid-watch.sh.ejs +78 -0
- package/dist/templates/scripts/pii-scan.mjs.ejs +125 -0
- package/dist/templates/scripts/pr-merge-watch.mjs.ejs +445 -0
- package/dist/templates/scripts/record-agent-return.mjs.ejs +279 -0
- package/dist/templates/scripts/record-journey-evidence.mjs.ejs +69 -0
- package/dist/templates/scripts/rework-log.mjs.ejs +172 -0
- package/dist/templates/scripts/run-postman-tests.sh.ejs +112 -0
- package/dist/templates/scripts/schemas/agent-return-external.schema.json +74 -0
- package/dist/templates/scripts/schemas/agent-return.schema.json.ejs +181 -0
- package/dist/templates/scripts/schemas/cross-model-dispatch.schema.json +95 -0
- package/dist/templates/scripts/seed-test-data.sh.ejs +99 -0
- package/dist/templates/scripts/self-validation.mjs.ejs +126 -0
- package/dist/templates/scripts/setup-repo.sh.ejs +78 -0
- package/dist/templates/scripts/spotbugs-baseline.json.ejs +4 -0
- package/dist/templates/scripts/test-gate-layering.mjs.ejs +92 -0
- package/dist/templates/scripts/validate-api-snapshots.mjs.ejs +86 -0
- package/dist/templates/scripts/validate-k6-scenarios.mjs.ejs +96 -0
- package/dist/templates/scripts/validate-openapi-field-types.mjs.ejs +124 -0
- package/dist/templates/scripts/validate-postman-collection.mjs.ejs +159 -0
- package/dist/templates/scripts/verify-fe-coverage.mjs.ejs +138 -0
- package/dist/templates/scripts/verify-i18n-parity.mjs.ejs +114 -0
- package/dist/templates/scripts/verify-module-coverage.mjs.ejs +207 -0
- package/dist/templates/scripts/verify-mutation-baseline.mjs.ejs +209 -0
- package/dist/templates/scripts/verify-spotbugs.mjs.ejs +216 -0
- package/dist/templates/scripts/verify-tokens.mjs.ejs +177 -0
- package/dist/templates/security/RISK_ASSESSMENT.md.ejs +35 -0
- package/dist/templates/security/STRIDE.md.ejs +24 -0
- package/dist/templates/security/gitleaks.toml.ejs +21 -0
- package/dist/templates/security/zap/baseline-auth.context.ejs +58 -0
- package/dist/templates/security/zap/rules.tsv.ejs +100 -0
- package/dist/templates/ship/TICK_PROMPT.md.ejs +55 -0
- package/dist/templates/ship/supervisor.sh.ejs +46 -0
- package/dist/templates/smoke-journeys/manifest.json.ejs +1 -0
- package/dist/templates/sonar-project.properties.ejs +25 -0
- package/dist/templates/standards/doc-profile.ejs +27 -0
- package/dist/templates/standards/gold-doc-set.yml.ejs +528 -0
- package/dist/templates/standards/gold-registry.java.yml.ejs +266 -0
- package/dist/templates/standards/gold-registry.typescript.yml.ejs +104 -0
- package/dist/templates/standards/gold-registry.yml.ejs +119 -0
- package/dist/templates/standards/thresholds.yml.ejs +86 -0
- package/dist/templates/static-analysis/.dependency-cruiser.cjs.ejs +78 -0
- package/dist/templates/static-analysis/.golangci.yml.ejs +70 -0
- package/dist/templates/static-analysis/.shellcheckrc.ejs +13 -0
- package/dist/templates/static-analysis/.yamllint.yml.ejs +41 -0
- package/dist/templates/static-analysis/checkstyle.xml.ejs +83 -0
- package/dist/templates/static-analysis/detekt.yml.ejs +26 -0
- package/dist/templates/static-analysis/eslint.config.mjs.ejs +54 -0
- package/dist/templates/static-analysis/eslint.config.static.mjs.ejs +38 -0
- package/dist/templates/static-analysis/eslintrc-static.json.ejs +30 -0
- package/dist/templates/static-analysis/jscpd.json.ejs +25 -0
- package/dist/templates/static-analysis/knip.json.ejs +26 -0
- package/dist/templates/static-analysis/pmd-ruleset.xml.ejs +92 -0
- package/dist/templates/static-analysis/prettierignore.ejs +59 -0
- package/dist/templates/static-analysis/prettierrc.json.ejs +10 -0
- package/dist/templates/static-analysis/requirements-dev.txt.ejs +11 -0
- package/dist/templates/static-analysis/ruff.toml.ejs +40 -0
- package/dist/templates/static-analysis/rustfmt.toml.ejs +3 -0
- package/dist/templates/static-analysis/spotbugs-exclude.xml.ejs +120 -0
- package/dist/templates/static-analysis/spotbugs.gradle.ejs +18 -0
- package/dist/templates/static-analysis/spotless.gradle.ejs +38 -0
- package/dist/templates/static-analysis/stylelintrc.json.ejs +16 -0
- package/dist/templates/suppressions/archunit-baseline.json.ejs +1 -0
- package/dist/templates/suppressions/consumer-audit-allowlist.json.ejs +1 -0
- package/dist/templates/suppressions/gitleaksignore.ejs +20 -0
- package/dist/templates/suppressions/pii-allowlist.json.ejs +1 -0
- package/dist/templates/suppressions/suppressions-schema.json.ejs +39 -0
- package/dist/templates/suppressions/trivyignore.ejs +17 -0
- package/dist/templates/testing/test-taxonomy.md.ejs +120 -0
- package/dist/test/example.behavioral.test.js +2 -0
- package/dist/types/memory.d.ts +19 -0
- package/dist/types/memory.js +1 -0
- package/dist/types/plan.d.ts +102 -0
- package/dist/types/plan.js +2 -0
- package/dist/types/plugin.d.ts +45 -0
- package/dist/types/plugin.js +1 -0
- package/dist/utils/canon-loader.js +2 -0
- package/dist/utils/channel.js +1 -0
- package/dist/utils/config.d.ts +79 -0
- package/dist/utils/config.js +5 -0
- package/dist/utils/confirm-downgrade.js +5 -0
- package/dist/utils/env.js +2 -0
- package/dist/utils/error-catalog.js +2 -0
- package/dist/utils/errors.js +2 -0
- package/dist/utils/evidence-log.js +3 -0
- package/dist/utils/file-lock.js +2 -0
- package/dist/utils/first-run.js +8 -0
- package/dist/utils/fs.js +3 -0
- package/dist/utils/github-issue-helper.js +4 -0
- package/dist/utils/gradle.js +21 -0
- package/dist/utils/json-output.js +2 -0
- package/dist/utils/logger-config.js +4 -0
- package/dist/utils/logger.js +3 -0
- package/dist/utils/maturity-check.js +2 -0
- package/dist/utils/pkg.js +3 -0
- package/dist/utils/platform.js +2 -0
- package/dist/utils/plugin-loader.js +2 -0
- package/dist/utils/plugin-worker.js +2 -0
- package/dist/utils/prettier-format.js +2 -0
- package/dist/utils/profiler.js +2 -0
- package/dist/utils/render.js +2 -0
- package/dist/utils/replay.js +3 -0
- package/dist/utils/run-cli.js +6 -0
- package/dist/utils/run-id.js +3 -0
- package/dist/utils/safe-read.js +2 -0
- package/dist/utils/task-id.js +2 -0
- package/dist/utils/today.js +2 -0
- package/dist/utils/tty.js +2 -0
- package/dist/utils/walk-dir.js +2 -0
- package/dist/verify/rules/drive-by-scope.js +1 -0
- package/dist/verify/rules/orphan-todos.js +1 -0
- package/dist/verify/rules/registry.js +1 -0
- package/dist/verify/rules/skip-patterns.js +1 -0
- package/dist/verify/rules/types.d.ts +15 -0
- package/dist/verify/rules/types.js +1 -0
- package/dist/verify/rules/ui-language.js +1 -0
- package/dist/verify/run.js +3 -0
- package/dist/wizard/archetype-defaults.js +1 -0
- package/dist/wizard/presets.js +1 -0
- package/dist/wizard/prompts.js +44 -0
- package/dist/wizard/types.d.ts +637 -0
- package/dist/wizard/types.js +1 -0
- package/dist/worktree/harvest.js +2 -0
- package/dist/worktree/links.js +2 -0
- package/dist/worktree/paths.js +2 -0
- package/dist/worktree/validate.js +3 -0
- package/package.json +124 -0
- package/scripts/check-arc42-slots.mjs +738 -0
- package/scripts/check-doc-freshness.mjs +236 -0
- package/scripts/check-doc-set.mjs +243 -0
- package/scripts/check-doc-style.mjs +228 -0
- package/scripts/gold-audit.mjs +319 -0
- package/scripts/lib/doc-gate-allowlist.mjs +123 -0
- package/scripts/lib/doc-set-resolve.mjs +274 -0
- package/scripts/lib/glob-walk.mjs +148 -0
- package/scripts/lib/gold-audit-lib.mjs +1260 -0
- package/scripts/lib/run-helpers.mjs +413 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync}from"node:fs";import{dirname,join,resolve}from"node:path";import{fileURLToPath}from"node:url";import{INVARIANT_CATALOG}from"../invariants/catalog.js";import{ERROR_CATALOG}from"../utils/error-catalog.js";import{loadCanonEntries}from"../utils/canon-loader.js";import{ensureDir,writeFileTranslated,readFileTranslated}from"../utils/fs.js";import{loadConfig}from"../utils/config.js";import{slugifyProjectName}from"./init.js";const DOCS_ROOT=resolve(fileURLToPath(import.meta.url),"../../..","docs"),CANON_MD_PATH=join(DOCS_ROOT,"internal/SYSTEM/CANON.md"),TEMPLATES_DIR=join(dirname(fileURLToPath(import.meta.url)),"..","templates"),HANDOFF_TEMPLATE_PATH=join(TEMPLATES_DIR,"HANDOFF.template.md"),FLAG_CATALOG=new Map([["hasPublicApi",{summary:"Project exposes a public API (REST / GraphQL / gRPC).",detail:"Generates: OWASP ZAP DAST scan, an OpenAPI/contract test suite, and an API deprecation policy with a breaking-change gate. Also unlocks the contractType prompt."}],["isMultiTenant",{summary:"Project serves multiple tenants from one deployment.",detail:"Descriptive metadata input only \u2014 adds a risk-register entry for tenant data isolation (R-008). Does not generate isolation/auth machinery; isolation topology is user-determined (row-level, schema-per-tenant, DB-per-tenant, or file-per-tenant)."}],["contractType",{summary:"Contract-testing strategy for the public API.",detail:"Generates: a consumer/provider contract-test suite \u2014 Pact (rest-owned), OpenAPI-diff (rest-public), graphql-inspector (graphql), buf breaking (grpc), or schema-registry (message-queue) \u2014 wired into CI."}]]);function resolveFlagCode(code){const lower=code.toLowerCase();for(const key of FLAG_CATALOG.keys())if(key.toLowerCase()===lower)return key}export function runExplain(code,opts){if(opts.handoff!==void 0)return scaffoldHandoff(opts.handoff,opts.out);if(opts.list)return listAll(opts.format);if(!code)return{exitCode:1,output:"",error:`Usage: arbiter explain <code> [--format json] | --list
|
|
3
|
+
`};const flagKey=resolveFlagCode(code);if(flagKey)return explainFlag(flagKey,opts.format);const normalized=code.toUpperCase();return normalized.startsWith("INV-")?explainInv(normalized,opts.format):normalized.startsWith("PROJ-")?explainProjectInvariant(normalized,opts.format):normalized.startsWith("CANON-")?explainCanon(normalized,opts.format):normalized.startsWith("E_")||ERROR_CATALOG.has(normalized)?explainError(normalized,opts.format):opts.format==="json"?{exitCode:1,output:JSON.stringify({error:`Unknown code: ${code}`},null,2),error:""}:{exitCode:1,output:"",error:`Unknown code: ${code}
|
|
4
|
+
Run \`arbiter explain --list\` to see all known codes.
|
|
5
|
+
`}}function scaffoldHandoff(topic,outDir){if(!topic.trim())return{exitCode:1,output:"",error:`Usage: arbiter explain --handoff <topic>
|
|
6
|
+
`};const slug=slugifyProjectName(topic).toUpperCase(),targetDir=outDir??process.cwd(),filePath=join(targetDir,`HANDOFF-${slug}.md`);if(existsSync(filePath))return{exitCode:0,output:`${filePath} already exists \u2014 not overwritten (edit it directly).
|
|
7
|
+
`,error:""};const template=readFileTranslated(HANDOFF_TEMPLATE_PATH,"utf-8"),date=new Date().toISOString().slice(0,10),content=template.replaceAll("{{TOPIC}}",topic).replaceAll("{{DATE}}",date);return ensureDir(targetDir),writeFileTranslated(filePath,content),{exitCode:0,output:`Scaffolded ${filePath}
|
|
8
|
+
`,error:""}}function explainInv(id,format){const inv=INVARIANT_CATALOG.find(i=>i.id===id);if(!inv)return{exitCode:1,output:"",error:`Unknown invariant: ${id}
|
|
9
|
+
`};if(format==="json"){const payload={code:inv.id,category:"INV",summary:inv.title,detail:inv.description,enforcement:inv.enforcement??"",tier:inv.tier,alwaysActive:inv.alwaysActive};return{exitCode:0,output:JSON.stringify(payload,null,2),error:""}}const lines=["",`${inv.id} \u2014 ${inv.title}`,"",` ${inv.description}`,""];return inv.enforcement&&lines.push(` Enforcement: ${inv.enforcement}`,""),lines.push(` Tier: ${inv.tier} | Always active: ${inv.alwaysActive?"yes":"no"}`,""),lines.push(" Run `arbiter explain --list` to see all codes.",""),{exitCode:0,output:lines.join(`
|
|
10
|
+
`),error:""}}function explainProjectInvariant(id,format){const inv=loadProjectInvariants().find(i=>i.id===id);if(!inv)return{exitCode:1,output:"",error:`Unknown invariant: ${id}
|
|
11
|
+
`};if(format==="json"){const payload={code:inv.id,category:"PROJ",summary:inv.title,detail:inv.description,enforcement:inv.enforcement??"",tier:inv.tier,alwaysActive:inv.alwaysActive};return{exitCode:0,output:JSON.stringify(payload,null,2),error:""}}const lines=["",`${inv.id} \u2014 ${inv.title}`,"",` ${inv.description}`,""];return inv.enforcement&&lines.push(` Enforcement: ${inv.enforcement}`,""),lines.push(` Tier: ${inv.tier} | Always active: ${inv.alwaysActive?"yes":"no"}`,""),lines.push(" Project-declared invariant (arbiter.json governance.projectInvariants).",""),lines.push(" Run `arbiter explain --list` to see all codes.",""),{exitCode:0,output:lines.join(`
|
|
12
|
+
`),error:""}}function loadProjectInvariants(){return loadConfig(process.cwd())?.governance?.projectInvariants??[]}function explainCanon(id,format){const entry=loadCanonEntries(CANON_MD_PATH).find(e=>e.id===id);if(!entry)return{exitCode:1,output:"",error:`Unknown CANON rule: ${id}
|
|
13
|
+
`};if(format==="json"){const payload={code:entry.id,category:"CANON",summary:entry.title,rule:entry.rule,why:entry.why,enforcement:entry.enforcement,sourceIssues:entry.sourceIssues,promotedTo:entry.promotedTo};return{exitCode:0,output:JSON.stringify(payload,null,2),error:""}}const lines=["",`${entry.id} \u2014 ${entry.title}`,"",` Rule: ${entry.rule}`,"",` Why: ${entry.why}`,"",` Enforcement: ${entry.enforcement}`,""];return entry.sourceIssues&&lines.push(` Source issues: ${entry.sourceIssues}`,""),entry.promotedTo&&lines.push(` Promoted to: ${entry.promotedTo}`,""),{exitCode:0,output:lines.join(`
|
|
14
|
+
`),error:""}}function explainError(code,format){const entry=ERROR_CATALOG.get(code);if(!entry)return{exitCode:1,output:"",error:`Unknown error code: ${code}
|
|
15
|
+
`};if(format==="json"){const payload={code:entry.code,category:"ERROR",summary:entry.summary,detail:entry.detail,recovery:entry.recovery,docUrl:entry.docUrl};return{exitCode:0,output:JSON.stringify(payload,null,2),error:""}}const lines=["",`${entry.code} \u2014 ${entry.summary}`,"",` ${entry.detail}`,""," Recovery:",` ${entry.recovery}`,""];return entry.docUrl&&lines.push(` See: ${entry.docUrl}`,""),{exitCode:0,output:lines.join(`
|
|
16
|
+
`),error:""}}function explainFlag(key,format){const entry=FLAG_CATALOG.get(key);if(!entry)return{exitCode:1,output:"",error:`Unknown flag: ${key}
|
|
17
|
+
`};if(format==="json"){const payload={code:key,category:"FLAG",summary:entry.summary,detail:entry.detail};return{exitCode:0,output:JSON.stringify(payload,null,2),error:""}}return{exitCode:0,output:["",`${key} \u2014 ${entry.summary}`,"",` ${entry.detail}`,""," Run `arbiter explain --list` to see all codes.",""].join(`
|
|
18
|
+
`),error:""}}function listAll(format){const errorCodes=Array.from(ERROR_CATALOG.keys()),invCodes=INVARIANT_CATALOG.map(i=>i.id),projInvariants=loadProjectInvariants(),canonEntries=loadCanonEntries(CANON_MD_PATH),canonCodes=canonEntries.map(e=>e.id);if(format==="json"){const items=[...errorCodes.map(c=>{const e=ERROR_CATALOG.get(c);return{code:c,category:"ERROR",summary:e?.summary??""}}),...INVARIANT_CATALOG.map(i=>({code:i.id,category:"INV",summary:i.title})),...projInvariants.map(i=>({code:i.id,category:"PROJ",summary:i.title})),...canonEntries.map(c=>({code:c.id,category:"CANON",summary:c.title})),...Array.from(FLAG_CATALOG.entries()).map(([code,e])=>({code,category:"FLAG",summary:e.summary}))];return{exitCode:0,output:JSON.stringify(items,null,2),error:""}}const flagCodes=Array.from(FLAG_CATALOG.keys());return{exitCode:0,output:["","ERROR codes:",...errorCodes.map(c=>` ${c}`),"","INV codes:",...invCodes.map(c=>` ${c}`),...projInvariants.length>0?["","PROJ codes (project-declared):"]:[],...projInvariants.map(i=>` ${i.id}`),"","CANON rules:",...canonCodes.map(c=>` ${c}`),"","FLAG codes (wizard answers):",...flagCodes.map(c=>` ${c}`),"","Run `arbiter explain <code>` for details on any entry.",""].join(`
|
|
19
|
+
`),error:""}}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync}from"node:fs";import{closeDescriptorTranslated,ensureDir,openAppendDescriptorTranslated,unlinkTranslated}from"../utils/fs.js";import{createHash,randomBytes}from"node:crypto";import{tmpdir}from"node:os";import{join,resolve}from"node:path";import{runCli,runInteractive,CliError}from"../utils/run-cli.js";import{ArbiterError}from"../utils/errors.js";export function deriveGateKey(dir){const commonDir=runCli("git",["rev-parse","--git-common-dir"],{cwd:dir}).stdout.trim(),absolute=resolve(dir,commonDir);return createHash("sha256").update(absolute).digest("hex").slice(0,16)}export function gateLockPath(key,env=process.env){const base=env.XDG_RUNTIME_DIR??tmpdir(),dir=join(base,"arbiter");return ensureDir(dir),join(dir,`${key}-gate.lock`)}export function assertFlockAvailable(env=process.env){try{runCli("flock",["--version"],{env,timeoutMs:1e4})}catch(e){const detail=e instanceof CliError&&e.notFound?"not found on PATH":"not runnable";throw ArbiterError.fromKey("E_GATE_MUTEX_UNSUPPORTED","errors.E_GATE_MUTEX_UNSUPPORTED",{detail})}}export function gateExecArgv(lockPath,cmdArgs){return["flock","-o","--",lockPath,...cmdArgs]}const CHILD_LOCK_FD=3,DESCENDANT_SENTINEL_FD=4,SUPERVISOR_SCRIPT=`exec ${DESCENDANT_SENTINEL_FD}>>"$1" || exit $?; shift; flock ${CHILD_LOCK_FD} || exit $?; (exec ${CHILD_LOCK_FD}>&-; exec "$@")`;export function gateQueueAdvisory(dir,lockPath){const helperPath=resolve(dir,"scripts","lib","waiter-count.mjs");if(!existsSync(helperPath))return null;try{const{stdout}=runCli("node",[helperPath,lockPath],{timeoutMs:5e3}),count=Number(stdout.trim());return!Number.isFinite(count)||count<2?null:`gate-exec: ${count} process(es) already queued on this mutex \u2014 CI is authoritative; consider pushing instead of waiting: ARBITER_PREPUSH_BYPASS=true ARBITER_PREPUSH_BYPASS_REASON="<reason>" git push (see docs/REFERENCE/api.md)`}catch{return null}}export async function runGateExec(opts){const dir=opts.dir??process.cwd();assertFlockAvailable();const key=opts.key??deriveGateKey(dir),lockPath=gateLockPath(key);process.stderr.write(`gate-exec: mutex ${lockPath} (blocking until free)
|
|
3
|
+
`);const advisory=gateQueueAdvisory(dir,lockPath);advisory&&process.stderr.write(`${advisory}
|
|
4
|
+
`);const lockFd=openAppendDescriptorTranslated(lockPath),sentinelPath=`${lockPath}.${process.pid}-${randomBytes(4).toString("hex")}.sentinel`;try{const{exitCode}=await runInteractive("sh",["-c",SUPERVISOR_SCRIPT,"gate-exec-supervisor",sentinelPath,...opts.cmdArgs],{cwd:dir,env:{...process.env,ARBITER_GATE_MUTEX_HELD:lockPath},extraFds:[lockFd],detached:!0,teardownProcessGroupOnSignal:!0,teardownOnParentSignal:!0,trackedDescendantFdPath:sentinelPath});return exitCode}finally{closeDescriptorTranslated(lockFd,lockPath),existsSync(sentinelPath)&&unlinkTranslated(sentinelPath)}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{fileURLToPath}from"node:url";import{resolve}from"node:path";import{runCli,CliError}from"../utils/run-cli.js";import{detectBrownfieldClass}from"../kit/brownfield-detect.js";import{paint,colorEnabled,asciiOnly}from"../utils/tty.js";import{jsonOutput}from"../utils/json-output.js";function packageRoot(){return resolve(fileURLToPath(import.meta.url),"../../..")}function resolveClass(repo,override){if(override)return override;try{return detectBrownfieldClass(repo,"multi").brownfieldClass}catch{return"gold"}}function renderReport(p){const lines=[],b=p.level,toNext=b.nextLevel===null?"":` \xB7 ${b.toNextLevel} to ${b.nextLevel}`;if(lines.push(`gold-audit: ${b.level} (${b.brownfieldClass}) \xB7 score ${p.score}${toNext} \xB7 Y ${p.yCount}/${p.totals.checks} \xB7 RISKY ${p.riskyCount}`),p.gaps.length===0)lines.push(" what's missing: nothing \u2014 all applicable checks are Y.");else{lines.push(` what's missing (${p.gaps.length} family/families):`);for(const g of p.gaps){lines.push(` ${g.dimension}:`);for(const c of g.checks){const file=c.evidence?.file??"",detail=c.evidence?.detail?`: ${c.evidence.detail}`:"",ev=file||detail?` [${file}${detail}]`:"";lines.push(` ${c.verdict} ${c.id} ${c.title}${ev}`)}}}return lines.join(`
|
|
3
|
+
`)+`
|
|
4
|
+
`}const COCKPIT_GLYPH={unicode:{Y:"\u{1F7E2}",P:"\u{1F7E1}",N:"\u{1F534}",NA:"\xB7",NV:"\u2754"},ascii:{Y:"Y",P:"P",N:"N",NA:".",NV:"?"}},VERDICT_COLOR={Y:"green",P:"yellow",N:"red",NA:"dim",NV:"cyan"},FRESH_COLOR={FRESH:"green",PARTIAL:"yellow",STALE:"red"};function sanitize(s,ascii=!1){const noCtrl=(typeof s=="string"?s:"").replace(/[\x00-\x1f\x7f\u0085\u2028\u2029]/g,"");return ascii?noCtrl.replace(/[^\x20-\x7e]/g,""):noCtrl}function num(n){const x=Number(n);return Number.isFinite(x)?x:0}function clampScore(n){const x=Number(n);return Number.isFinite(x)?Math.max(0,Math.min(100,x)):0}function displayScore(n){return String(Math.round(clampScore(n)*10)/10)}function scoreColor(score){return score>=75?"green":score>=50?"yellow":"red"}function scoreBar(score,w,ascii){const filled=Math.max(0,Math.min(w,Math.round(clampScore(score)/100*w))),fill=ascii?"#":"\u2588",empty=ascii?".":"\u2591";return fill.repeat(filled)+empty.repeat(w-filled)}function verdictCell(verdict,ascii){const glyphs=ascii?COCKPIT_GLYPH.ascii:COCKPIT_GLYPH.unicode,v=typeof verdict=="string"?verdict:"NA",key=Object.hasOwn(glyphs,v)?v:"NA";return{glyph:glyphs[key]??glyphs.NA??"?",color:VERDICT_COLOR[key]??"dim"}}export function renderCockpit(p,fresh,opts){const{color,ascii}=opts,glyphs=ascii?COCKPIT_GLYPH.ascii:COCKPIT_GLYPH.unicode,sep=ascii?" | ":" \xB7 ",lines=[];if(fresh&&num(fresh.counts.total)>0){const c=fresh.counts,statusText=sanitize(fresh.status,ascii);lines.push(paint(`DATA ${statusText}`,FRESH_COLOR[statusText]??"dim",color)+`${sep}${num(c.fresh)}/${num(c.total)} report(s) within ${num(fresh.staleHours)}h`)}const b=p.level,toNext=b.nextLevel===null?"max level":`${num(b.toNextLevel)} to ${sanitize(b.nextLevel,ascii)}`;lines.push(paint(sanitize(b.level,ascii),scoreColor(clampScore(p.score)),color)+` (${sanitize(b.brownfieldClass,ascii)})${sep}score ${displayScore(p.score)}${sep}${toNext}${sep}Y ${num(p.yCount)}/${num(p.totals.checks)}`),lines.push(" "+paint(scoreBar(p.score,24,ascii),scoreColor(clampScore(p.score)),color)+` ${displayScore(p.score)}`),lines.push(""),lines.push("DIMENSIONS");const byDim=new Map;for(const c of p.checks){const dimKey=typeof c.dimension=="string"?c.dimension:"",arr=byDim.get(dimKey);arr?arr.push(c):byDim.set(dimKey,[c])}const rows=[...new Set([...Object.keys(p.dimensions),...byDim.keys()])].sort().map(id=>{const dim=p.dimensions[id]??{score:0,y:0},strip=(byDim.get(id)??[]).map(c=>{const cell=verdictCell(c.verdict,ascii);return paint(cell.glyph,cell.color,color)}).join(" ");return{label:sanitize(id,ascii),score:clampScore(dim.score),strip}}),w=rows.reduce((m,r)=>Math.max(m,r.label.length),0);for(const r of rows)lines.push(` ${r.label.padEnd(w)} ${paint(scoreBar(r.score,12,ascii),scoreColor(r.score),color)} ${displayScore(r.score).padStart(5)} ${r.strip}`);if(num(p.riskyCount)>0){lines.push("");const dash=ascii?"--":"\u2014";lines.push(paint(`RISKY: ${num(p.riskyCount)} check(s) ${dash} false-gap meta-gate (scoring suppressed under --strict)`,"red",color))}return lines.push(""),lines.push(`legend: ${glyphs.Y} Y ${glyphs.P} P ${glyphs.N} N ${glyphs.NA} NA ${glyphs.NV} NV`),lines.join(`
|
|
5
|
+
`)+`
|
|
6
|
+
`}function runGoldAuditCheck(repo,script,cls,opts){const args=[script,"--check","--class",cls];opts.requireBaseline&&args.push("--require-baseline"),opts.stack&&args.push("--stack",opts.stack);try{const{stdout,stderr}=runCli("node",args,{cwd:repo});return stdout&&process.stdout.write(stdout),stderr&&process.stderr.write(stderr),{exitCode:0,payload:null}}catch(err){return err instanceof CliError?(err.stdout&&process.stdout.write(err.stdout),err.stderr&&process.stderr.write(err.stderr),{exitCode:err.exitCode===2?2:1,payload:null}):(process.stderr.write(`gold-audit: engine failed \u2014 ${String(err)}
|
|
7
|
+
`),{exitCode:1,payload:null})}}function runGoldAuditCockpit(repo,script,cls,opts){const args=[script,"--cockpit-data","--class",cls];opts.stack&&args.push("--stack",opts.stack);let stdout;try{stdout=runCli("node",args,{cwd:repo}).stdout}catch(err){const detail=err instanceof CliError?err.message:String(err);return process.stderr.write(`gold-audit: engine failed \u2014 ${detail}
|
|
8
|
+
`),{exitCode:1,payload:null}}const text=stdout.trim();if(!text.startsWith("{"))return opts.quiet||process.stdout.write(text+`
|
|
9
|
+
`),{exitCode:0,payload:null};let env;try{env=JSON.parse(text)}catch(err){return process.stderr.write(`gold-audit: invalid cockpit JSON \u2014 ${err.message}
|
|
10
|
+
`),{exitCode:1,payload:null}}if(!opts.quiet){const ascii=asciiOnly(!!opts.ascii),color=colorEnabled()&&!ascii;try{process.stdout.write(renderCockpit(env.payload,env.freshness??null,{color,ascii}))}catch(err){return process.stderr.write(`gold-audit: could not render cockpit \u2014 ${err.message}
|
|
11
|
+
`),{exitCode:1,payload:env.payload}}}return{exitCode:0,payload:env.payload}}function emitGoldAuditResult(opts,result){opts.quiet||(opts.json?jsonOutput("gold-audit","ok",typeof result=="string"?{}:{...result}):typeof result=="string"?process.stdout.write(result+`
|
|
12
|
+
`):process.stdout.write(renderReport(result)))}export function runGoldAudit(opts={}){const repo=opts.repo?resolve(opts.repo):process.cwd(),cls=resolveClass(repo,opts.class),script=resolve(packageRoot(),"scripts/gold-audit.mjs");if(opts.check)return runGoldAuditCheck(repo,script,cls,opts);if(opts.cockpit||opts.ascii)return runGoldAuditCockpit(repo,script,cls,opts);const args=["--json","--class",cls];opts.stack&&args.push("--stack",opts.stack);let stdout;try{stdout=runCli("node",[script,...args],{cwd:repo}).stdout}catch(err){const detail=err instanceof CliError?err.message:String(err);return opts.json?jsonOutput("gold-audit","error",{},[`gold-audit: engine failed \u2014 ${detail}`]):process.stderr.write(`gold-audit: engine failed \u2014 ${detail}
|
|
13
|
+
`),{exitCode:1,payload:null}}const text=stdout.trim();if(!text.startsWith("{"))return emitGoldAuditResult(opts,text),{exitCode:0,payload:null};let payload;try{payload=JSON.parse(text)}catch(err){const message=`gold-audit: engine emitted invalid JSON \u2014 ${err.message}`;return opts.json?jsonOutput("gold-audit","error",{},[message]):process.stderr.write(message+`
|
|
14
|
+
`),{exitCode:1,payload:null}}return emitGoldAuditResult(opts,payload),{exitCode:0,payload}}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync,readFileSync,statSync}from"node:fs";import{dirname,join,resolve}from"node:path";import{ensureDir,writeFileTranslated}from"../utils/fs.js";import{INVARIANT_CATALOG}from"../invariants/catalog.js";import{loadConfig}from"../utils/config.js";import{buildInvNodes}from"../graph/builders/inv.js";import{buildAdrNodes}from"../graph/builders/adr.js";import{buildReqNodes}from"../graph/builders/req.js";import{buildAstNodes}from"../graph/builders/ast.js";import{buildTestNodes}from"../graph/builders/test-nodes.js";import{buildEvidenceNodes}from"../graph/builders/evidence.js";import{buildCanonNodes}from"../graph/builders/canon.js";import{GraphStore}from"../graph/store.js";import{toDot,toMermaid,toJson}from"../graph/query.js";export const GRAPH_RELATIVE_PATH=join(".arbiter","graph.json");export function runGraphBuild(opts={}){const dir=resolve(opts.dir??"."),format=opts.format??"json",defaultExt=format==="json"?"graph.json":`graph.${format}`,defaultOut=join(dir,".arbiter",defaultExt),outPath=opts.output!==void 0?resolve(opts.output):defaultOut,store=new GraphStore,stored=loadConfig(dir),invariants=stored?.governance?.projectInvariants!==void 0&&stored.governance.projectInvariants.length>0?[...INVARIANT_CATALOG,...stored.governance.projectInvariants]:INVARIANT_CATALOG;buildInvNodes(invariants,store),buildAdrNodes(store,{},dir),buildReqNodes(store,{},dir),buildAstNodes(store,{},dir),buildTestNodes(store,{},dir),buildEvidenceNodes(store,{},dir),buildCanonNodes(store,{},dir);const snapshot=store.snapshot();let rendered;format==="dot"?rendered=toDot(snapshot)+`
|
|
3
|
+
`:format==="mermaid"?rendered=toMermaid(snapshot)+`
|
|
4
|
+
`:rendered=toJson(snapshot)+`
|
|
5
|
+
`;try{ensureDir(dirname(outPath)),writeFileTranslated(outPath,rendered)}catch(err){return{status:"error",exitCode:2,path:outPath,nodes:snapshot.nodes.length,edges:snapshot.edges.length,reason:`failed to write ${outPath}: ${err instanceof Error?err.message:String(err)}`}}return{status:"ok",exitCode:0,path:outPath,nodes:snapshot.nodes.length,edges:snapshot.edges.length}}export function runVerifyGraph(opts={}){const dir=resolve(opts.dir??"."),inPath=opts.input!==void 0?resolve(opts.input):join(dir,GRAPH_RELATIVE_PATH),loadResult=loadSnapshot(inPath);if("reason"in loadResult)return makeError(inPath,[],0,[],loadResult.reason);const snapshot=loadResult.snapshot,brokenRefFailures=checkBrokenRefs(snapshot),store=buildStoreFromSnapshot(snapshot),invNodes=store.nodesByKind("INV"),orphanInvFailures=checkOrphanInvariants(invNodes,store),orphanNodeFailures=checkOrphanNodes(store),missingEvidenceFailures=checkMissingEvidence(invNodes,store),staleProverFailures=checkStaleProvers(store,dir),allFailures=[...brokenRefFailures,...orphanInvFailures,...orphanNodeFailures,...missingEvidenceFailures,...staleProverFailures],orphans=orphanInvFailures.map(f=>({id:f.id,reason:f.reason}));return allFailures.length>0?{status:"error",exitCode:2,path:inPath,orphans,totalInv:invNodes.length,failures:allFailures,reason:summarizeFailures(allFailures)}:{status:"ok",exitCode:0,path:inPath,orphans:[],totalInv:invNodes.length,failures:[]}}function loadSnapshot(inPath){if(!existsSync(inPath))return{reason:`graph snapshot not found at ${inPath} \u2014 run \`arbiter graph build\` first`};let raw;try{raw=readFileSync(inPath,"utf-8")}catch(err){return{reason:`failed to read ${inPath}: ${err instanceof Error?err.message:String(err)}`}}let parsed;try{parsed=JSON.parse(raw)}catch(err){return{reason:`failed to parse ${inPath}: ${err instanceof Error?err.message:String(err)}`}}if(parsed===null||typeof parsed!="object")return{reason:`${inPath}: expected JSON object`};const obj=parsed;return!Array.isArray(obj.nodes)||!Array.isArray(obj.edges)?{reason:`${inPath}: missing nodes/edges arrays`}:{snapshot:parsed}}function buildStoreFromSnapshot(snapshot){const nodeIds=new Set(snapshot.nodes.map(n=>n.id)),store=new GraphStore;for(const node of snapshot.nodes)store.upsertNode(node);for(const edge of snapshot.edges)nodeIds.has(edge.from)&&nodeIds.has(edge.to)&&store.addEdge(edge);return store}function checkBrokenRefs(snapshot){const nodeIds=new Set(snapshot.nodes.map(n=>n.id)),failures=[];for(const edge of snapshot.edges){const edgeId=`${edge.from} -[${edge.kind}]-> ${edge.to}`;nodeIds.has(edge.from)||failures.push({kind:"broken-ref",id:edgeId,reason:`edge 'from' node "${edge.from}" does not exist`}),nodeIds.has(edge.to)||failures.push({kind:"broken-ref",id:edgeId,reason:`edge 'to' node "${edge.to}" does not exist`})}return failures}function checkOrphanInvariants(invNodes,store){return invNodes.filter(inv=>store.outgoing(inv.id,"enforces").length===0&&store.incoming(inv.id,"implements").length===0).map(inv=>({kind:"orphan-invariant",id:inv.id,reason:"no `enforces` edge and no incoming `implements` edge"}))}function checkOrphanNodes(store){return store.allNodes().filter(node=>node.kind!=="GATE"&&store.outgoing(node.id).length===0&&store.incoming(node.id).length===0).map(node=>({kind:"orphan-node",id:node.id,reason:`${node.kind} node has no edges`}))}function checkMissingEvidence(invNodes,store){const gateEvidenceEdges=store.allEdges().filter(e=>e.kind==="produces");if(gateEvidenceEdges.length===0)return[];const gatesWithEvidence=new Set(gateEvidenceEdges.map(e=>e.from)),failures=[];for(const inv of invNodes)for(const edge of store.outgoing(inv.id,"enforces"))if(!gatesWithEvidence.has(edge.to)){failures.push({kind:"missing-evidence",id:inv.id,reason:`INV enforces GATE "${edge.to}" but that GATE has no EVIDENCE node via produces edge`});break}return failures}function checkStaleProvers(store,dir){const failures=[];for(const testNode of store.nodesByKind("TEST")){const testPath=testNode.attrs.path;if(typeof testPath!="string")continue;let exists=!1;try{statSync(join(dir,testPath)),exists=!0}catch{}exists||failures.push({kind:"stale-prover",id:testNode.id,reason:`TEST node path "${testPath}" does not exist on disk`})}return failures}function makeError(path,orphans,totalInv,failures,reason){return{status:"error",exitCode:2,path,orphans,totalInv,failures,reason}}function summarizeFailures(failures){const byKind={};for(const f of failures){const bucket=byKind[f.kind]??[];bucket.push(f.id),byKind[f.kind]=bucket}return Object.entries(byKind).map(([k,ids])=>`${ids.length} ${k}(${ids.join(", ")})`).join(", ")}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DEFAULT_THRESHOLDS}from"../../config/schema.js";import{resolveCollaborationMode}from"../../config/collaboration-mode-defaults.js";function buildLanguageField(config){return config.language!=="unknown"?{language:config.language}:{}}function buildDatabaseFields(config){return{hasDatabase:config.hasDatabase,...config.databaseEngine!==void 0?{databaseEngine:config.databaseEngine}:{}}}function buildProviderFields(config){return{...config.observability!==void 0?{observability:config.observability}:{},...config.auth!==void 0?{auth:config.auth}:{},...config.frontend!==void 0?{frontend:config.frontend}:{}}}function buildCollapsedAxisFields(config){return{...config.runnerProfile!==void 0&&config.runnerProfile!=="fleet"?{runnerProfile:config.runnerProfile}:{},...config.industryOverlay!==void 0&&config.industryOverlay!=="none"?{industryOverlay:config.industryOverlay}:{},...config.deployTarget!==void 0&&config.deployTarget!=="none"?{deployTarget:config.deployTarget}:{}}}function buildOptionalAxisFields(config){return{...config.evidenceRetention!==void 0?{evidenceRetention:config.evidenceRetention}:{},...config.thresholdProfile!==void 0?{thresholdProfile:config.thresholdProfile}:{},...config.strictnessTier!==void 0?{strictnessTier:config.strictnessTier}:{},...buildCollapsedAxisFields(config),...config.basePackage!==void 0?{basePackage:config.basePackage}:{},...config.taxonomy!==void 0?{taxonomy:config.taxonomy}:{}}}function buildCollaborationOverrides(config){return{collaborationMode:resolveCollaborationMode(config),...config.solo!==void 0?{solo:config.solo}:{},...config.branchingStrategy!==void 0?{branchingStrategy:config.branchingStrategy}:{},automation:config.automation??{autonomy:"L0"}}}export function buildArbiterConfig(config){const level=config.governanceLevel,backend=config.decompositionBackend??(config.useGitHub?"github":"markdown");return{version:"0.2",projectName:config.projectName,tools:config.tools,governanceLevel:level,permitGitHub:config.useGitHub,decomposition:{backend},...buildLanguageField(config),packageManager:config.packageManager??"npm",features:{debtGates:config.enableDebtGates,suppressions:config.enableSuppressions,securityScanning:config.enableSecurityScanning,mutationTesting:config.enableMutationTesting!==!1,contractTesting:config.enableContractTesting!==!1,evidenceHarness:config.enableEvidenceHarness===!0,selfValidationHarness:config.enableSelfValidationHarness!==!1,auditToolchain:config.enableAuditToolchain===!0,fiveLaneCi:config.enableFiveLaneCi===!0,soloDevMode:config.enableSoloDevMode===!0,mcpFallback:config.enableMcpFallback===!0,noSkippedTests:config.enableNoSkippedTests!==!1,riskRegister:config.enableRiskRegister===!0,operationsHandbook:config.enableOperationsHandbook===!0,iso27001Mapping:config.enableIso27001Mapping===!0,nis2Mapping:config.enableNis2Mapping===!0,gdprMapping:config.enableGdprMapping===!0,codeownersNotify:config.enableCodeownersNotify===!0,taxonomy25d:config.enableTaxonomy25d===!0,perfTesting:config.enablePerfTesting===!0},...buildCollaborationOverrides(config),thresholds:config.thresholds??DEFAULT_THRESHOLDS[level],invariantTiers:config.invariantTiers,archetype:config.archetype,architectureStyle:config.architectureStyle,isMultiTenant:config.isMultiTenant,...buildDatabaseFields(config),hasPublicApi:config.hasPublicApi,...config.acceptBetaTools===!0?{acceptBetaTools:!0}:{},contractType:config.contractType,...buildOptionalAxisFields(config),...config.lanes.length>0?{lanes:config.lanes}:{},...config.taskTiers!==void 0?{taskTiers:config.taskTiers}:{},...buildProviderFields(config),...config.crossModelReview!==void 0?{crossModelReview:config.crossModelReview}:{},...config.preset!==void 0&&config.preset!=="none"?{preset:config.preset}:{},...config.projectInvariants!==void 0&&config.projectInvariants.length>0?{governance:{projectInvariants:config.projectInvariants,...config.liveSsot!==void 0?{liveSsot:config.liveSsot}:{}}}:config.liveSsot!==void 0?{governance:{liveSsot:config.liveSsot}}:{}}}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync,readFileSync}from"node:fs";import{resolve,join,normalize,isAbsolute,relative,sep,basename}from"node:path";import{ArbiterError}from"../../utils/errors.js";import{t}from"../../i18n/index.js";import{getLogger}from"../../utils/logger.js";import{levelAtLeast}from"../../config/levels.js";import{buildRegistry,runGeneratorsFromRegistry}from"../../generators/registry.js";import{loadPlugin}from"../../utils/plugin-loader.js";import{renderFromAbsPath}from"../../utils/render.js";import{copyFileTranslated,unlinkTranslated,writeFile}from"../../utils/fs.js";import{runCli,CliError}from"../../utils/run-cli.js";import{buildArbiterConfig}from"./build-arbiter-config.js";import{runBackendSetup}from"./github-setup.js";import{loadConfig,saveConfig}from"../../utils/config.js";import{beginGenerationSession,endGenerationSession}from"../../utils/fs.js";import{loadGeneratedManifest,saveGeneratedManifest}from"../../state/generated-manifest.js";import{buildAdoptPredicate,recordLocalOverride}from"../adopt-policy.js";import{detectInstalledSkills}from"../../integrations/skill-detector.js";import{computeSkipReport,excludeOwnEmittedSkills}from"../../generators/skills.js";import{jsonOutput,statusToExitCode}from"../../utils/json-output.js";export function runGenerators(config){return runGeneratorsFromRegistry(buildRegistry(config),[],{dryRun:!1})}export async function generateAndFinalize(args){const{config,targetDir,initOptions,log,brownfieldDetected,packageManager}=args;log(`
|
|
3
|
+
Generating...`);const committed=[];try{const execution=await executeInitGeneration({config,targetDir,dryRun:!1,adoptGovernance:initOptions.adoptGovernance===!0,onAdopt:(key,priorContent,newContent)=>{recordLocalOverride(targetDir,{key,priorContent,newContent})}}),{results,errors:generatorErrors,mergedConfig,plugins}=execution;writeSkillAudit(targetDir,execution.installedSkills),saveGeneratedManifest(targetDir,{...execution.prevManifest,...execution.generatedHashes}),committed.push(...results);const newConfig=buildArbiterConfig(mergedConfig),backendResult=runBackendSetup(config,log);await saveConfig(targetDir,newConfig),committed.push(...await runPlugins(targetDir,plugins,newConfig)),initOptions.json||printResults(committed,targetDir),printBrownfieldConflicts(committed,initOptions,log),assertEmittedFilesPresent(committed),printGreenfieldConflicts(committed,initOptions,log);const created=committed.filter(result=>result.action==="created").length,skipped=committed.filter(result=>result.action==="skipped").length,brownfieldWarning=brownfieldDetected&&!initOptions.brownfield?t("cli.init.brownfield_route"):void 0;brownfieldWarning!==void 0&&!initOptions.json&&log(`
|
|
4
|
+
${brownfieldWarning}`),log(`
|
|
5
|
+
Done! ${created} files created, ${skipped} skipped.`),maybeCaptureBaseline(config,targetDir,initOptions.brownfield,packageManager),activateGitHooks(targetDir,log);const setup=printInstallHint(config,targetDir,packageManager,initOptions.json);emitInitOutput(initOptions.json,generatorErrors.map(error=>`${error.key}: ${error.message}`),brownfieldWarning===void 0?backendResult.warnings:[...backendResult.warnings,brownfieldWarning],{created,skipped,setup})}catch(err){throw process.stderr.write(`
|
|
6
|
+
Generation failed \u2014 attempting rollback...
|
|
7
|
+
`),rollbackGeneration(committed),process.stderr.write(` Rollback complete. Review arbiter.json if it was partially written.
|
|
8
|
+
`),err}finally{endGenerationSession()}}function detectSkillsForPlan(targetDir){const claudeHome=process.env.HOME?`${process.env.HOME}/.claude`:"";return excludeOwnEmittedSkills(detectInstalledSkills({targetDir,claudeHome}))}function writeSkillAudit(targetDir,installedSkills){installedSkills.length!==0&&writeFile(join(targetDir,".arbiter","detected-integrations.json"),JSON.stringify({detectedSkills:installedSkills,skippedGenerators:computeSkipReport(installedSkills)},null,2)+`
|
|
9
|
+
`)}function printBrownfieldConflicts(committed,options,log){if(!options.brownfield||options.json)return;const conflicts=committed.filter(result=>result.action==="skipped");conflicts.length>0&&(log(`
|
|
10
|
+
Brownfield conflicts: ${conflicts.length} existing file(s) kept unchanged.`),log(` Use --force to replace them with arbiter governance files.
|
|
11
|
+
`))}function printGreenfieldConflicts(committed,options,log){if(options.brownfield||options.json)return;const skippedFiles=committed.filter(result=>result.action==="skipped"&&result.reason!=="not-applicable");if(skippedFiles.length>0){const names=skippedFiles.map(result=>basename(result.path)).join(", ");log(`
|
|
12
|
+
${skippedFiles.length} file(s) already exist: ${names}`),log(` Re-run with --force to overwrite existing files.
|
|
13
|
+
`)}}function emitInitOutput(json,errorLines,warnings,result){const{created,skipped,setup}=result;if(json){const status=errorLines.length>0?"error":warnings.length>0?"warning":"ok";jsonOutput("init",status,{created,skipped,...setup===void 0?{}:{nextSteps:[setup]}},errorLines.length>0?errorLines:void 0,warnings.length>0?{warnings}:void 0),status!=="ok"&&process.exit(statusToExitCode(status));return}errorLines.length>0&&(process.stdout.write(`
|
|
14
|
+
Generator failures (${errorLines.length}):
|
|
15
|
+
${errorLines.map(line=>` - ${line}`).join(`
|
|
16
|
+
`)}
|
|
17
|
+
|
|
18
|
+
See https://github.com/arbiter-framework/arbiter/issues/483 for context.
|
|
19
|
+
`),process.exit(statusToExitCode("error"))),process.stdout.write(`${t("cli.init.verify_hint")}
|
|
20
|
+
`)}function printInstallHint(config,targetDir,packageManager,json){const manager=packageManager??config.packageManager??"npm";if(existsSync(join(targetDir,"node_modules","@getarbiter","cli","dist","cli.js")))return localCliInstallStep(targetDir,manager,json);const callerSpec="$arbiter_spec",command=manager==="pnpm"?`pnpm add --save-dev --save-exact "${callerSpec}"`:manager==="yarn"?`yarn add --dev --exact "${callerSpec}"`:manager==="bun"?`COREPACK_ENABLE_PROJECT_SPEC=0 bun add --dev --exact --trust "${callerSpec}"`:`npm install --save-dev --save-exact "${callerSpec}"`,setup={command,requiresUserValue:!0};return json!==!0&&process.stdout.write(`${t("cli.init.local_arbiter_setup",{setupCommand:command})}
|
|
21
|
+
`),setup}function localCliInstallStep(targetDir,manager,json){const missing=missingDevDependencies(targetDir);if(missing!==null&&missing.length===0)return;const installCommand=`${manager} install`,key=missing===null?"cli.init.install_first_hint_unreadable":"cli.init.install_first_hint";return json!==!0&&process.stdout.write(`${t(key,{installCommand})}
|
|
22
|
+
`),{command:installCommand,requiresUserValue:!1}}function missingDevDependencies(targetDir){try{const manifest=JSON.parse(readFileSync(join(targetDir,"package.json"),"utf-8"));return Object.keys(manifest.devDependencies??{}).filter(name=>!existsSync(join(targetDir,"node_modules",name,"package.json")))}catch{return null}}function activateGitHooks(targetDir,log){if(!existsSync(join(targetDir,".githooks","pre-commit")))return;let current="";try{current=runCli("git",["config","--get","core.hooksPath"],{cwd:targetDir}).stdout.trim()}catch(err){if(err instanceof CliError&&err.notFound)return}if(current!==".githooks"){if(current!==""){getLogger().warn("init.hookspath_external",{current},`core.hooksPath is set to '${current}'; arbiter's hooks (.githooks) are NOT active. To use the gate guards, run: git config core.hooksPath .githooks`);return}try{runCli("git",["config","core.hooksPath",".githooks"],{cwd:targetDir}),log(" \u2713 Git hooks activated (core.hooksPath \u2192 .githooks) \u2014 the gate now guards every commit and push.")}catch{getLogger().warn("init.hookspath_set_failed",{},"Could not set core.hooksPath automatically. Activate manually: git config core.hooksPath .githooks")}}}function assertPathContained(resolvedPath,safeRoot,rawPath){if(!resolvedPath.startsWith(safeRoot+sep))throw new Error(`Plugin output path escapes target directory: ${rawPath}`)}export async function collectPluginInvariants(targetDir,plugins){const all=[];for(const pkg of plugins)try{const plugin=await loadPlugin(pkg,targetDir);plugin.invariants!==void 0&&all.push(...plugin.invariants)}catch(err){getLogger().warn("init.plugin_invariants_unavailable",{plugin:pkg},`Plugin "${pkg}" invariants unavailable: ${err instanceof Error?err.message:String(err)}`)}return all}export function mergeProjectInvariants(config,pluginInvariants){if(pluginInvariants.length===0)return config;const configIds=new Set((config.projectInvariants??[]).map(inv=>inv.id)),pluginOnly=pluginInvariants.filter(inv=>!configIds.has(inv.id));return{...config,projectInvariants:[...config.projectInvariants??[],...pluginOnly]}}export async function runPlugins(targetDir,plugins,storedConfig){const all=[],writtenPaths=new Set,failures=[];for(const pkg of plugins)try{const plugin=await loadPlugin(pkg,targetDir);if(plugin.detect&&!await plugin.detect(storedConfig))continue;const ctx={config:storedConfig,targetDir,renderTemplate(relPath,data){const safe=normalize(relPath);if(safe.startsWith("..")||isAbsolute(safe))throw new Error(`Invalid plugin template path: ${relPath}`);return renderFromAbsPath(join(plugin.templateRoot,safe),data)}},result=await plugin.generate(ctx);if(!Array.isArray(result.files)){getLogger().warn("init.plugin_invalid_result",{plugin:pkg},`Plugin "${pkg}" returned invalid result (no files array). Skipping.`);continue}for(const file of result.files){if(writtenPaths.has(file.path)){getLogger().warn("init.plugin_conflict",{plugin:pkg,path:file.path},`Plugin "${pkg}" conflict: "${file.path}" already written by a prior plugin. Skipping.`),all.push({path:file.path,action:"skipped"});continue}const resolvedFilePath=resolve(targetDir,file.path);assertPathContained(resolvedFilePath,resolve(targetDir),file.path),writtenPaths.add(file.path),all.push(writeFile(resolvedFilePath,file.content,{backup:file.action==="backup-and-replace",skipIfExists:file.action==="skip"}))}}catch(err){failures.push(`Plugin "${pkg}": ${err instanceof Error?err.message:String(err)}`)}if(failures.length>0)throw ArbiterError.fromKey("E_INIT_PLUGIN_FAILURES","errors.E_INIT_PLUGIN_FAILURES",{failures:failures.join(`
|
|
23
|
+
`)});return all}async function buildInitGenerationPlan(config,targetDir){const installedSkills=detectSkillsForPlan(targetDir),storedBefore=loadConfig(targetDir),plugins=Array.isArray(storedBefore?.plugins)?storedBefore.plugins:[],mergedConfig=mergeProjectInvariants(config,await collectPluginInvariants(targetDir,plugins));return{mergedConfig,specs:buildRegistry(mergedConfig,installedSkills),installedSkills,plugins,prevManifest:loadGeneratedManifest(targetDir)}}export async function executeInitGeneration(args){const{config,targetDir,dryRun}=args,plan=await buildInitGenerationPlan(config,targetDir);beginGenerationSession({targetDir,prevHashes:plan.prevManifest,adoptPredicate:buildAdoptPredicate({adoptGovernance:args.adoptGovernance===!0}),...dryRun||args.onAdopt===void 0?{}:{onAdopt:args.onAdopt}});const errors=[];let results,generatedHashes;try{results=runGeneratorsFromRegistry(plan.specs,errors,{dryRun})}finally{generatedHashes=endGenerationSession()}return{...plan,results,errors,generatedHashes}}export async function computeDryRunPreview(config){const{results}=await executeInitGeneration({config,targetDir:config.targetDir,dryRun:!0}),preview={created:[],modified:[],skipped:[]};for(const result of results){if(result.reason==="not-applicable")continue;const rel=relative(config.targetDir,result.path);result.action==="created"?preview.created.push(rel):result.action==="skipped"?preview.skipped.push(rel):preview.modified.push(rel)}return preview}function errMsg(err){return err instanceof Error?err.message:String(err)}export function rollbackGeneration(results){const rollbackErrors=[];for(const result of results)if(result.action==="created")try{existsSync(result.path)&&unlinkTranslated(result.path)}catch(err){rollbackErrors.push(`Could not remove ${result.path}: ${errMsg(err)}`)}else if(result.action==="backed-up-and-replaced"){const backup=`${result.path}.arbiter-backup`;try{existsSync(backup)&&(copyFileTranslated(backup,result.path),unlinkTranslated(backup))}catch(err){rollbackErrors.push(`Could not restore backup for ${result.path}: ${errMsg(err)}`)}}if(rollbackErrors.length>0){process.stderr.write(` Rollback partial \u2014 manual cleanup required:
|
|
24
|
+
`);for(const e of rollbackErrors)process.stderr.write(` ${e}
|
|
25
|
+
`)}}export function maybeCaptureBaseline(config,targetDir,brownfield,packageManager){const fatal=levelAtLeast(config.governanceLevel,"L3")&&config.enableDebtGates;if(shouldCaptureBaseline(fatal,brownfield,config.enableDebtGates)){if(typescriptDependenciesAreMissing(config,targetDir)){reportMissingTypescriptDependencies(config,packageManager);return}runBrownfieldCapture(targetDir,fatal?{fatal:!0}:void 0)}}function shouldCaptureBaseline(fatal,brownfield,debtGatesEnabled){return fatal||brownfield&&debtGatesEnabled}function typescriptDependenciesAreMissing(config,targetDir){return config.language==="typescript"&&!existsSync(join(targetDir,"node_modules"))}function reportMissingTypescriptDependencies(config,packageManager){const installCommand=`${packageManager??config.packageManager??"npm"} install`;process.stdout.write(` Debt baseline NOT captured: node_modules is absent, so the collectors cannot measure this project yet. Run ${installCommand}, then: node scripts/capture-debt-baseline.mjs
|
|
26
|
+
`)}function runBrownfieldCapture(targetDir,opts={fatal:!1}){process.stdout.write(`${t("cli.init.capturing_baseline")}
|
|
27
|
+
`);try{runCli("node",["scripts/capture-debt-baseline.mjs"],{cwd:targetDir,timeoutMs:6e5}),process.stdout.write(`${t("cli.init.baseline_captured")}
|
|
28
|
+
`)}catch(err){const msg=err instanceof Error?err.message:String(err);opts.fatal&&(process.stderr.write(` GATE FAIL: Baseline capture failed (${msg}). Fix toolchain or run: node scripts/capture-debt-baseline.mjs
|
|
29
|
+
`),process.exit(1)),getLogger().warn("init.baseline_capture_failed",{err:msg},`Baseline capture failed (${msg}). Re-run manually: node scripts/capture-debt-baseline.mjs`)}}export function assertEmittedFilesPresent(results){const missing=results.filter(r=>r.action!=="skipped"&&r.action!=="dry-run").filter(r=>!existsSync(r.path)).map(r=>r.path);if(missing.length>0)throw new Error(`Emission integrity check failed \u2014 ${missing.length} file(s) reported written but not found on disk:
|
|
30
|
+
${missing.map(p=>` - ${p}`).join(`
|
|
31
|
+
`)}
|
|
32
|
+
This is a generator bug (content silently dropped). Re-run init or file an issue.`)}export function printResults(results,targetDir){for(const result of results){if(result.reason==="not-applicable")continue;const icon=result.action==="skipped"?"\u2502 ":"\u251C\u2500\u2500",label=result.excluded==="ignored"?" \u2014 skipped (.arbiterignore)":result.excluded==="deselected"?" \u2014 skipped (outside --only)":result.action==="skipped"?" (skipped \u2014 already exists)":result.action==="backed-up-and-replaced"?" (backed up + replaced)":"",relPath=result.path.replace(targetDir+"/","");process.stdout.write(`${t("cli.init.file_entry",{icon,relPath,label})}
|
|
33
|
+
`)}}export async function displayDryRunPreview(config){const preview=await computeDryRunPreview(config);if(process.stdout.write(`${t("cli.init.dry_run_notice_full")}
|
|
34
|
+
`),preview.created.length>0){process.stdout.write(`${t("cli.init.dry_run_create_header")}
|
|
35
|
+
`);for(const entry of preview.created)process.stdout.write(`${t("cli.init.dry_run_create_file",{entry})}
|
|
36
|
+
`)}if(preview.modified.length>0){process.stdout.write(`${t("cli.init.dry_run_modify_header")}
|
|
37
|
+
`);for(const entry of preview.modified)process.stdout.write(`${t("cli.init.dry_run_modify_file",{entry})}
|
|
38
|
+
`)}if(preview.skipped.length>0){process.stdout.write(`${t("cli.init.dry_run_skip_header")}
|
|
39
|
+
`);for(const entry of preview.skipped)process.stdout.write(`${t("cli.init.dry_run_skip_file",{entry})}
|
|
40
|
+
`)}process.stdout.write(`${t("cli.init.dry_run_deferred_note")}
|
|
41
|
+
`),process.stdout.write(`${t("cli.init.dry_run_run_hint")}
|
|
42
|
+
`)}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{join}from"node:path";import{FatalError,ConfigError}from"../../utils/errors.js";import{provisionLabels}from"../../github/labels.js";import{applyBranchProtection}from"../../github/branch-protection.js";import{createProjectBoard}from"../../github/project-board.js";import{resolveCollaborationMode}from"../../config/collaboration-mode-defaults.js";import{ensureDir}from"../../utils/fs.js";function throwOnFatalOrConfig(kind,msg,warnings){if(kind==="fatal")throw new FatalError("E_GH_FATAL",msg,{recoverableContext:warnings});if(kind==="config")throw new ConfigError("E_GH_NOT_INSTALLED",msg)}function setupLabels(owner,repo,warnings,log){const labelResult=provisionLabels(owner,repo);labelResult.created.length>0&&log(` \u2502 Created: ${labelResult.created.join(", ")}`),labelResult.updated.length>0&&log(` \u2502 Updated: ${labelResult.updated.join(", ")}`);for(const e of labelResult.classifiedErrors)log(` \u2502 Error: ${e.message}`),throwOnFatalOrConfig(e.kind,e.message,warnings),warnings.push(e.message)}function setupBranchProtection(owner,repo,collaborationMode,warnings,log){const bp=applyBranchProtection(owner,repo,collaborationMode);bp.applied?log(" \u2502 Branch protection applied."):bp.error?(bp.errorKind&&throwOnFatalOrConfig(bp.errorKind,`branch protection: ${bp.error}`,warnings),log(` \u2502 Skipped (requires admin access): ${bp.error}`),warnings.push(`branch protection skipped: ${bp.error}`)):log(" \u2502 Skipped (requires admin access)."),bp.repoSettingsError&&(log(` \u2502 Repo merge settings FAILED (INV-101): ${bp.repoSettingsError}`),warnings.push(`repo merge settings failed (INV-101): ${bp.repoSettingsError}`))}function setupProjectBoard(owner,repo,projectName,warnings,log){const pb=createProjectBoard(owner,repo,projectName);pb.created?log(` Project board created: ${pb.projectUrl}`):pb.error?log(` Skipped: ${pb.error}`):log(` Already exists: ${pb.projectUrl??"unknown"}`);for(const e of pb.classifiedErrors)throwOnFatalOrConfig(e.kind,e.message,warnings),log(` Warning: ${e.message}`),warnings.push(`project board: ${e.message}`)}export function runBackendSetup(config,log){if((config.decompositionBackend??(config.useGitHub?"github":"markdown"))==="github")return runGithubSetup(config,log);const workDir=join(config.targetDir,".arbiter","work");return ensureDir(workDir),log(`
|
|
3
|
+
Markdown backend: scaffolded .arbiter/work/`),{warnings:[]}}export function runGithubSetup(config,log=msg=>{process.stdout.write(`${msg}
|
|
4
|
+
`)}){if(!config.useGitHub||!config.githubOwner||!config.githubRepo)return{warnings:[]};const warnings=[];return log(`
|
|
5
|
+
GitHub setup...`),log(" \u251C\u2500\u2500 Provisioning labels..."),setupLabels(config.githubOwner,config.githubRepo,warnings,log),log(" \u251C\u2500\u2500 Applying branch protection to main..."),setupBranchProtection(config.githubOwner,config.githubRepo,resolveCollaborationMode(config),warnings,log),log(" \u2514\u2500\u2500 Creating project board..."),setupProjectBoard(config.githubOwner,config.githubRepo,config.projectName,warnings,log),{warnings}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{t}from"../../i18n/index.js";import{levelAtLeast}from"../../config/levels.js";import{buildRegistry}from"../../generators/registry.js";import{resolveStyle}from"../../generators/github.js";import{resolveServiceBucket}from"../../utils/render.js";import{isL3Allowed,hasMatrixCell}from"../../utils/maturity-check.js";import{validateCollaborationCoherence,validateLanguageArchetypeCoherence}from"../wizard/coherence.js";function capabilitiesForGenerator(key,config){switch(key){case"mutation":return[{feature:"mutation",language:config.language}];case"contract-testing":return[{feature:"contract",language:config.language}];case"coverage":return[{feature:"coverage",language:config.language}];case"security":return[{feature:"security",language:config.language}];case"debt-gates":return[{feature:"static_analysis",language:config.language}];case"behavioral-tests":return[{feature:"bdd",language:config.language}];case"archunit":return[{feature:"architecture",language:config.language}];case"frontend-quality":return[{feature:"style_tokens",language:"typescript"}];case"playwright-ts":return[{feature:"a11y",language:"typescript"},{feature:"e2e",language:"typescript"}];case"playwright-python":return[{feature:"a11y",language:"python"},{feature:"e2e",language:"python"}];case"github":return deriveWorkflowCapabilities(config);default:return[]}}function resolveWorkflowLanguages(language){return language==="multi"?["typescript","java"]:[language]}export function deriveWorkflowCapabilities(config){if(!levelAtLeast(config.governanceLevel,"L3"))return[];const c=workflowCtx(config),langs=resolveWorkflowLanguages(config.language);return WORKFLOW_DIM_RULES.filter(r=>r.emit(c)).flatMap(r=>r.dims).flatMap(feature=>langs.map(language=>({feature,language})))}const WORKFLOW_DIM_RULES=[{dims:["license_scan"],emit:()=>!0},{dims:["secret_scan"],emit:c=>c.secScanning||c.style!=="starter"||c.cm==="trunk-solo"},{dims:["container_scan"],emit:c=>c.isService||c.deploy},{dims:["sbom","binary_signing"],emit:c=>c.style!=="starter"||c.deploy},{dims:["provenance"],emit:c=>c.style!=="starter"},{dims:["fuzz"],emit:c=>c.isScheduled},{dims:["dast"],emit:c=>c.isScheduled&&c.isService||c.deploy}];function workflowCtx(config){const style=resolveStyle(config),cm=config.collaborationMode??"peer-review";return{style,cm,deploy:(config.deployTarget??"none")!=="none",isService:resolveServiceBucket(config.archetype)==="service",isScheduled:style!=="starter"&&cm!=="trunk-solo",secScanning:config.enableSecurityScanning}}export function deriveL3MaturityChecks(config,specs){const seen=new Set,checks=[];for(const spec of specs)if(spec.enabled)for(const cap of capabilitiesForGenerator(spec.key,config)){if(!hasMatrixCell(cap.language,cap.feature))continue;const dedupeKey=`${cap.feature}:${cap.language}`;seen.has(dedupeKey)||(seen.add(dedupeKey),checks.push(cap))}return checks}export function checkL3MaturityGates(config){if(!levelAtLeast(config.governanceLevel,"L3"))return;const accept=config.acceptBetaTools??!1,blocked=[];for(const{feature,language}of deriveL3MaturityChecks(config,buildRegistry(config))){const result=isL3Allowed(language,feature,accept);!result.allowed&&result.errorMessage&&blocked.push(` \u2022 ${result.errorMessage}`)}if(blocked.length>0){process.stderr.write(`${t("cli.init.gate_failed")}
|
|
3
|
+
`);for(const msg of blocked)process.stderr.write(`${msg}
|
|
4
|
+
`);process.stderr.write(`${t("cli.init.accept_beta_hint")}
|
|
5
|
+
`),process.exit(1)}}export function checkCollaborationCoherenceGate(config){const langArch=validateLanguageArchetypeCoherence(config.language,config.archetype);if(langArch.severity==="WARN"&&process.stdout.write(`
|
|
6
|
+
\u26A0 ${langArch.message}
|
|
7
|
+
`),config.collaborationMode===void 0)return;const result=validateCollaborationCoherence(config.collaborationMode,config.governanceLevel);result.severity==="CRITICAL"&&(process.stderr.write(`${t("cli.init.coherence_gate_failed")}
|
|
8
|
+
`),process.stderr.write(` \u2022 ${result.message}
|
|
9
|
+
`),result.remediation!==void 0&&process.stderr.write(` ${result.remediation}
|
|
10
|
+
`),process.exit(1))}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{ArbiterError}from"../../utils/errors.js";import{detectLanguageWithSource,jvmRoot}from"../../detectors/language.js";import{detectBuildCommands}from"../../detectors/build.js";import{detectArchetypeHint,detectFramework}from"../../detectors/framework.js";import{detectGitInfo}from"../../detectors/git.js";import{detectExisting}from"../../detectors/existing.js";import{detectBasePackage}from"../../detectors/package.js";import{detectGithubAccess}from"../../detectors/github.js";import{detectExternalModel}from"../../detectors/external-model.js";import{getLanguageHooks}from"../../detectors/language-hooks.js";import{detectPackageManager}from"../../detectors/package-manager.js";import{detectLanes}from"../../detectors/lanes.js";import{runWizard}from"../../wizard/prompts.js";import{DEFAULT_THRESHOLDS}from"../../config/schema.js";import{levelAtLeast}from"../../config/levels.js";import{presetToTiers,defaultPresetForLevel}from"../../invariants/filter.js";import{applyPreset}from"../../wizard/presets.js";import{defaultContractType}from"../../wizard/archetype-defaults.js";import{SUPPORTED_AI_TOOLS}from"../../wizard/types.js";function githubAuthError(githubAccess){return ArbiterError.fromKey("E_INIT_GITHUB_UNAUTHENTICATED","errors.E_INIT_GITHUB_UNAUTHENTICATED",{error:githubAccess.error??"gh is not authenticated"})}function resolveUseGitHub(options,_recipe,githubAccess){if(options.backend!==void 0){if(options.backend==="github"&&!githubAccess.authenticated)throw githubAuthError(githubAccess);return options.backend==="github"}if(options.github){if(!githubAccess.authenticated)throw githubAuthError(githubAccess);return!0}const arbGhEnv=process.env.ARBITER_GITHUB;if(arbGhEnv!==void 0&&arbGhEnv!=="1"&&process.stderr.write(`Warning: ARBITER_GITHUB=${arbGhEnv} is not '1' \u2014 only ARBITER_GITHUB=1 activates GitHub API calls. Ignored.
|
|
3
|
+
`),arbGhEnv==="1"){if(!githubAccess.authenticated)throw githubAuthError(githubAccess);return!0}return!1}function buildNonInteractiveConfig(args){const{options,recipe,targetDir,projectName,language,framework,buildCmds,packageManager,gitInfo,existing,githubAccess,lanes}=args,useGitHub=resolveUseGitHub(options,recipe,githubAccess),{tools,governanceLevel}=resolveToolsAndLevel(options,recipe),config=buildDefaultConfig({targetDir,projectName,language:recipe?.language??language,framework:recipe&&"framework"in recipe?recipe.framework??null:framework,buildCmds,packageManager,gitInfo,existing,tools,governanceLevel,useGitHub,acceptBetaTools:options.acceptBetaTools??!1,lanes,...options.archetype!==void 0?{archetypeOverride:options.archetype}:{},solo:options.solo===!0});return recipe&&applyRecipeOverrides(config,recipe),options.deployTarget!==void 0&&(config.deployTarget=options.deployTarget),config}export function formatLangHint(locked,source){return locked?"":source?` (detected from ${source})`:" (no markers found)"}export function detectProjectForInit(targetDir,options,log){const languageOverride=parseLanguage(options.language),languageLocked=languageOverride!==void 0,languageDetection=languageOverride===void 0?detectLanguageWithSource(targetDir):null,language=languageOverride??languageDetection?.language??"unknown",languageSource=languageDetection?.source??null,framework=detectFramework(targetDir,language),buildCmds=detectBuildCommands(targetDir,language),packageManager=detectPackageManager(targetDir),gitInfo=detectGitInfo(targetDir),existing=detectExisting(targetDir),githubAccess=detectGithubAccess(),externalModelAccess=detectExternalModel("codex"),lanes=detectLanes(targetDir).lanes;return log(` \u251C\u2500\u2500 Language: ${language}${formatLangHint(languageLocked,languageSource)}${framework?` / ${framework}`:""}`),log(` \u251C\u2500\u2500 Build: ${buildCmds.buildTool}`),log(` \u251C\u2500\u2500 Package manager: ${packageManager.name} (${formatPackageManagerSource(packageManager)})${packageManager.isWorkspace?" \u2014 workspace":""}`),log(` \u251C\u2500\u2500 Git: ${gitInfo.isGitRepo?"yes":"no"}${gitInfo.githubRepo?` (${gitInfo.githubOwner}/${gitInfo.githubRepo})`:""}`),githubAccess.authenticated&&log(` \u251C\u2500\u2500 GitHub: authenticated as ${githubAccess.username??"unknown"}`),{language,languageLocked,languageSource,framework,buildCmds,packageManager,gitInfo,existing,githubAccess,externalModelAccess,lanes}}function formatPackageManagerSource(packageManager){if(packageManager.source==="packageManager-field")return"package.json#packageManager";if(packageManager.source==="default")return"default";switch(packageManager.name){case"pnpm":return"pnpm-lock.yaml";case"yarn":return"yarn.lock";case"bun":return"bun lockfile";case"npm":return"package-lock.json"}}function resolveToolsAndLevel(options,recipe){return{tools:options.tools?parseTools(options.tools):recipe?.tools??parseTools(void 0),governanceLevel:options.level?parseLevel(options.level):recipe?.governanceLevel??parseLevel(void 0)}}export async function resolveConfig(args){const{options,recipe,targetDir,projectName,language,framework,buildCmds,packageManager,gitInfo,existing,githubAccess,externalModelAccess,lanes,languageLocked,languageSource}=args,presetFastPath=options.preset!==void 0&&options.preset!=="none";if(options.yes||recipe!==void 0||presetFastPath)return buildNonInteractiveConfig({options,recipe,targetDir,projectName,language,framework,buildCmds,packageManager,gitInfo,existing,githubAccess,lanes});const wizardResult=await runWizard({targetDir,projectName,language,framework,buildCmds:{...buildCmds,packageManager},gitInfo,existing,githubAccess,externalModelAccess,detectedLanes:lanes,languageLocked,languageSource});return wizardResult===null?null:(wizardResult.basePackage===void 0&&Object.assign(wizardResult,detectedBasePackage(language,targetDir)),wizardResult.packageManager=packageManager,wizardResult)}function buildDefaultConfig(opts){const archetype=opts.archetypeOverride??detectArchetypeHint(opts.targetDir,opts.language,opts.framework)??"library",hasDatabase=archetype==="backend-web-db"||archetype==="data-pipeline",hasPublicApi=archetype==="backend-web-db";return{targetDir:opts.targetDir,projectName:opts.projectName,description:`${opts.projectName} project`,language:opts.language,framework:opts.framework,archetype,architectureStyle:"none",isMultiTenant:!1,hasDatabase,hasPublicApi,buildTool:opts.buildCmds.buildTool,packageManager:opts.packageManager,buildCommand:opts.buildCmds.buildCommand,testCommand:opts.buildCmds.testCommand,lintCommand:opts.buildCmds.lintCommand,formatCommand:opts.buildCmds.formatCommand,tools:opts.tools,governanceLevel:opts.governanceLevel,useGitHub:opts.useGitHub,decompositionBackend:opts.useGitHub?"github":"markdown",githubOwner:opts.gitInfo.githubOwner,githubRepo:opts.gitInfo.githubRepo,existing:opts.existing,languageHooks:getLanguageHooks(opts.language),enableDebtGates:opts.governanceLevel!=="L1",enableSuppressions:!0,enableSecurityScanning:opts.governanceLevel!=="L1",enableMutationTesting:levelAtLeast(opts.governanceLevel,"L3"),enableContractTesting:defaultContractType(archetype,hasPublicApi)!=="none",enableEvidenceHarness:opts.governanceLevel==="L4",enableSelfValidationHarness:!0,enableSoloDevMode:!1,collaborationMode:opts.solo===!0?"trunk-solo":"peer-review",automation:{autonomy:"L0"},invariantTiers:presetToTiers(defaultPresetForLevel(opts.governanceLevel)),acceptBetaTools:opts.acceptBetaTools??!1,contractType:defaultContractType(archetype,hasPublicApi),thresholds:DEFAULT_THRESHOLDS[opts.governanceLevel],lanes:opts.lanes??[],...detectedBasePackage(opts.language,opts.targetDir),...archetype==="frontend-spa"?inferFEFramework(opts.framework)!==void 0?{frontend:{framework:inferFEFramework(opts.framework)}}:{}:{}}}function inferFEFramework(framework){if(!framework)return;const lower=framework.toLowerCase();if(lower==="vue"||lower.endsWith("+vue")||lower.startsWith("vue+"))return"vue";if(lower==="svelte")return"svelte";if(lower==="react"||lower.includes("react"))return"react"}function applyRecipeOverrides(config,recipe){recipe.archetype!==void 0&&(config.archetype=recipe.archetype),recipe.architectureStyle!==void 0&&(config.architectureStyle=recipe.architectureStyle),recipe.isMultiTenant!==void 0&&(config.isMultiTenant=recipe.isMultiTenant),recipe.hasDatabase!==void 0&&(config.hasDatabase=recipe.hasDatabase),recipe.hasPublicApi!==void 0&&(config.hasPublicApi=recipe.hasPublicApi),recipe.enableDebtGates!==void 0&&(config.enableDebtGates=recipe.enableDebtGates),recipe.enableSuppressions!==void 0&&(config.enableSuppressions=recipe.enableSuppressions),recipe.enableSecurityScanning!==void 0&&(config.enableSecurityScanning=recipe.enableSecurityScanning),recipe.enableMutationTesting!==void 0&&(config.enableMutationTesting=recipe.enableMutationTesting),recipe.enableContractTesting!==void 0&&(config.enableContractTesting=recipe.enableContractTesting),recipe.enableSoloDevMode!==void 0&&(config.enableSoloDevMode=recipe.enableSoloDevMode),recipe.enableMcpFallback!==void 0&&(config.enableMcpFallback=recipe.enableMcpFallback),recipe.enableNoSkippedTests!==void 0&&(config.enableNoSkippedTests=recipe.enableNoSkippedTests),recipe.automation!==void 0&&(config.automation=recipeAutomation(recipe.automation)),applyRecipeAxisOverrides(config,recipe),applyRecipeCiOverrides(config,recipe),applyRecipeActivationOverrides(config,recipe)}function applyRecipeCiOverrides(config,recipe){recipe.enableAuditToolchain!==void 0&&(config.enableAuditToolchain=recipe.enableAuditToolchain),recipe.enableFiveLaneCi!==void 0&&(config.enableFiveLaneCi=recipe.enableFiveLaneCi)}function applyRecipeActivationOverrides(config,recipe){recipe.enableCodeownersNotify!==void 0&&(config.enableCodeownersNotify=recipe.enableCodeownersNotify),recipe.enableTaxonomy25d!==void 0&&(config.enableTaxonomy25d=recipe.enableTaxonomy25d),recipe.enablePerfTesting!==void 0&&(config.enablePerfTesting=recipe.enablePerfTesting),applyRecipeComplianceOverrides(config,recipe)}function applyRecipeComplianceOverrides(config,recipe){recipe.enableRiskRegister!==void 0&&(config.enableRiskRegister=recipe.enableRiskRegister),recipe.enableOperationsHandbook!==void 0&&(config.enableOperationsHandbook=recipe.enableOperationsHandbook),recipe.enableIso27001Mapping!==void 0&&(config.enableIso27001Mapping=recipe.enableIso27001Mapping),recipe.enableNis2Mapping!==void 0&&(config.enableNis2Mapping=recipe.enableNis2Mapping),recipe.enableGdprMapping!==void 0&&(config.enableGdprMapping=recipe.enableGdprMapping)}function applyRecipeAxisOverrides(config,recipe){recipe.databaseEngine!==void 0&&(config.databaseEngine=recipe.databaseEngine,config.hasDatabase=recipe.databaseEngine!=="none"),recipe.contractType!==void 0&&(config.contractType=recipe.contractType),recipe.lanes!==void 0&&(config.lanes=recipe.lanes),recipe.evidenceHarness!==void 0&&(config.enableEvidenceHarness=recipe.evidenceHarness),recipe.decomposition!==void 0&&(config.decompositionBackend=recipe.decomposition.backend)}function recipeAutomation(a){return{autonomy:a.autonomy,...a.maxParallelWorktrees!==void 0?{maxParallelWorktrees:a.maxParallelWorktrees}:{},...a.defaultGateLevel!==void 0?{defaultGateLevel:a.defaultGateLevel}:{}}}export function applyPresetOptions(options,config){applyPreset(options.preset??"none",config),options.authProvider!=null&&options.authProvider!=="none"&&(config.auth={provider:"none",...config.auth},config.auth.provider=options.authProvider),options.observabilityProvider!=null&&options.observabilityProvider!=="none"&&(config.observability={provider:"none",...config.observability},config.observability.provider=options.observabilityProvider)}function detectedBasePackage(language,targetDir){if(language!=="java"&&language!=="kotlin"&&language!=="multi")return{};const bp=detectBasePackage(jvmRoot(targetDir)??targetDir);return bp!==void 0?{basePackage:bp}:{}}function parseTools(tools){if(!tools)return["claude","codex"];const VALID=new Set(SUPPORTED_AI_TOOLS),parsed=tools.split(",").map(s=>s.trim()),invalid=parsed.filter(s=>!VALID.has(s));if(invalid.length>0)throw ArbiterError.fromKey("E_INVALID_TOOL","errors.E_INVALID_TOOL",{noun:invalid.length===1?"tool":"tools",tool:invalid.map(s=>`"${s}"`).join(", "),valid:[...VALID].join(", ")});return parsed}function parseLevel(level){if(level===void 0)return"L2";if(level==="L1"||level==="L2"||level==="L3"||level==="L4")return level;throw ArbiterError.fromKey("E_INVALID_LEVEL","errors.E_INVALID_LEVEL",{level},{hint:"Use L1 (fast), L2 (standard, default), L3 (audit-grade), or L4 (compliance-grade)."})}export function resolveAdoptionTier(tier){if(tier==="bootstrap")return{governanceLevel:"L1",brownfield:!0};if(tier==="L1"||tier==="L2"||tier==="L3"||tier==="L4")return{governanceLevel:tier,brownfield:!1};throw ArbiterError.fromKey("E_INVALID_LEVEL","errors.E_INVALID_LEVEL",{level:tier},{hint:"Use bootstrap (gentlest Day-1 on-ramp), L1, L2, L3, or L4."})}export function applyAdoptionTier(options){if(options.tier===void 0)return;const resolved=resolveAdoptionTier(options.tier);options.level=resolved.governanceLevel,resolved.brownfield&&(options.brownfield=!0)}export function parseLanguage(language){if(language===void 0)return;if(new Set(["typescript","java","kotlin","rust","python","go","multi"]).has(language))return language;throw ArbiterError.fromKey("E_INVALID_LANGUAGE","errors.E_INVALID_LANGUAGE",{language})}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{runProbes}from"../../compatibility/probe.js";import{formatText}from"../../compatibility/report.js";import{t}from"../../i18n/index.js";export function verifyToolchainBeforeWrite(targetDir,options){options.noVerify||options.dryRun||runToolchainVerify(targetDir,!!options.json)}function runToolchainVerify(targetDir,jsonMode=!1){const writeHuman=message=>{jsonMode?process.stderr.write(message):process.stdout.write(message)};writeHuman(`${t("cli.init.verifying_toolchain")}
|
|
3
|
+
`);let report;try{report=runProbes(targetDir)}catch(err){const message=err instanceof Error?err.message:String(err);process.stderr.write(`
|
|
4
|
+
Toolchain verification failed unexpectedly: ${message}
|
|
5
|
+
No files were generated or modified. Use --no-verify to skip verification.
|
|
6
|
+
`),process.exit(1)}writeHuman(`${formatText(report)}
|
|
7
|
+
`),report.hasFailures&&(process.stderr.write(`
|
|
8
|
+
arbiter init aborted: toolchain incompatibilities detected.
|
|
9
|
+
Fix the issues above and re-run, or use --no-verify to skip.
|
|
10
|
+
`),process.exit(1))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{resolve,join}from"node:path";import{acquireLock}from"../utils/file-lock.js";import{UserFacingError,ArbiterError}from"../utils/errors.js";import{t}from"../i18n/index.js";import{jsonOutput}from"../utils/json-output.js";import{getLogger}from"../utils/logger.js";import{detectGitInfo,detectAdverseGitState}from"../detectors/git.js";import{isBrownfield}from"../detectors/existing.js";import{loadConfig}from"../utils/config.js";import{slugifyProjectName,resolveProjectName}from"../config/resolve-project-name.js";import{isWindows,isWSL2}from"../utils/platform.js";import{runCli}from"../utils/run-cli.js";import{showTelemetryBannerIfFirstRun}from"../utils/first-run.js";import{loadRecipe}from"../recipes/loader.js";import{resolveConfig,applyPresetOptions,applyAdoptionTier,resolveAdoptionTier,detectProjectForInit}from"./init/resolve-config.js";import{buildArbiterConfig}from"./init/build-arbiter-config.js";import{runGenerators,runPlugins,computeDryRunPreview,displayDryRunPreview,rollbackGeneration,assertEmittedFilesPresent,printResults,generateAndFinalize}from"./init/generate.js";import{ensureDir}from"../utils/fs.js";import{runGithubSetup}from"./init/github-setup.js";import{verifyToolchainBeforeWrite}from"./init/toolchain.js";import{checkL3MaturityGates,checkCollaborationCoherenceGate,deriveWorkflowCapabilities,deriveL3MaturityChecks}from"./init/maturity-gates.js";export{runGenerators,runPlugins,computeDryRunPreview,rollbackGeneration,assertEmittedFilesPresent,printResults,runGithubSetup,buildArbiterConfig,resolveAdoptionTier,deriveWorkflowCapabilities,deriveL3MaturityChecks};function assertNotNativeWindows(){if(isWindows()&&!isWSL2())throw ArbiterError.fromKey("E_INIT_WIN32","errors.E_INIT_WIN32")}function assertKnownLanguage(language){if(language==="unknown")throw ArbiterError.fromKey("E_INIT_UNKNOWN_LANGUAGE","errors.E_INIT_UNKNOWN_LANGUAGE")}function runPreMutationGitGuards(targetDir,options){detectGitInfo(targetDir).isGitRepo&&(guardAdverseGitState(targetDir,options.force),options.brownfield&&guardBrownfieldDirtyTree(targetDir,options.force))}export{slugifyProjectName,resolveProjectName};export async function runInit(options){const targetDir=resolve(options.dir??process.cwd()),projectName=slugifyProjectName(resolveProjectName(targetDir,loadConfig(targetDir))),log=options.json?()=>{}:msg=>{process.stdout.write(`${msg}
|
|
3
|
+
`)};if(options.json&&!options.yes){jsonOutput("init","error",{},["--json requires --yes (interactive wizard is incompatible with machine-readable output)"],{errorClass:"config"}),process.exit(78);return}applyAdoptionTier(options),assertNotNativeWindows(),runPreMutationGitGuards(targetDir,options),verifyToolchainBeforeWrite(targetDir,options),ensureDir(join(targetDir,".arbiter"));const lock=await acquireLock(join(targetDir,".arbiter",".lock"));try{showTelemetryBannerIfFirstRun(void 0,options.quiet),log(`
|
|
4
|
+
Arbiter \u2014 AI Development Governance Framework
|
|
5
|
+
`),log(" Detecting project...");const detection=detectProjectForInit(targetDir,options,log);options.json||logExistingDetections(detection.existing),detection.gitInfo.isGitRepo&&guardAdverseGitState(targetDir,options.force);const recipe=await loadRecipeFromOptions(options,log),config=await resolveConfig({options,recipe,targetDir,projectName,language:detection.language,framework:detection.framework,buildCmds:detection.buildCmds,packageManager:detection.packageManager.name,gitInfo:detection.gitInfo,existing:detection.existing,githubAccess:detection.githubAccess,externalModelAccess:detection.externalModelAccess,lanes:detection.lanes,languageLocked:detection.languageLocked,languageSource:detection.languageSource});if(config===null)return;if(applyPresetOptions(options,config),assertKnownLanguage(config.language),options.dryRun){await displayDryRunPreview(config);return}checkL3MaturityGates(config),checkCollaborationCoherenceGate(config),await generateAndFinalize({config,targetDir,initOptions:options,log,brownfieldDetected:isBrownfield(detection.existing),packageManager:detection.packageManager.name})}finally{await lock.release()}}async function loadRecipeFromOptions(options,log){if(!options.recipe)return;const recipe=await loadRecipe(options.recipe,options.recipeSha256!==void 0?{sha256:options.recipeSha256}:{});return options.json||log(" \u251C\u2500\u2500 Recipe: loaded"),recipe}function guardAdverseGitState(targetDir,force){const adverseState=detectAdverseGitState(targetDir);if(!adverseState)return;const warning=`
|
|
6
|
+
Warning: ${adverseState.message}
|
|
7
|
+
${adverseState.suggestedFix}
|
|
8
|
+
`;if(!force)throw new UserFacingError(`${adverseState.message}
|
|
9
|
+
${adverseState.suggestedFix}
|
|
10
|
+
${t("cli.shared.force_override_hint")}`);getLogger().warn("init.adverse_git_state",{message:adverseState.message,suggested_fix:adverseState.suggestedFix},warning)}export function guardBrownfieldDirtyTree(targetDir,force){try{if(runCli("git",["status","--porcelain"],{cwd:targetDir,timeoutMs:5e3}).stdout.trim()==="")return;if(!force)throw ArbiterError.fromKey("E_INIT_DIRTY_TREE","errors.E_INIT_DIRTY_TREE");getLogger().warn("init.dirty_tree",{},t("cli.init.dirty_tree_warn"))}catch(err){if(err instanceof UserFacingError)throw err;const code=err.code;if(code==="ENOENT"||code==="EACCES")return;throw err}}function logExistingDetections(existing){existing.agentsMd&&process.stdout.write(`${t("cli.init.existing_agents_md")}
|
|
11
|
+
`),existing.claudeDir&&process.stdout.write(`${t("cli.init.existing_claude_dir")}
|
|
12
|
+
`),existing.agentsDir&&process.stdout.write(`${t("cli.init.existing_agents_dir")}
|
|
13
|
+
`),existing.geminiDir&&process.stdout.write(`${t("cli.init.existing_gemini_dir")}
|
|
14
|
+
`),existing.windsurfRules&&process.stdout.write(`${t("cli.init.existing_windsurf")}
|
|
15
|
+
`),existing.aiderConf&&process.stdout.write(`${t("cli.init.existing_aider")}
|
|
16
|
+
`),existing.aiRulez&&process.stdout.write(`${t("cli.init.ai_rulez_detected")}
|
|
17
|
+
`)}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{resolve}from"node:path";import{loadConfig}from"../utils/config.js";import{runConfigure,parseValue}from"./configure.js";import{CLUSTERS,METHODOLOGY_CATALOG,probeAll}from"./method.js";function isBooleanPath(path){return path.startsWith("features.")}function currentString(values,path){const v=values[path];return v==null?"":Array.isArray(v)?v.join(","):typeof v=="string"?v:typeof v=="number"||typeof v=="boolean"?String(v):""}const VERDICT_HINT={wired:"on, files emitted",partial:"on, files NOT emitted",unverified:"on, emission uncheckable (no manifest)",off:"off"};async function promptFeature(clack,feature,status){const staged=[];for(const path of feature.configPaths){const before=currentString(status.values,path);if(isBooleanPath(path)){const next2=await clack.confirm({message:path,initialValue:status.values[path]===!0});if(clack.isCancel(next2))return null;String(next2)!==before&&staged.push(`${path}=${String(next2)}`);continue}const next=await clack.text({message:path,initialValue:before,validate:raw=>{const value=raw??"";if(value.trim()==="")return"Provide a value, or press Ctrl-C to cancel.";try{parseValue(path,value);return}catch(err){return err instanceof Error?err.message:String(err)}}});if(clack.isCancel(next))return null;next!==before&&staged.push(`${path}=${next}`)}return staged}async function promptCluster(clack,cluster,statuses){const rows=METHODOLOGY_CATALOG.filter(f=>f.cluster===cluster),byId=new Map(statuses.map(s=>[s.id,s])),picked=await clack.multiselect({message:`${cluster} \u2014 which features do you want to change?`,options:rows.map(f=>({value:f.id,label:f.name,hint:VERDICT_HINT[byId.get(f.id)?.verdict??"off"]})),required:!1});if(clack.isCancel(picked))return null;const staged=[];for(const id of picked){const feature=rows.find(f=>f.id===id),status=byId.get(id);if(!feature||!status)continue;const result=await promptFeature(clack,feature,status);if(result===null)return null;staged.push(...result)}return staged}export async function runInteractiveMethod(dir){const clack=await import("@clack/prompts"),targetDir=resolve(dir??process.cwd()),config=loadConfig(targetDir);if(!config){process.stderr.write("arbiter: no arbiter.json found. Run `arbiter init` first.\n"),process.exit(1);return}const statuses=probeAll(targetDir,config),wired=statuses.filter(s=>s.verdict==="wired").length,partial=statuses.filter(s=>s.verdict==="partial").length,off=statuses.filter(s=>s.verdict==="off").length;clack.intro("arbiter method"),clack.note(`wired ${wired} partial ${partial} off ${off}
|
|
3
|
+
Full report: arbiter method status (or --json)`,"methodology");const cluster=await clack.select({message:"Which cluster do you want to tune?",options:CLUSTERS.map(c=>({value:c,label:c,hint:`${statuses.filter(s=>s.cluster===c&&s.verdict==="wired").length}/${statuses.filter(s=>s.cluster===c).length} wired`}))});if(clack.isCancel(cluster)){clack.cancel("Cancelled \u2014 nothing written.");return}const staged=await promptCluster(clack,cluster,statuses);if(staged===null){clack.cancel("Cancelled \u2014 nothing written.");return}if(staged.length===0){clack.outro("No changes.");return}clack.note(staged.join(`
|
|
4
|
+
`),"staged changes");const apply=await clack.confirm({message:"Apply these changes?",initialValue:!0});if(clack.isCancel(apply)||!apply){clack.cancel("Cancelled \u2014 nothing written.");return}await runConfigure({dir:targetDir,sets:staged}),clack.outro("Applied via arbiter configure.")}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync}from"node:fs";import{join,resolve}from"node:path";import{loadConfig}from"../utils/config.js";import{jsonOutput}from"../utils/json-output.js";import{GENERATED_MANIFEST_FILE,loadGeneratedManifest}from"../state/generated-manifest.js";import{resolveSettingValue}from"./settings.js";export const CLUSTERS=["process-core","testing","gates-cli","engineering","agentic-harness","ssot-traceability","audit-evidence"];const DEFAULT_ON_PATHS=new Set(["features.contractTesting","features.mutationTesting","features.evidenceHarness"]);export const NON_METHODOLOGY_PATHS=new Map([["archetype","project shape \u2014 what the project is, not how it is built"],["architectureStyle","project shape"],["isMultiTenant","project shape"],["hasDatabase","project shape"],["hasPublicApi","project shape"],["contractType","project shape \u2014 the contract flavour, not whether contracts are tested"],["permitGitHub","access/integration switch, not a methodology dial"]]),METHODOLOGY_CATALOG=[{id:"M-PROC-01",cluster:"process-core",name:"Governance level",configPaths:["governanceLevel"]},{id:"M-PROC-02",cluster:"process-core",name:"Collaboration mode",configPaths:["collaborationMode"]},{id:"M-PROC-03",cluster:"process-core",name:"Branching strategy",configPaths:["branchingStrategy"]},{id:"M-PROC-04",cluster:"process-core",name:"Solo merge mode",configPaths:["solo.mergeMode"]},{id:"M-PROC-05",cluster:"process-core",name:"Solo-dev mode",configPaths:["features.soloDevMode"]},{id:"M-TEST-01",cluster:"testing",name:"Contract testing",configPaths:["features.contractTesting"]},{id:"M-TEST-02",cluster:"testing",name:"Mutation testing",configPaths:["features.mutationTesting","thresholds.mutationScore"]},{id:"M-TEST-03",cluster:"testing",name:"Performance testing",configPaths:["features.perfTesting"]},{id:"M-TEST-04",cluster:"testing",name:"Coverage floors",configPaths:["thresholds.lineCoverage","thresholds.branchCoverage"]},{id:"M-GATE-01",cluster:"gates-cli",name:"Debt ratchet gates",configPaths:["features.debtGates"],emits:["scripts/debt-lib.mjs","scripts/capture-debt-baseline.mjs","scripts/debt-report.mjs"]},{id:"M-GATE-02",cluster:"gates-cli",name:"Suppression ledger",configPaths:["features.suppressions"],emits:["suppressions/suppressions-schema.json","scripts/check-suppressions.mjs"]},{id:"M-GATE-03",cluster:"gates-cli",name:"Acceptance-criteria anchor",configPaths:["features.acceptanceAnchor"]},{id:"M-GATE-04",cluster:"gates-cli",name:"Default gate level",configPaths:["automation.defaultGateLevel"]},{id:"M-ENG-01",cluster:"engineering",name:"Complexity ceiling",configPaths:["thresholds.cyclomaticComplexity"]},{id:"M-ENG-02",cluster:"engineering",name:"Method size limits",configPaths:["thresholds.methodLength","thresholds.maxParams"]},{id:"M-AGENT-01",cluster:"agentic-harness",name:"AI tool harness",configPaths:["tools"]},{id:"M-AGENT-02",cluster:"agentic-harness",name:"Ship autonomy level",configPaths:["automation.autonomy"]},{id:"M-AGENT-03",cluster:"agentic-harness",name:"Parallel worktree cap",configPaths:["automation.maxParallelWorktrees"]},{id:"M-AGENT-04",cluster:"agentic-harness",name:"Cross-model review consent",configPaths:["crossModelReview.enabled","crossModelReview.diffEgressConsent","crossModelReview.providers","crossModelReview.slots.codeReview","crossModelReview.slots.redTeamReview","crossModelReview.timeoutMs","crossModelReview.onUnavailable"]},{id:"M-SSOT-01",cluster:"ssot-traceability",name:"25-dimension taxonomy",configPaths:["features.taxonomy25d"]},{id:"M-SSOT-02",cluster:"ssot-traceability",name:"CODEOWNERS notification",configPaths:["features.codeownersNotify"]},{id:"M-AUDIT-01",cluster:"audit-evidence",name:"Evidence harness",configPaths:["features.evidenceHarness"],emits:["scripts/done-evidence.mjs"]},{id:"M-AUDIT-02",cluster:"audit-evidence",name:"Security scanning",configPaths:["features.securityScanning"]},{id:"M-AUDIT-03",cluster:"audit-evidence",name:"Risk register",configPaths:["features.riskRegister"]},{id:"M-AUDIT-04",cluster:"audit-evidence",name:"Operations handbook",configPaths:["features.operationsHandbook"]},{id:"M-AUDIT-05",cluster:"audit-evidence",name:"ISO 27001 mapping",configPaths:["features.iso27001Mapping"]},{id:"M-AUDIT-06",cluster:"audit-evidence",name:"NIS2 mapping",configPaths:["features.nis2Mapping"]},{id:"M-AUDIT-07",cluster:"audit-evidence",name:"GDPR mapping",configPaths:["features.gdprMapping"]}];function pathActive(config,path){const value=resolveSettingValue(config,path);return value==null?DEFAULT_ON_PATHS.has(path):typeof value=="boolean"?value:Array.isArray(value)?value.length>0:typeof value=="string"?value!=="":!0}function resolveMissingEmits(declared,manifest,targetDir,manifestPresent){return!manifestPresent||declared.length===0?[]:declared.filter(key=>!(key in manifest)||!existsSync(join(targetDir,key)))}function resolveEmitFacet(declaredCount,manifestPresent,missingCount){return declaredCount===0?"n/a":manifestPresent?missingCount===0?"satisfied":"missing":"unknown"}function resolveVerdict(config,emit){return config==="inactive"?"off":emit==="missing"?"partial":emit==="unknown"?"unverified":"wired"}export function probeFeature(feature,config,manifest,targetDir,manifestPresent=!0){const configFacet=feature.configPaths.every(p=>pathActive(config,p))?"active":"inactive",values={};for(const p of feature.configPaths)values[p]=resolveSettingValue(config,p)??null;const declared=feature.emits??[],missingEmits=resolveMissingEmits(declared,manifest,targetDir,manifestPresent),emit=resolveEmitFacet(declared.length,manifestPresent,missingEmits.length),verdict=resolveVerdict(configFacet,emit);return{id:feature.id,cluster:feature.cluster,name:feature.name,verdict,config:configFacet,emit,values,missingEmits}}export function probeAll(targetDir,config){const manifestPresent=existsSync(join(targetDir,GENERATED_MANIFEST_FILE)),manifest=loadGeneratedManifest(targetDir);return METHODOLOGY_CATALOG.map(f=>probeFeature(f,config,manifest,targetDir,manifestPresent))}const GLYPH={wired:"OK ",partial:"WARN",unverified:"? ",off:"off "},EMIT_LABEL={satisfied:"Emit \u2713",missing:"Emit \u2717","n/a":"Emit n/a",unknown:"Emit ?"};function facets(s){return`Config ${s.config==="active"?"\u2713":"\u2717"} / ${EMIT_LABEL[s.emit]}`}export function runMethodStatus(opts={}){const targetDir=resolve(opts.dir??process.cwd()),config=loadConfig(targetDir);if(config==null){process.stderr.write("arbiter: no arbiter.json found. Run `arbiter init` first.\n"),process.exit(1);return}const statuses=probeAll(targetDir,config);if(opts.json){jsonOutput("method","ok",{clusters:CLUSTERS.map(cluster=>({cluster,features:statuses.filter(s=>s.cluster===cluster)})),summary:{wired:statuses.filter(s=>s.verdict==="wired").length,partial:statuses.filter(s=>s.verdict==="partial").length,unverified:statuses.filter(s=>s.verdict==="unverified").length,off:statuses.filter(s=>s.verdict==="off").length}});return}process.stdout.write(`
|
|
3
|
+
arbiter method \u2014 methodology wiring status
|
|
4
|
+
`);for(const cluster of CLUSTERS){const rows=statuses.filter(s=>s.cluster===cluster);if(rows.length!==0){process.stdout.write(`
|
|
5
|
+
${cluster}
|
|
6
|
+
`);for(const row of rows){process.stdout.write(` ${GLYPH[row.verdict]} ${row.name.padEnd(30)} ${facets(row)}
|
|
7
|
+
`);for(const missing of row.missingEmits)process.stdout.write(` not emitted: ${missing}
|
|
8
|
+
`)}}}const count=v=>statuses.filter(s=>s.verdict===v).length,unverified=count("unverified");process.stdout.write(`
|
|
9
|
+
wired ${count("wired")} partial ${count("partial")} unverified ${unverified} off ${count("off")}
|
|
10
|
+
`),unverified>0&&process.stdout.write(`No ${GENERATED_MANIFEST_FILE} here \u2014 emission is uncheckable, not missing. Run \`arbiter update\` to generate one.
|
|
11
|
+
`),process.stdout.write("Tune with `arbiter method` (interactive) \u2014 every write is delegated to `arbiter configure`.\n")}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync}from"node:fs";import{resolve,join}from"node:path";import{runCli,CliError}from"../utils/run-cli.js";const GEN_WIKI_REL=join("scripts","gen-wiki.mjs"),CHECK_WIKI_LINT_REL=join("scripts","check-wiki-lint.mjs"),UPDATE_HINT="vault scripts not found \u2014 run 'arbiter update' to (re)generate them";function resolveMode(opts){return[opts.validateOnly,opts.sync,opts.write,opts.dryRun].filter(Boolean).length>1?"conflict":opts.validateOnly?"validate":opts.sync?"sync":opts.write?"write":"dry-run"}function errorResult(mode,vaultDir,reason){return{status:"error",exitCode:2,mode,contractVersion:1,vaultDir,reason}}function parseLintOutput(output){const brokenLinks=(output.match(/\[broken-link\]/g)??[]).length,orphans=(output.match(/\[orphan\]/g)??[]).length,stale=(output.match(/\[stale\]/g)??[]).length,citation=(output.match(/\[citation\]/g)??[]).length,total=brokenLinks+orphans+stale+citation;return{brokenLinks,orphans,stale,ok:total===0}}function runLint(repoDir,vaultAbsDir,mode,vaultDir){try{const r=runCli("node",[CHECK_WIKI_LINT_REL,"--wiki-dir",vaultAbsDir,"--assert-stale"],{cwd:repoDir});return parseLintOutput(r.stdout+r.stderr)}catch(err){return err instanceof CliError&&!err.notFound?parseLintOutput(err.stdout+err.stderr):errorResult(mode,vaultDir,`check-wiki-lint.mjs failed to launch: ${err instanceof Error?err.message:String(err)}`)}}function runGenRegen(repoDir,vaultAbsDir,mode,vaultDir){try{return runCli("node",[GEN_WIKI_REL,"--wiki-dir",vaultAbsDir],{cwd:repoDir}),{ok:!0}}catch(err){const detail=err instanceof CliError?err.stdout+err.stderr:String(err);return errorResult(mode,vaultDir,`gen-wiki.mjs regen failed: ${detail||String(err)}`)}}function isObsidianResult(v){return"status"in v}function handleValidateMode(repoDir,vaultAbsDir,mode,vaultDir){const validation=runLint(repoDir,vaultAbsDir,mode,vaultDir);return isObsidianResult(validation)?validation:validation.ok?{status:"ok",exitCode:0,mode,contractVersion:1,vaultDir,validation}:{status:"error",exitCode:1,mode,contractVersion:1,vaultDir,validation,reason:"vault validation found violations"}}function handleSyncMode(repoDir,vaultAbsDir,mode,vaultDir){const regen=runGenRegen(repoDir,vaultAbsDir,mode,vaultDir);if(isObsidianResult(regen))return regen;const validation=runLint(repoDir,vaultAbsDir,mode,vaultDir);return isObsidianResult(validation)?validation:validation.ok?{status:"ok",exitCode:0,mode,contractVersion:1,vaultDir,regenerated:!0,validation}:{status:"error",exitCode:1,mode,contractVersion:1,vaultDir,regenerated:!0,validation,reason:"vault validation found violations after regeneration"}}export function runObsidian(opts={}){const repoDir=resolve(opts.dir??process.cwd()),vaultDir=opts.vaultPath??"wiki",vaultAbsDir=resolve(repoDir,vaultDir),mode=resolveMode(opts);if(mode==="conflict")return errorResult("dry-run",vaultDir,"conflicting mode flags \u2014 pass only one of --sync, --validate-only, --write, --dry-run");const genWikiPath=join(repoDir,GEN_WIKI_REL),lintPath=join(repoDir,CHECK_WIKI_LINT_REL);return!existsSync(genWikiPath)||!existsSync(lintPath)?errorResult(mode,vaultDir,UPDATE_HINT):mode==="dry-run"||mode==="write"?{status:"ok",exitCode:0,mode,contractVersion:1,vaultDir}:mode==="validate"?handleValidateMode(repoDir,vaultAbsDir,mode,vaultDir):handleSyncMode(repoDir,vaultAbsDir,mode,vaultDir)}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{resolve,join}from"node:path";import{loadConfig,saveConfig}from"../utils/config.js";import{loadPlugin}from"../utils/plugin-loader.js";import{detectPackageManager}from"../detectors/package-manager.js";import{runCli}from"../utils/run-cli.js";import{acquireLock}from"../utils/file-lock.js";import{ensureDir}from"../utils/fs.js";import{jsonOutput}from"../utils/json-output.js";import{ArbiterError}from"../utils/errors.js";import{t}from"../i18n/index.js";function isLocalSpec(pkg){return pkg.startsWith(".")||pkg.startsWith("/")}function packageNameOf(spec){const at=spec.startsWith("@")?spec.indexOf("@",1):spec.indexOf("@");return at===-1?spec:spec.slice(0,at)}const INSTALL_ARGS={npm:spec=>["install","--save-dev",spec],pnpm:spec=>["add","--save-dev",spec],yarn:spec=>["add","--dev",spec],bun:spec=>["add","--dev",spec]};function requireConfig(targetDir,command,json){const stored=loadConfig(targetDir);if(stored)return stored;throw json&&(jsonOutput(command,"error",{},[t("cli.plugin.no_config")]),process.exit(1)),ArbiterError.fromKey("E_CONFIG_NOT_FOUND","errors.E_CONFIG_NOT_FOUND",{},{hint:"Run `arbiter init` to initialize governance in this directory."})}async function requireLoadable(name,targetDir,json){try{await loadPlugin(name,targetDir);return}catch(err){const message=err instanceof Error?err.message:String(err);throw json&&(jsonOutput("plugin add","error",{name},[message]),process.exit(1)),ArbiterError.fromKey("E_PLUGIN_UNRESOLVABLE","cli.plugin.load_error",{name,message,hint:"Verify the package name or path is correct and that it is installed."})}}async function persistPlugins(targetDir,stored,plugins){ensureDir(join(targetDir,".arbiter"));const lock=await acquireLock(join(targetDir,".arbiter",".lock"));try{await saveConfig(targetDir,{...stored,plugins})}finally{await lock.release()}}export async function runPluginAdd(options){const targetDir=resolve(options.dir??process.cwd()),json=options.json===!0,stored=requireConfig(targetDir,"plugin add",json),spec=options.pkg,local=isLocalSpec(spec),name=local?spec:packageNameOf(spec);if(!local&&options.install){const pm=detectPackageManager(targetDir);runCli(pm.name,INSTALL_ARGS[pm.name](spec),{cwd:targetDir,timeoutMs:18e4})}await requireLoadable(name,targetDir,json);const existing=stored.plugins??[],added=!existing.includes(name),plugins=added?[...existing,name].sort((a,b)=>a.localeCompare(b)):existing;if(added&&await persistPlugins(targetDir,stored,plugins),json){jsonOutput("plugin add","ok",{name,added,plugins});return}if(!added){process.stdout.write(`${t("cli.plugin.already_configured",{name})}
|
|
3
|
+
`);return}process.stdout.write(`${t("cli.plugin.added",{name})}
|
|
4
|
+
`),process.stdout.write(`${t("cli.plugin.security_advisory",{name})}
|
|
5
|
+
`)}async function loadStatus(name,targetDir){try{return await loadPlugin(name,targetDir),"loaded"}catch(err){const message=err instanceof Error?err.message:String(err);return/not found/i.test(message)?"not found":"error"}}export async function runPluginList(options){const targetDir=resolve(options.dir??process.cwd()),names=requireConfig(targetDir,"plugin list",options.json===!0).plugins??[],plugins=[];for(const name of names)plugins.push({name,status:await loadStatus(name,targetDir)});if(options.json){jsonOutput("plugin list","ok",{plugins});return}if(plugins.length===0){process.stdout.write(`${t("cli.plugin.no_plugins")}
|
|
6
|
+
`);return}process.stdout.write(`${t("cli.plugin.plugins_header")}
|
|
7
|
+
`);for(const p of plugins)process.stdout.write(`${t("cli.plugin.plugin_row",{name:p.name,status:p.status})}
|
|
8
|
+
`)}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
const FAILED_CONCLUSIONS=new Set(["FAILURE","TIMED_OUT","CANCELLED"]);export function failingCheckNames(pr){return(pr.statusCheckRollup??[]).filter(check=>FAILED_CONCLUSIONS.has(check.conclusion??"")).map(check=>check.name??check.context??"(unnamed check)")}function unmergedDetail(pr){const failing=failingCheckNames(pr),merge=pr.mergeStateStatus===void 0?"":`, mergeStateStatus ${pr.mergeStateStatus}`,checks=failing.length>0?` Failing checks: ${failing.join(", ")}.`:" No check has reported a red conclusion yet.";return`PR #${pr.number} is ${pr.state}${merge}, not MERGED.${checks} Watch it to green and merge it (\`node scripts/pr-merge-watch.mjs <owner/repo> ${pr.number}\`), or pass --no-pr if this repo lands by direct push.`}export function evaluateMerged(prs,branch,explicitPr,candidateSha,qualification){if(candidateSha!==void 0)return evaluateQualifiedMerged(prs,candidateSha,explicitPr,qualification);if(explicitPr!==void 0){const named=prs.find(pr=>pr.number===explicitPr);return named===void 0?{merged:!1,detail:`PR #${explicitPr} was not found on branch "${branch}" (\`gh pr list --head ${branch} --state all\` returned ${prs.length} PR(s)).`}:named.state==="MERGED"?{merged:!0,number:named.number}:{merged:!1,detail:unmergedDetail(named)}}const merged=prs.find(pr=>pr.state==="MERGED");if(merged!==void 0)return{merged:!0,number:merged.number};const[first]=prs;return first===void 0?{merged:!1,detail:`no PR exists for branch "${branch}". Open one and drive it to green before completing, or pass --no-pr if this repo lands by direct push.`}:{merged:!1,detail:unmergedDetail(first)}}function evaluateQualifiedMerged(prs,candidateSha,explicitPr,qualification){return evaluateQualifiedCompletionInternal(prs,candidateSha,qualification?.policy??"exact-pr",{explicitPr,requireMainBase:qualification?.requireMainBase??!1,mergeReachableFromMain:qualification?.mergeReachableFromMain??!0})}function evaluateQualifiedCompletionInternal(prs,candidateSha,policy,options){const candidate=prs.find(pr=>isQualifiedCandidate(pr,candidateSha,options.explicitPr));if(!hasMergeCommit(candidate))return{merged:!1,detail:"Merged PR does not carry the qualified candidate head and merge refs."};const prerequisiteFailure=qualificationPrerequisiteFailure(candidate,policy,options);if(prerequisiteFailure!==void 0)return{merged:!1,detail:prerequisiteFailure};const cutoff=Date.parse(candidate.mergedAt??"");return!Number.isFinite(cutoff)||!successfulCiAtMerge(candidate.statusCheckRollup??[],cutoff)?{merged:!1,detail:"Candidate CI cannot establish successful qualification before merge."}:{merged:!0,number:candidate.number}}function hasMergeCommit(candidate){return candidate?.mergeCommit?.oid!==void 0}function isQualifiedCandidate(pr,candidateSha,explicitPr){return pr.state==="MERGED"&&pr.headRefOid===candidateSha&&(explicitPr===void 0||pr.number===explicitPr)}function qualificationPrerequisiteFailure(candidate,policy,options){if(options.requireMainBase&&candidate.baseRefName!=="main")return"Merged PR does not target main.";if(!options.mergeReachableFromMain)return"Merged PR commit is not reachable from current origin/main.";if(policy==="exact-pr"&&candidate.mergeCommit.oid!==candidate.headRefOid)return"Merged PR head/merge refs do not match the qualified candidate SHA."}function finishedBeforeMerge(check,cutoff){const time=Date.parse((check.checkSuite?check.completedAt:check.createdAt)??"");return Number.isFinite(time)&&time<=cutoff}function ciCreatedAt(check){return Date.parse(check.checkSuite?.createdAt??check.createdAt??"")}function successfulCiAtMerge(checks,cutoff){let success=!1;for(const check of checks){const created=ciCreatedAt(check);if(!Number.isFinite(created))return!1;if(created>cutoff){if(!check.checkSuite)return!1;continue}const outcome=check.conclusion??check.state??"";if(!["SUCCESS","SKIPPED","NEUTRAL"].includes(outcome)||!finishedBeforeMerge(check,cutoff))return!1;outcome==="SUCCESS"&&(success=!0)}return success}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync,readFileSync}from"node:fs";import{createHash}from"node:crypto";import{join}from"node:path";import{manifestKey}from"../state/generated-manifest.js";import{RETIRED_RENDERS}from"../state/retired-renders.js";import{isSafetyClassKey}from"../generators/safety-class.js";import{unlinkTranslated}from"../utils/fs.js";const byKey=(a,b)=>a<b?-1:a>b?1:0;export function diskHasher(targetDir){return key=>{const path=join(targetDir,key);if(!existsSync(path))return null;try{return createHash("sha256").update(readFileSync(path)).digest("hex")}catch{return null}}}export function planRetirement(opts){const plan={retire:[],orphans:[],stale:[]};if(!opts.fullRegistryRun)return plan;const visited=new Set(opts.results.map(r=>manifestKey(opts.targetDir,r.path)).filter(k=>k!==null)),candidates=new Set([...Object.keys(opts.prevManifest),...Object.keys(RETIRED_RENDERS)]);for(const key of candidates){if(visited.has(key))continue;const onDisk=opts.diskHash(key);onDisk!==null&&(isSafetyClassKey(key)?onDisk===opts.prevManifest[key]||(RETIRED_RENDERS[key]??[]).includes(onDisk)?plan.retire.push(key):plan.orphans.push(key):plan.stale.push(key))}return plan.retire.sort(byKey),plan.orphans.sort(byKey),plan.stale.sort(byKey),plan}export function applyRetirement(targetDir,plan){for(const key of[...plan.retire])try{unlinkTranslated(join(targetDir,key))}catch{plan.retire=plan.retire.filter(k=>k!==key),plan.orphans=[...plan.orphans,key].sort(byKey)}}export function retirementWarning(plan){const sections=[];return plan.retire.length>0&§ions.push(`Retired ${plan.retire.length} arbiter-owned file(s) this version no longer emits (unmodified since arbiter wrote them, so removal is lossless): ${plan.retire.join(", ")}`),plan.orphans.length>0&§ions.push(`Warning: ${plan.orphans.length} arbiter-owned file(s) are no longer emitted but were edited locally, so they were left in place \u2014 delete them by hand once you have salvaged your changes: ${plan.orphans.join(", ")}`),plan.stale.length>0&§ions.push(`Note: ${plan.stale.length} previously generated file(s) were not emitted by this run (a retired template, or a generator this config no longer selects). They are left untouched \u2014 review before deleting: ${plan.stale.join(", ")}`),sections.length>0?sections.join(`
|
|
3
|
+
`):null}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{storeFromSnapshot}from"../graph/store.js";export function runReviewDiff(opts){const baseStore=storeFromSnapshot(opts.base),headStore=storeFromSnapshot(opts.head),enforcementChanges=diffEnforcement(baseStore,headStore),removedProvers=diffProvers(baseStore,headStore),adrSupersessions=diffAdrSupersessions(baseStore,headStore),hasWeakening=enforcementChanges.some(c=>c.direction==="weakened"),hasLostProvers=removedProvers.length>0,block=hasWeakening||hasLostProvers,recommendation=block?"BLOCK":"PASS",riskDelta=hasWeakening||hasLostProvers?"increased":enforcementChanges.some(c=>c.direction==="strengthened")?"decreased":"unchanged",lines=[];if(enforcementChanges.length>0){lines.push(`Enforcement changes: ${enforcementChanges.length}`);for(const c of enforcementChanges)lines.push(` ${c.inv}: ${c.direction} (${c.added.length} added, ${c.removed.length} removed)`)}removedProvers.length>0&&lines.push(`Removed last provers: ${removedProvers.join(", ")}`),adrSupersessions.length>0&&lines.push(`ADR supersessions: ${adrSupersessions.map(s=>s.adr).join(", ")}`),lines.length===0&&lines.push("No semantic changes detected.");const summary=`[${recommendation}] ${lines[0]}`;return{status:"ok",exitCode:block?2:0,recommendation,risk_delta:riskDelta,changes:{enforcement_changes:enforcementChanges,removed_provers:removedProvers,adr_supersessions:adrSupersessions},summary}}function diffEnforcement(base,head){const changes=[],allInvIds=new Set([...base.nodesByKind("INV").map(n=>n.id),...head.nodesByKind("INV").map(n=>n.id)]);for(const invId of allInvIds){const baseGates=new Set(base.outgoing(invId,"enforces").map(e=>e.to)),headGates=new Set(head.outgoing(invId,"enforces").map(e=>e.to)),added=[...headGates].filter(g=>!baseGates.has(g)),removed=[...baseGates].filter(g=>!headGates.has(g));if(added.length===0&&removed.length===0)continue;const direction=removed.length>0?"weakened":"strengthened";changes.push({inv:invId,direction,added,removed})}return changes}function diffProvers(base,head){const removedLastProverInvs=[];for(const inv of base.nodesByKind("INV")){if(base.incoming(inv.id,"proves").length===0)continue;head.incoming(inv.id,"proves").length===0&&removedLastProverInvs.push(inv.id)}return removedLastProverInvs.sort()}function diffAdrSupersessions(base,head){const supersessions=[];for(const adr of base.nodesByKind("ADR")){const baseImplementors=base.incoming(adr.id,"implements").map(e=>e.from);if(baseImplementors.length===0)continue;const headImplementors=new Set(head.incoming(adr.id,"implements").map(e=>e.from)),removed=baseImplementors.filter(id=>!headImplementors.has(id));removed.length>0&&supersessions.push({adr:adr.id,removedImplementors:removed})}return supersessions}export function renderMarkdown(result){const lines=[`## Semantic Review: ${result.recommendation==="PASS"?"PASS":"BLOCK"}`,"",`**Risk delta:** ${result.risk_delta}`,`**Recommendation:** ${result.recommendation}`,""],{enforcement_changes,removed_provers,adr_supersessions}=result.changes;if(enforcement_changes.length>0){lines.push("### Enforcement Changes");for(const c of enforcement_changes){const dir=c.direction==="weakened"?"WEAKENED":"strengthened";lines.push(`- **${c.inv}**: ${dir}`),c.added.length>0&&lines.push(` - Added: ${c.added.join(", ")}`),c.removed.length>0&&lines.push(` - Removed: ${c.removed.join(", ")}`)}lines.push("")}if(removed_provers.length>0){lines.push("### Removed Provers (BLOCK)");for(const inv of removed_provers)lines.push(`- **${inv}**: last test prover removed`);lines.push("")}if(adr_supersessions.length>0){lines.push("### Implicit ADR Supersessions");for(const s of adr_supersessions)lines.push(`- **${s.adr}**: implementors removed: ${s.removedImplementors.join(", ")}`);lines.push("")}return enforcement_changes.length===0&&removed_provers.length===0&&adr_supersessions.length===0&&lines.push("No semantic changes detected."),lines.join(`
|
|
3
|
+
`)}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{loadConfig}from"../utils/config.js";import{jsonOutput}from"../utils/json-output.js";export const SETTINGS_CATALOG=[{group:"Collaboration",fields:[{path:"collaborationMode",label:"Collaboration mode"},{path:"solo.mergeMode",label:"Solo merge mode"},{path:"branchingStrategy",label:"Branching strategy"}]},{group:"Governance",fields:[{path:"governanceLevel",label:"Governance level"},{path:"permitGitHub",label:"Permit GitHub integration"}]},{group:"Tools",fields:[{path:"tools",label:"AI tools"}]},{group:"Project shape",fields:[{path:"archetype",label:"Archetype"},{path:"architectureStyle",label:"Architecture style"},{path:"contractType",label:"Contract type"},{path:"isMultiTenant",label:"Multi-tenant"},{path:"hasDatabase",label:"Has database"},{path:"hasPublicApi",label:"Has public API"}]},{group:"Features",fields:[{path:"features.debtGates",label:"Debt gates"},{path:"features.suppressions",label:"Suppressions"},{path:"features.securityScanning",label:"Security scanning"},{path:"features.mutationTesting",label:"Mutation testing"},{path:"features.contractTesting",label:"Contract testing"},{path:"features.evidenceHarness",label:"Evidence harness"},{path:"features.acceptanceAnchor",label:"Acceptance-criteria anchor gate (INV-138)"},{path:"features.soloDevMode",label:"Solo dev mode (deprecated)"},{path:"features.codeownersNotify",label:"CODEOWNERS post-merge email notify (L2+)"},{path:"features.taxonomy25d",label:"26-dimension compliance test taxonomy"},{path:"features.perfTesting",label:"k6 performance testing ecosystem"},{path:"features.riskRegister",label:"Risk register doc"},{path:"features.operationsHandbook",label:"Operations handbook doc"},{path:"features.iso27001Mapping",label:"ISO 27001 control mapping"},{path:"features.nis2Mapping",label:"NIS2 control mapping"},{path:"features.gdprMapping",label:"GDPR control mapping"}]},{group:"Automation",fields:[{path:"automation.autonomy",label:"Ship autonomy level (absent = L0)"},{path:"automation.maxParallelWorktrees",label:"Max parallel wave worktrees"},{path:"automation.defaultGateLevel",label:"Default gate level (L1|L2)"}]},{group:"Cross-model review",fields:[{path:"crossModelReview.enabled",label:"Cross-model review enabled"},{path:"crossModelReview.diffEgressConsent",label:"Diff egress consent"},{path:"crossModelReview.providers",label:"Review providers"},{path:"crossModelReview.slots.codeReview",label:"Code-review slots"},{path:"crossModelReview.slots.redTeamReview",label:"Red-team-review slots"},{path:"crossModelReview.timeoutMs",label:"Review timeout (ms)"},{path:"crossModelReview.onUnavailable",label:"Unavailable-provider policy"}]},{group:"Thresholds",fields:[{path:"thresholds.lineCoverage",label:"Line coverage %"},{path:"thresholds.branchCoverage",label:"Branch coverage %"},{path:"thresholds.mutationScore",label:"Mutation score %"},{path:"thresholds.cyclomaticComplexity",label:"Cyclomatic complexity"},{path:"thresholds.methodLength",label:"Method length"},{path:"thresholds.maxParams",label:"Max parameters"}]}],SETTINGS_PATHS=new Set(SETTINGS_CATALOG.flatMap(g=>g.fields.map(f=>f.path)));export function resolveSettingValue(config,path){let cursor=config;for(const segment of path.split(".")){if(cursor==null||typeof cursor!="object")return;cursor=cursor[segment]}return cursor}function formatValue(value){return value===void 0?"(unset)":Array.isArray(value)?value.length?value.join(", "):"(none)":typeof value=="string"?value:typeof value=="number"||typeof value=="boolean"||typeof value=="bigint"?String(value):JSON.stringify(value)}export function runSettings(opts={}){const dir=opts.dir??process.cwd(),config=loadConfig(dir);if(config==null&&(process.stderr.write("arbiter: no arbiter.json found. Run `arbiter init` first.\n"),process.exit(1)),opts.json){const out=SETTINGS_CATALOG.map(g=>({group:g.group,fields:g.fields.map(f=>({path:f.path,label:f.label,value:resolveSettingValue(config,f.path)??null}))}));jsonOutput("settings","ok",{groups:out});return}process.stdout.write(`
|
|
3
|
+
arbiter settings \u2014 current configuration
|
|
4
|
+
`);for(const group of SETTINGS_CATALOG){process.stdout.write(`
|
|
5
|
+
${group.group}
|
|
6
|
+
`);for(const field of group.fields){const value=formatValue(resolveSettingValue(config,field.path));process.stdout.write(` ${field.path.padEnd(34)} ${value}
|
|
7
|
+
`)}}process.stdout.write("\nEdit with `arbiter configure` (interactive) or `arbiter configure --set <path>=<value>`.\n")}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync,readFileSync}from"node:fs";import{join}from"node:path";import{homedir}from"node:os";import{loadConfig}from"../utils/config.js";import{resolveCompanions}from"../integrations/companions.js";import{resolveLanguage}from"../detectors/language.js";import{getLogger}from"../utils/logger.js";import{resolveCollaborationMode,resolveDefaultMergeMode}from"../config/collaboration-mode-defaults.js";import{resolveSetting}from"../config/override-resolver.js";import{assertOverridablePath,parseValue}from"./configure.js";import{writeOverride}from"./task-state.js";import{AUTONOMY_LEVELS,VALID_GATE_LEVELS}from"../config/schema.js";const AUTONOMY_PATH="automation.autonomy",DEFAULT_GATE_LEVEL_PATH="automation.defaultGateLevel",AUTO_ADVANCE_BEHAVIOR="auto-advance",AUTO_MERGE_BEHAVIOR="auto-merge";export function buildShipOverrides(root,input){const overrides={};input.autonomy!==void 0&&(overrides[AUTONOMY_PATH]=input.autonomy);for(const assignment of input.sets??[]){const eqIdx=assignment.indexOf("=");if(eqIdx<0)throw new Error(`Invalid --set "${assignment}" \u2014 expected <path>=<value>.`);const path=assignment.slice(0,eqIdx),rawValue=assignment.slice(eqIdx+1);assertOverridablePath(path),parseValue(path,rawValue),overrides[path]=rawValue}for(const[path,value]of Object.entries(overrides))assertOverridablePath(path),writeOverride(root,path,value);return overrides}function buildOverrides(opts){const merged={...opts.overrides??{}};return opts.autonomyOverride!==void 0&&merged[AUTONOMY_PATH]===void 0&&(merged[AUTONOMY_PATH]=opts.autonomyOverride),Object.keys(merged).length>0?merged:void 0}const ARBITER_SELF_PACKAGE="@getarbiter/cli";export const SELF_ONLY_GATES=["template-authoring","selfOnly-invariants","matrix-fixtures"];const AUTONOMY_GRANTS={L0:new Set,L1:new Set([AUTO_ADVANCE_BEHAVIOR,AUTO_MERGE_BEHAVIOR]),L2:new Set([AUTO_ADVANCE_BEHAVIOR,AUTO_MERGE_BEHAVIOR,"fix-on-red-attempt"]),L3:new Set([AUTO_ADVANCE_BEHAVIOR,AUTO_MERGE_BEHAVIOR,"fix-on-red-attempt","wave-batch","fix-on-red-autopush","subagent-auto-spawn"])};export function autonomyAllows(level,behavior){return AUTONOMY_GRANTS[level].has(behavior)}function isAutonomyLevel(v){return AUTONOMY_LEVELS.includes(v)}export const CONSUMER_DEFAULT_PROFILE={isArbiterSelf:!1,collaborationMode:"peer-review",mergeMode:"pr-ff",governanceLevel:"L2",autonomy:"L0",defaultGateLevel:"L1",companions:[]};export function isArbiterSelf(root){const pkgPath=join(root,"package.json");if(!existsSync(pkgPath))return!1;try{return JSON.parse(readFileSync(pkgPath,"utf-8")).name===ARBITER_SELF_PACKAGE}catch{return!1}}export function resolveShipProfile(root,opts={}){const overrides=buildOverrides(opts),self=isArbiterSelf(root);let config;try{config=loadConfig(root)}catch(err){getLogger().warn("ship.config_unreadable",{root},`could not read arbiter.json (${err instanceof Error?err.message:String(err)}); using consumer-safe ship defaults`),config=null}const autonomy=resolveAutonomy(root,overrides,config?.automation?.autonomy),prefs=resolveProfilePrefs(root,overrides,config?.automation),crossModelReview=resolveCrossModelReview(root,overrides,config?.crossModelReview),companions=profileCompanions(root,self,opts,config);return config===null?{...CONSUMER_DEFAULT_PROFILE,isArbiterSelf:self,autonomy,...prefs,companions}:{...collaborationProfile(config),isArbiterSelf:self,autonomy,...prefs,companions,...crossModelReview!==void 0?{crossModelReview}:{}}}function profileCompanions(root,self,opts,config){return resolveCompanions({self,claudeHome:opts.claudeHome??join(homedir(),".claude"),language:resolveLanguage(root,config??void 0),...config?.companions?{overrides:config.companions}:{}})}function collaborationProfile(config){const collaborationMode=resolveCollaborationMode({...config.collaborationMode!==void 0?{collaborationMode:config.collaborationMode}:{},...config.features.soloDevMode===!0?{enableSoloDevMode:!0}:{}});return{collaborationMode,mergeMode:config.solo?.mergeMode??resolveDefaultMergeMode(collaborationMode),governanceLevel:config.governanceLevel}}function resolveProfilePrefs(root,overrides,profile){const gateRaw=resolveSetting(DEFAULT_GATE_LEVEL_PATH,{root,...overrides!==void 0?{overrides}:{},...profile?.defaultGateLevel!==void 0?{profileValue:profile.defaultGateLevel}:{}});return{defaultGateLevel:VALID_GATE_LEVELS.includes(gateRaw)?gateRaw:"L1"}}function resolveAutonomy(root,overrides,profileValue){const resolved=resolveSetting(AUTONOMY_PATH,{root,...overrides!==void 0?{overrides}:{},...profileValue!==void 0?{profileValue}:{}});return isAutonomyLevel(resolved)?resolved:"L0"}function resolveCrossModelReview(root,overrides,profile){if(profile===void 0)return;const enabled=resolveSetting("crossModelReview.enabled",{root,...overrides!==void 0?{overrides}:{},profileValue:String(profile.enabled)})==="true";return{...profile,enabled}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_REVIEW_MAX_ROUNDS=2;export function resolveReviewMaxRounds(ship){return ship?.review?.maxRounds??2}export function evaluateReviewRound(signals){return signals.forced?{allowed:!0}:signals.rounds<signals.maxRounds?{allowed:!0}:{allowed:!1,detail:`this task has already had ${signals.rounds} review round(s) and the cap is ${signals.maxRounds}. Land it with the remaining findings parked (\`arbiter note\` each one, then ONE follow-up issue), or pass --force-review to take another round deliberately.`}}export function reviewScopeLine(base,rounds,maxRounds){return`git diff ${base}..HEAD (round ${rounds} of ${maxRounds}) \xB7 reviewer findings below HIGH do not block landing; AC-fit verdicts still require all-PASS`}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{existsSync,statSync}from"node:fs";import{join}from"node:path";import{readUnifiedState}from"./task-state.js";import{runCliJson}from"../utils/run-cli.js";import{readFileTranslated}from"../utils/fs.js";const TIER_RANK={XS:0,S:1,Standard:2},WAVE_OR_EPIC=/^(?:wave|epic)(?:\/.*)?$/i,GRAPH_MAX_BYTES=256*1024*1024,ALLOWED_RELATIONS=new Set(["imports","imports_from","calls","references","re_exports","implements","inherits","extends","uses","depends_on"]),BLAST_RADIUS_S=25,BLAST_RADIUS_STANDARD=75;export function normTier(tier){return tier==="XS"||tier==="S"?tier:"Standard"}export function widenTier(base,signals){let result=base;const widenTo=floor=>{TIER_RANK[floor]>TIER_RANK[result]&&(result=floor)};(signals.milestoneBundled||signals.labels.some(label=>WAVE_OR_EPIC.test(label)))&&widenTo("Standard");const blastRadius=signals.blastRadius;return typeof blastRadius=="number"&&Number.isFinite(blastRadius)&&blastRadius>=0&&(blastRadius>=BLAST_RADIUS_STANDARD?widenTo("Standard"):blastRadius>=BLAST_RADIUS_S&&widenTo("S")),result}export function gatherTierSignals(root,taskId){try{return{...gatherIssueSignals(root,taskId),blastRadius:gatherBlastRadius(root)}}catch{return{blastRadius:null,labels:[],milestoneBundled:!1}}}function gatherIssueSignals(root,taskId){const issueNumber=taskId?.replace(/^#/,"");if(issueNumber===void 0||!/^\d+$/.test(issueNumber))return{labels:[],milestoneBundled:!1};try{const response=runCliJson("gh",["issue","view",issueNumber,"--json","labels,milestone"],{cwd:root,timeoutMs:1e4});return isIssueResponse(response)?{labels:response.labels.map(label=>label.name.toLowerCase()),milestoneBundled:response.milestone!==null}:{labels:[],milestoneBundled:!1}}catch{return{labels:[],milestoneBundled:!1}}}function gatherBlastRadius(root){try{const manifest=readPlanManifest(root);if(manifest===null||manifest.size===0)return null;const graphPath=join(root,"graphify-out","graph.json");if(!existsSync(graphPath))return null;const graphStat=statSync(graphPath);if(graphStat.size>GRAPH_MAX_BYTES)return null;for(const file of manifest){const path=join(root,file);if(existsSync(path)&&graphStat.mtimeMs<statSync(path).mtimeMs)return null}return countBlastRadius(JSON.parse(readFileTranslated(graphPath,"utf-8")),manifest)}catch{return null}}function readPlanManifest(root){const plan=readUnifiedState(root)?.plan;if(typeof plan!="string"||!isRepoRelativePosixPath(plan))return null;const files=parsePlanFilesManifest(readFileTranslated(join(root,plan),"utf-8"));return files===null||files.length===0?null:new Set(files)}function parsePlanFilesManifest(plan){const frontMatter=plan.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];if(frontMatter===void 0)return null;const lines=frontMatter.split(/\r?\n/),filesIndex=lines.findIndex(line=>/^files:\s*$/.test(line));if(filesIndex===-1)return null;const files=[];for(const line of lines.slice(filesIndex+1)){if(/^[A-Za-z][A-Za-z0-9_-]*:\s*/.test(line))break;if(line.trim()==="")continue;const file=line.match(/^\s+-\s+(.+?)\s*$/)?.[1];if(file===void 0||!isRepoRelativePosixPath(file))return null;files.push(file)}return files}function countBlastRadius(graph,manifest){if(!isRecord(graph)||!Array.isArray(graph.nodes)||!Array.isArray(graph.links))return null;const sourceFiles=graphNodeSourceFiles(graph.nodes),dependents=new Set;for(const link of graph.links){const files=allowlistedEdgeFiles(link,sourceFiles);files!==null&&addDependentFiles(manifest,dependents,files)}return dependents.size}function graphNodeSourceFiles(nodes){const sourceFiles=new Map;for(const node of nodes)isGraphNode(node)&&sourceFiles.set(node.id,node.source_file);return sourceFiles}function isGraphNode(value){return isRecord(value)&&typeof value.id=="string"&&typeof value.source_file=="string"}function allowlistedEdgeFiles(link,sourceFiles){if(!isRecord(link))return null;const{source,target,relation}=link;if(typeof source!="string"||typeof target!="string"||typeof relation!="string"||!ALLOWED_RELATIONS.has(relation))return null;const sourceFile=sourceFiles.get(source),targetFile=sourceFiles.get(target);return sourceFile===void 0||targetFile===void 0?null:[sourceFile,targetFile]}function addDependentFiles(manifest,dependents,[source,target]){manifest.has(source)&&!manifest.has(target)&&dependents.add(target),manifest.has(target)&&!manifest.has(source)&&dependents.add(source)}function isIssueResponse(value){return isRecord(value)&&Array.isArray(value.labels)&&value.labels.every(label=>isRecord(label)&&typeof label.name=="string")&&Object.hasOwn(value,"milestone")}function isRecord(value){return typeof value=="object"&&value!==null}function isRepoRelativePosixPath(value){return value.length>0&&!value.startsWith("/")&&!value.includes("\\")&&!value.split("/").some(segment=>segment===""||segment==="."||segment==="..")}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{normalizeChainId}from"./task-state.js";export const DEFAULT_TRAIN_LIMITS={maxChain:10,maxAgeMinutes:480};export function resolveTrainLimits(ship){return{maxChain:ship?.train?.maxChain??DEFAULT_TRAIN_LIMITS.maxChain,maxAgeMinutes:ship?.train?.maxAgeMinutes??DEFAULT_TRAIN_LIMITS.maxAgeMinutes}}export function splitTrainIds(positional,explicitId,chain){const primary=explicitId??positional[0],extras=explicitId===void 0?positional.slice(1):positional,bare=id=>id.replace(/^#/,""),chainIds=[...extras,...chain].filter(id=>primary===void 0||bare(id)!==bare(primary));return{...primary!==void 0?{taskId:primary}:{},chainIds}}export function appendChainIds(existing,additions){const result=[...existing],seen=new Set(existing);for(const raw of additions){const id=normalizeChainId(raw);seen.has(id)||(seen.add(id),result.push(id))}return result}function ageMinutes(openedAt,now){if(openedAt===void 0)return null;const opened=Date.parse(openedAt);return Number.isNaN(opened)?null:(now.getTime()-opened)/6e4}export function evaluateSeal(signals,limits){if(signals.explicitSeal)return{sealed:!0,reason:"explicit",detail:"sealed on explicit request (--seal)"};if(signals.widenedTier==="Standard")return{sealed:!0,reason:"risk",detail:"the issue being added widens the tier to Standard \u2014 a risk-bearing issue rides its own train"};if(signals.chainSize>=limits.maxChain)return{sealed:!0,reason:"max-chain",detail:`the train already carries ${signals.chainSize} issue(s), the limit is ${limits.maxChain}`};const age=ageMinutes(signals.openedAt,signals.now);return age!==null&&age>limits.maxAgeMinutes?{sealed:!0,reason:"max-age",detail:`the train has been open ${Math.round(age)} min, the budget is ${limits.maxAgeMinutes} min`}:{sealed:!1}}export function hasShipTaskId(taskId){return taskId!==void 0&&taskId.length>0}export function shipTaskChanged(existing,taskId){return existing===null||taskId===void 0||existing.taskId.length===0||taskId.length===0?!1:existing.taskId.replace(/^#/,"")!==taskId.replace(/^#/,"")}function seedPrimaryCount(existing,taskId,taskChanged){return hasShipTaskId(taskId)||!taskChanged&&hasShipTaskId(existing?.taskId)?1:0}function seedChainIds(existing,chainIds,taskChanged){return chainIds??(taskChanged?[]:existing?.chainIds??[])}function seedProjectedSize(existing,taskId,chainIds){if(chainIds===void 0&&taskId===void 0)return null;const taskChanged=shipTaskChanged(existing,taskId);return seedPrimaryCount(existing,taskId,taskChanged)+seedChainIds(existing,chainIds,taskChanged).length}export function evaluateSeedSize(existing,taskId,chainIds,limits){const projectedSize=seedProjectedSize(existing,taskId,chainIds);return projectedSize===null||projectedSize<=limits.maxChain?{ok:!0}:{ok:!1,detail:`the requested seed would make the train carry ${projectedSize} issue(s), the limit is ${limits.maxChain}`}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{writeUnifiedState,appendLog}from"./task-state.js";export function runTaskMark(opts={}){const root=opts.dir??process.cwd(),cursor={};opts.tddPhase!==void 0&&(cursor.tddPhase=opts.tddPhase),opts.last!==void 0&&(cursor.lastAction=opts.last),opts.next!==void 0&&(cursor.nextAction=opts.next);const patch={cursor};opts.taskId!==void 0&&opts.taskId.length>0&&(patch.taskId=opts.taskId),writeUnifiedState(root,patch);const digest=opts.digest??(opts.next?`mark \u2192 next: ${opts.next}`:"mark");appendLog(root,digest)}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{join}from"node:path";import{createHash,randomBytes}from"node:crypto";import{sanitizeTaskId}from"../utils/task-id.js";import{readTaskId}from"./task-state.js";import{currentBranch,headSha}from"../evidence/git-checks.js";import{appendFileTranslated,ensureDir}from"../utils/fs.js";const NOTE_KINDS=["dup","smell","risk","debt","note"],NOTE_SEVERITIES=["low","med","high","info"];function resolveShard(opts,dir){if(opts.shardOverride!==void 0&&opts.shardOverride.length>0)return sanitizeTaskId(opts.shardOverride);const taskId=readTaskId(dir);if(taskId!==void 0&&taskId.length>0)return sanitizeTaskId(taskId);const branch=currentBranch(dir);return branch!=="unknown"&&branch.length>0?sanitizeTaskId(branch):"anon-"+randomBytes(4).toString("hex")}function normalizeWhitespace(s){return s.trim().replace(/\s+/g," ")}function normalizePath(file){return file===void 0||file.length===0?"":file.replace(/\\/g,"/").replace(/^\.\//,"")}function computeFingerprint(parts){const material=[parts.kind,normalizePath(parts.file),parts.symbol,normalizeWhitespace(parts.note)].join("\0");return createHash("sha1").update(material).digest("hex")}function buildEntry(opts,dir,note){const kind=opts.kind??"note",file=opts.file??"",symbol=opts.symbol??"",entry={ts:new Date().toISOString(),note,kind,severity:opts.severity??"info",foundDuring:readTaskId(dir)??"unknown",file:normalizePath(file),line:opts.line??null,sha:opts.shaOverride??headSha(dir),fingerprint:computeFingerprint({kind,file,symbol,note})};return opts.graphNode!==void 0&&opts.graphNode.length>0&&(entry.graphNode=opts.graphNode),entry}export function runTaskNote(opts){const dir=opts.dir??process.cwd(),note=opts.note.trim();if(note.length===0)return{ok:!1,reason:"note text must not be empty"};if(opts.kind!==void 0&&!NOTE_KINDS.includes(opts.kind))return{ok:!1,reason:`invalid --kind "${opts.kind}" (expected one of: ${NOTE_KINDS.join("|")})`};if(opts.severity!==void 0&&!NOTE_SEVERITIES.includes(opts.severity))return{ok:!1,reason:`invalid --severity "${opts.severity}" (expected one of: ${NOTE_SEVERITIES.join("|")})`};const entry=buildEntry(opts,dir,note),shard=resolveShard(opts,dir),findingsDir=join(dir,".arbiter","findings"),spoolPath=join(findingsDir,`${shard}.jsonl`);try{ensureDir(findingsDir),appendFileTranslated(spoolPath,JSON.stringify(entry)+`
|
|
3
|
+
`)}catch(err){return{ok:!1,reason:`findings spool write failed: ${err instanceof Error?err.message:String(err)}`}}return{ok:!0,spoolPath,fingerprint:entry.fingerprint}}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{dirname}from"node:path";import{runCli}from"../utils/run-cli.js";import{combineTestOutput,extractFailureSignature,repositoryRelativeLog,writeTddEvidence}from"../evidence/tdd.js";import{blobShaInCommit,currentBranch,hasDirtyTestPaths,pathExistsInCommit}from"../evidence/git-checks.js";import{normalizeChainId,readTaskId,readUnifiedState}from"./task-state.js";import{loadConfig}from"../utils/config.js";import{detectLanguage}from"../detectors/language.js";export const DEFAULT_RECORD_RED_TIMEOUT_MS=6e4,MAX_RECORD_RED_TIMEOUT_MS=6e5;function clampTimeout(ms){return ms===void 0?DEFAULT_RECORD_RED_TIMEOUT_MS:!Number.isFinite(ms)||ms<1e3?1e3:ms>MAX_RECORD_RED_TIMEOUT_MS?MAX_RECORD_RED_TIMEOUT_MS:Math.floor(ms)}function toGoPackageDir(dir){return dir===""||dir==="."?".":dir.startsWith("./")||dir.startsWith("/")||dir.startsWith("../")?dir:`./${dir}`}function resolveLanguage(dir){const stored=loadConfig(dir);return stored?.language?stored.language:detectLanguage(dir)}function selectRunner(language,testPath){if(/\.(test|spec)\.[cm]?[jt]sx?$/.test(testPath))return["npx","vitest","run",testPath];switch(language){case"go":return["go","test",toGoPackageDir(dirname(testPath))];case"python":return["pytest",testPath];default:return["npx","vitest","run",testPath]}}function isCliOutputError(err){return err!==null&&typeof err=="object"&&"stdout"in err&&typeof err.stdout=="string"&&"stderr"in err&&typeof err.stderr=="string"}function testCommandLaunchFailure(err){return{ok:!1,reason:`test command failed to launch: ${err instanceof Error?err.message:String(err)}`}}function captureTestOutput(cmd,args,dir,timeoutMs){try{const r=runCli(cmd,args,{cwd:dir,timeoutMs});return{log:combineTestOutput(r.stdout,r.stderr),exitCode:r.exitCode}}catch(err){return isCliOutputError(err)&&typeof err.exitCode=="number"&&err.exitCode>0&&err.timedOut!==!0&&err.notFound!==!0?{log:combineTestOutput(err.stdout,err.stderr),exitCode:err.exitCode}:testCommandLaunchFailure(err)}}function resolveHeadSha(dir,timeoutMs){try{return runCli("git",["rev-parse","HEAD"],{cwd:dir,timeoutMs}).stdout.trim()}catch(err){return{ok:!1,reason:`git rev-parse HEAD failed: ${err instanceof Error?err.message:String(err)}`}}}function checkTestCommitIntegrity(opts,sha,dir){return opts.force?null:hasDirtyTestPaths(dir)?{ok:!1,reason:"commit the RED test first \u2014 evidence must point at the commit that contains it (__tests__/** has uncommitted changes). Pass --force to override."}:pathExistsInCommit(sha,opts.testPath,dir)?null:{ok:!1,reason:`test_path "${opts.testPath}" not found in HEAD (${sha}) \u2014 evidence must point at a commit that contains the RED test. Pass --force to override.`}}export function taskIdFromBranch(branch){const m=branch.match(/^task\/#?(\d+)(?:-.*)?$/);return m?`#${m[1]}`:void 0}function resolveSelectedTaskId(requestedTaskId,activeTaskId,dir){if(requestedTaskId===void 0)return{taskId:activeTaskId};let taskId;try{taskId=normalizeChainId(requestedTaskId)}catch(err){return{ok:!1,reason:err instanceof Error?err.message:String(err)}}const chainIds=readUnifiedState(dir)?.chainIds??[];return taskId===activeTaskId||chainIds.some(id=>id===taskId)?{taskId}:{ok:!1,reason:`task ${taskId} is not the active task or a declared train secondary \u2014 add it with \`arbiter ship --chain-add ${taskId}\` before recording its RED evidence.`}}function resolveActiveTaskId(dir,requestedTaskId){const docTaskId=readTaskId(dir),branch=currentBranch(dir),branchTaskId=taskIdFromBranch(branch);if(branchTaskId!==void 0&&docTaskId!==void 0&&branchTaskId!==docTaskId)return{ok:!1,reason:`branch/task-document mismatch \u2014 refusing to guess which task's evidence to record. branch "${branch}" resolves to task ${branchTaskId}; the task document (.claude/.task/status.json) says ${docTaskId}. Run \`arbiter task init --id ${branchTaskId}\` to realign the task document with the current branch (or switch to the intended branch).`};const taskId=branchTaskId??docTaskId;if(taskId===void 0){if(requestedTaskId!==void 0)try{return{taskId:normalizeChainId(requestedTaskId)}}catch(err){return{ok:!1,reason:err instanceof Error?err.message:String(err)}}return{ok:!1,reason:"no active task \u2014 run `arbiter task init --id #NNN` (or `/task #NNN`) to initialise the task first"}}return resolveSelectedTaskId(requestedTaskId,taskId,dir)}export function runTaskRecordRed(opts){const dir=opts.dir??process.cwd(),timeoutMs=clampTimeout(opts.timeoutMs),resolution=resolveActiveTaskId(dir,opts.taskId);if("reason"in resolution)return resolution;const taskId=resolution.taskId,shaOrErr=resolveHeadSha(dir,5e3);if(typeof shaOrErr=="object")return shaOrErr;const sha=shaOrErr,integrityFailure=checkTestCommitIntegrity(opts,sha,dir);if(integrityFailure)return integrityFailure;const testCmd=opts.testCmd??selectRunner(resolveLanguage(dir),opts.testPath),outputOrErr=captureTestOutput(String(testCmd[0]),testCmd.slice(1),dir,timeoutMs);if("ok"in outputOrErr)return outputOrErr;if(outputOrErr.exitCode===0)return{ok:!1,reason:"test command exited 0 (suite passed) \u2014 a RED phase requires a failing run; refusing to mint RED evidence"};const log=repositoryRelativeLog(outputOrErr.log,dir),sig=extractFailureSignature(log);if(sig===null)return{ok:!1,reason:"no recognised failure signature in test output \u2014 the test appears to pass or produced unrecognised output. Tests must be RED before recording evidence."};const blob=blobShaInCommit(sha,opts.testPath,dir),evidence={$schemaVersion:1,task_id:taskId,test_path:opts.testPath,test_commit_sha:sha,...blob!==null?{test_blob_sha:blob}:{},test_run_log:log,observed_failure:sig.match,recorded_at:new Date().toISOString(),test_command:[...testCmd]};return saveEvidence(dir,evidence,sig.framework)}function saveEvidence(dir,evidence,framework){try{return{ok:!0,evidencePath:writeTddEvidence({repoDir:dir,evidence}),framework}}catch(err){return{ok:!1,reason:err instanceof Error?err.message:String(err)}}}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import{join}from"node:path";import{sanitizeTaskId}from"../utils/task-id.js";import{createGhIssue,appendTechDebtIssue}from"../utils/github-issue-helper.js";import{readTaskId}from"./task-state.js";import{appendFileTranslated,ensureDir}from"../utils/fs.js";function buildIssueBody(description,taskId){return["## Background","",`This issue was filed automatically by \`arbiter task record-tech-debt\` (triggered by ${taskId}).`,"","## Finding","",description,"","## Risk","","Unaddressed tech-debt may cause regressions or impede future feature work.","","## Remediation","","Review and address in a follow-up task.","","## Related","",`- Triggered by: ${taskId}`].join(`
|
|
3
|
+
`)}function resolveTaskId(opts,dir){return opts.triggeredBy?opts.triggeredBy:readTaskId(dir)??{ok:!1,reason:"no task-id: neither --triggered-by nor an active task found"}}export function runTaskRecordTechDebt(opts){const dir=opts.dir??process.cwd(),taskIdResult=resolveTaskId(opts,dir);if(typeof taskIdResult=="object")return taskIdResult;const taskId=taskIdResult,description=opts.description.trim();if(!description)return{ok:!1,reason:"--description must not be empty"};const ghResult=createGhIssue(dir,{title:`tech-debt: ${description}`,body:buildIssueBody(description,taskId),labels:["tech-debt","follow-up"]});if(!ghResult.ok)return ghResult;const issueNumber=ghResult.issueNumber,evidenceDir=join(dir,".arbiter","evidence",sanitizeTaskId(taskId));try{ensureDir(evidenceDir),appendTechDebtIssue(evidenceDir,issueNumber);const timestamp=new Date().toISOString().slice(0,16).replace("T"," ");appendFileTranslated(join(evidenceDir,"log.md"),[`## ${timestamp} \u2014 tech-debt issue #${issueNumber}`,`- ${description}`,`- triggered-by: ${taskId}`,""].join(`
|
|
4
|
+
`))}catch(err){return{ok:!1,reason:`evidence write failed: ${err instanceof Error?err.message:String(err)}`}}return{ok:!0,issueNumber}}
|