@webpresso/agent-kit 3.1.30 → 3.3.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/README.md +53 -15
- package/bin/_run.js +183 -20
- package/catalog/AGENTS.md.tpl +3 -3
- package/catalog/agent/agents/implementer.md +45 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
- package/catalog/agent/rules/pre-implementation.md +28 -1
- package/catalog/agent/rules/supported-agent-clis.md +9 -0
- package/catalog/agent/rules/typescript-version.md +50 -0
- package/catalog/agent/skills/codex/SKILL.md +25 -1
- package/catalog/agent/skills/verify/SKILL.md +4 -2
- package/catalog/docs/templates/blueprint.md +1 -1
- package/dist/esm/audit/agents.js +1 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
- package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
- package/dist/esm/audit/ci-test-perf.js +30 -0
- package/dist/esm/audit/command-surface.js +4 -2
- package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
- package/dist/esm/audit/host-substitution-risk.js +256 -0
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +13 -1
- package/dist/esm/audit/toolchain-isolation.js +1 -2
- package/dist/esm/audit/typescript-version.d.ts +2 -0
- package/dist/esm/audit/typescript-version.js +123 -0
- package/dist/esm/blueprint/core/parser.js +101 -2
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
- package/dist/esm/blueprint/core/validation/criteria.js +41 -2
- package/dist/esm/blueprint/core/validation/state.js +25 -14
- package/dist/esm/blueprint/db/migrations/run.js +4 -2
- package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
- package/dist/esm/blueprint/lifecycle/audit.js +87 -20
- package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
- package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
- package/dist/esm/blueprint/lifecycle/local.js +19 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
- package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/gates.d.ts +7 -0
- package/dist/esm/blueprint/trust/gates.js +20 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
- package/dist/esm/blueprint/trust/promotion.js +74 -112
- package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
- package/dist/esm/blueprint/trust/scaffold.js +64 -0
- package/dist/esm/blueprint/trust/validator.d.ts +1 -0
- package/dist/esm/blueprint/trust/validator.js +7 -1
- package/dist/esm/blueprint/utils/archive.js +27 -5
- package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
- package/dist/esm/blueprint/utils/document-paths.js +45 -12
- package/dist/esm/build/cli-mcp-parity.js +15 -0
- package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
- package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +43 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
- package/dist/esm/cli/commands/agent-launch.js +93 -4
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit-core.js +8 -1
- package/dist/esm/cli/commands/audit.js +21 -5
- package/dist/esm/cli/commands/blueprint/execution.js +18 -6
- package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
- package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
- package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
- package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
- package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
- package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
- package/dist/esm/cli/commands/blueprint/router.js +13 -1
- package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
- package/dist/esm/cli/commands/ci-preflight.js +30 -6
- package/dist/esm/cli/commands/compile.js +5 -31
- package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
- package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
- package/dist/esm/cli/commands/doctor.js +5 -0
- package/dist/esm/cli/commands/hook.d.ts +5 -0
- package/dist/esm/cli/commands/hook.js +15 -9
- package/dist/esm/cli/commands/init/config.js +2 -2
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
- package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
- package/dist/esm/cli/commands/init/convergence-state.js +54 -0
- package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
- package/dist/esm/cli/commands/init/convergence.js +57 -0
- package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
- package/dist/esm/cli/commands/init/git-convergence.js +168 -0
- package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
- package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
- package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
- package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
- package/dist/esm/cli/commands/init/host-visibility.js +94 -5
- package/dist/esm/cli/commands/init/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/index.js +326 -173
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
- package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
- package/dist/esm/cli/commands/init/merge.d.ts +1 -0
- package/dist/esm/cli/commands/init/merge.js +30 -1
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
- package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
- package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
- package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
- package/dist/esm/cli/commands/opencode-rotate.js +147 -0
- package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
- package/dist/esm/cli/commands/public-readiness.js +42 -0
- package/dist/esm/cli/commands/quality-runner.js +3 -3
- package/dist/esm/cli/commands/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +751 -209
- package/dist/esm/cli/commands/run-wait.d.ts +2 -0
- package/dist/esm/cli/commands/run-wait.js +122 -0
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/setup-help.js +1 -1
- package/dist/esm/cli/commands/setup-repair-help.js +1 -1
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/cli/direct-provider-launch.js +9 -5
- package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
- package/dist/esm/cli/optional-tool-freshness.js +219 -9
- package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
- package/dist/esm/daemon/domains/git-actions.js +136 -0
- package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
- package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
- package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
- package/dist/esm/daemon/domains/sessions-search.js +29 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
- package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
- package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
- package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
- package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/git-handlers.js +27 -0
- package/dist/esm/daemon/handlers/product.d.ts +7 -0
- package/dist/esm/daemon/handlers/product.js +10 -119
- package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
- package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
- package/dist/esm/daemon/handlers/shared.d.ts +5 -0
- package/dist/esm/daemon/handlers/shared.js +16 -0
- package/dist/esm/daemon/methods.d.ts +8 -1
- package/dist/esm/daemon/methods.js +7 -0
- package/dist/esm/daemon/protocol/contract.d.ts +43 -0
- package/dist/esm/daemon/protocol/contract.js +26 -0
- package/dist/esm/daemon/protocol/index.d.ts +1 -1
- package/dist/esm/daemon/protocol/index.js +1 -1
- package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
- package/dist/esm/hooks/__conformance__/matrix.js +137 -0
- package/dist/esm/hooks/conformance/batch.js +5 -0
- package/dist/esm/hooks/doctor.d.ts +5 -3
- package/dist/esm/hooks/doctor.js +67 -16
- package/dist/esm/hooks/permission-request/index.js +9 -3
- package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
- package/dist/esm/hooks/permission-request/policy.js +13 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
- package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/logger.js +16 -0
- package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/runner.js +68 -16
- package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
- package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
- package/dist/esm/hooks/shared/guard-outcome.js +215 -0
- package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
- package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
- package/dist/esm/hooks/shared/types.d.ts +8 -5
- package/dist/esm/hooks/shared/types.js +50 -9
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
- package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
- package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
- package/dist/esm/mcp/blueprint/registration.js +3 -2
- package/dist/esm/mcp/server.d.ts +1 -0
- package/dist/esm/mcp/server.js +27 -0
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +5 -0
- package/dist/esm/mcp/tools/_registry.js +10 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/result.js +44 -1
- package/dist/esm/mcp/tools/audit.d.ts +5 -1
- package/dist/esm/mcp/tools/audit.js +42 -0
- package/dist/esm/mcp/tools/audits.d.ts +4 -0
- package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
- package/dist/esm/mcp/tools/ci-preflight.js +183 -0
- package/dist/esm/mcp/tools/pr-upsert.js +39 -1
- package/dist/esm/mcp/tools/review-run.d.ts +28 -0
- package/dist/esm/mcp/tools/review-run.js +216 -0
- package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
- package/dist/esm/mcp/tools/run-wait.js +593 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/mcp/tools/session-id.d.ts +3 -0
- package/dist/esm/mcp/tools/session-id.js +45 -0
- package/dist/esm/mcp/tools/session-info.d.ts +3 -0
- package/dist/esm/mcp/tools/session-info.js +49 -0
- package/dist/esm/output-transforms/audit.d.ts +23 -0
- package/dist/esm/output-transforms/audit.js +47 -0
- package/dist/esm/output-transforms/index.js +2 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +27 -14
- package/dist/esm/review/availability.d.ts +33 -0
- package/dist/esm/review/availability.js +122 -20
- package/dist/esm/review/delivery-verifier.d.ts +27 -0
- package/dist/esm/review/delivery-verifier.js +115 -0
- package/dist/esm/review/events.d.ts +3 -2
- package/dist/esm/review/events.js +23 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +36 -10
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/output-policy.d.ts +2 -0
- package/dist/esm/review/execution/output-policy.js +12 -0
- package/dist/esm/review/execution/review-checkout.js +121 -21
- package/dist/esm/review/execution/supervisor.js +36 -4
- package/dist/esm/review/execution/types.d.ts +9 -0
- package/dist/esm/review/opencode-account.d.ts +24 -0
- package/dist/esm/review/opencode-account.js +68 -0
- package/dist/esm/review/opencode-auth.d.ts +33 -0
- package/dist/esm/review/opencode-auth.js +79 -0
- package/dist/esm/review/subject.d.ts +25 -0
- package/dist/esm/review/subject.js +116 -26
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +32 -25
- package/dist/esm/test/duration-sequencer.d.ts +20 -0
- package/dist/esm/test/duration-sequencer.js +54 -0
- package/dist/esm/test/shard-durations.json +922 -0
- package/dist/esm/test/worker-budget.d.ts +6 -0
- package/dist/esm/test/worker-budget.js +59 -1
- package/dist/esm/test-helpers/global-setup.d.ts +1 -2
- package/dist/esm/test-helpers/global-setup.js +79 -32
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/ultragoal/service.d.ts +7 -0
- package/dist/esm/ultragoal/service.js +42 -2
- package/dist/esm/utils/write-json-file.js +3 -1
- package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
- package/dist/esm/worktrees/codex-project-trust.js +56 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/dist/esm/worktrees/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +18 -13
package/dist/esm/cli/cli.js
CHANGED
|
@@ -55,9 +55,11 @@ const SUPPORTED_COMMANDS = [
|
|
|
55
55
|
"e2e",
|
|
56
56
|
"ci",
|
|
57
57
|
"ci-preflight",
|
|
58
|
+
"public:readiness",
|
|
58
59
|
"typecheck",
|
|
59
60
|
"lint",
|
|
60
61
|
"format",
|
|
62
|
+
"rust-check",
|
|
61
63
|
"logs",
|
|
62
64
|
"session-id",
|
|
63
65
|
"session-info",
|
|
@@ -70,6 +72,7 @@ const SUPPORTED_COMMANDS = [
|
|
|
70
72
|
"hook",
|
|
71
73
|
"hooks",
|
|
72
74
|
"self-install-guard",
|
|
75
|
+
"optional-tool-refresh",
|
|
73
76
|
"gain",
|
|
74
77
|
"bench",
|
|
75
78
|
"install",
|
|
@@ -101,6 +104,10 @@ const ROOT_HELP_CORE_ENTRIES = [
|
|
|
101
104
|
command: "format",
|
|
102
105
|
description: "Format through the portable wp surface (--check for CI/husky)",
|
|
103
106
|
},
|
|
107
|
+
{
|
|
108
|
+
command: "rust-check",
|
|
109
|
+
description: "Prove affected tracked Rust crates compile and pass tests (cargo check + test)",
|
|
110
|
+
},
|
|
104
111
|
{
|
|
105
112
|
command: "e2e",
|
|
106
113
|
description: "Build and run E2E commands through the portable webpresso surface",
|
|
@@ -111,6 +118,10 @@ const ROOT_HELP_CORE_ENTRIES = [
|
|
|
111
118
|
command: "ci-preflight",
|
|
112
119
|
description: "Run the locally-runnable required CI checks fail-fast (the pre-push gate)",
|
|
113
120
|
},
|
|
121
|
+
{
|
|
122
|
+
command: "public:readiness",
|
|
123
|
+
description: "Run the public package readiness gate through the portable wp surface",
|
|
124
|
+
},
|
|
114
125
|
{ command: "logs", description: "Print persisted raw output from recent quality runs" },
|
|
115
126
|
{ command: "dev", description: "Run a manifest-backed development target" },
|
|
116
127
|
{ command: "status", description: "Show unified local readiness and next action" },
|
|
@@ -364,6 +375,10 @@ const COMMAND_REGISTRARS = {
|
|
|
364
375
|
const { registerCiPreflightCommand } = await import("./commands/ci-preflight.js");
|
|
365
376
|
registerCiPreflightCommand(cli);
|
|
366
377
|
},
|
|
378
|
+
"public:readiness": async (cli) => {
|
|
379
|
+
const { registerPublicReadinessCommand } = await import("./commands/public-readiness.js");
|
|
380
|
+
registerPublicReadinessCommand(cli);
|
|
381
|
+
},
|
|
367
382
|
typecheck: async (cli) => {
|
|
368
383
|
const { registerTypecheckCommand } = await import("./commands/typecheck.js");
|
|
369
384
|
registerTypecheckCommand(cli);
|
|
@@ -376,6 +391,10 @@ const COMMAND_REGISTRARS = {
|
|
|
376
391
|
const { registerFormatCommand } = await import("./commands/format.js");
|
|
377
392
|
registerFormatCommand(cli);
|
|
378
393
|
},
|
|
394
|
+
"rust-check": async (cli) => {
|
|
395
|
+
const { registerRustCheckCommand } = await import("./commands/rust-check.js");
|
|
396
|
+
registerRustCheckCommand(cli);
|
|
397
|
+
},
|
|
379
398
|
logs: async (cli) => {
|
|
380
399
|
const { registerLogsCommand } = await import("./commands/logs.js");
|
|
381
400
|
registerLogsCommand(cli);
|
|
@@ -422,6 +441,15 @@ const COMMAND_REGISTRARS = {
|
|
|
422
441
|
const { registerSelfInstallGuardCommand } = await import("./commands/self-install-guard.js");
|
|
423
442
|
registerSelfInstallGuardCommand(cli);
|
|
424
443
|
},
|
|
444
|
+
"optional-tool-refresh": async (cli) => {
|
|
445
|
+
cli
|
|
446
|
+
.command("optional-tool-refresh <tool> [...args]", "Internal detached optional-tool freshness worker")
|
|
447
|
+
.allowUnknownOptions()
|
|
448
|
+
.action(async (tool, args = []) => {
|
|
449
|
+
const { runOptionalToolRefreshWorker } = await import("./optional-tool-freshness.js");
|
|
450
|
+
process.exitCode = runOptionalToolRefreshWorker([tool, ...args]);
|
|
451
|
+
});
|
|
452
|
+
},
|
|
425
453
|
gain: async (cli) => {
|
|
426
454
|
const { registerGainCommand } = await import("./commands/gain/index.js");
|
|
427
455
|
registerGainCommand(cli);
|
|
@@ -636,6 +664,21 @@ export async function main() {
|
|
|
636
664
|
console.error(formatUnknownCommandError(command, SUPPORTED_COMMANDS));
|
|
637
665
|
return 1;
|
|
638
666
|
}
|
|
667
|
+
// `wp run wait` is the read-only workflow-run waiter, intercepted before the
|
|
668
|
+
// package-manager `run` facade (which maps `wp run <script>` to `vp run
|
|
669
|
+
// <script>`). Without this, `wait` would be treated as a package script name.
|
|
670
|
+
// Errors resolve to an exit code here, mirroring main()'s catch below, so a
|
|
671
|
+
// bad flag never rejects the main() promise.
|
|
672
|
+
if (resolvedCommand === "run" && argv[3] === "wait") {
|
|
673
|
+
const { runRunWaitCommand } = await import("./commands/run-wait.js");
|
|
674
|
+
try {
|
|
675
|
+
return await runRunWaitCommand(argv.slice(4));
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
679
|
+
return 1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
639
682
|
await COMMAND_REGISTRARS[resolvedCommand](cli, resolvedCommand);
|
|
640
683
|
if (resolvedCommand === "session") {
|
|
641
684
|
const { runSessionCommand } = await import("./commands/session.js");
|
|
@@ -2,6 +2,8 @@ import type { CAC } from "cac";
|
|
|
2
2
|
import { type DashPermissionMode, type DashProvider } from "#cli/commands/dash/provider-capabilities.js";
|
|
3
3
|
import { type RuntimeCommandOptions } from "#runtime/executor.js";
|
|
4
4
|
import { type SecretsConfig } from "#runtime/secrets-config.js";
|
|
5
|
+
import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
|
|
6
|
+
import { resolveHealthyOpencodeAccount } from "#cli/commands/opencode-probe.js";
|
|
5
7
|
export declare const AGENT_LAUNCH_COMMANDS: readonly ["claude", "codex", "grok", "opencode"];
|
|
6
8
|
export type AgentLaunchCommand = DashProvider;
|
|
7
9
|
export declare const DEFAULT_AGENT_SECRET_PROFILE = "preview";
|
|
@@ -18,6 +20,11 @@ export interface AgentLaunchDeps {
|
|
|
18
20
|
error?: Error;
|
|
19
21
|
};
|
|
20
22
|
readonly ensureCodexSkillSurface?: (cwd: string, env?: NodeJS.ProcessEnv) => string | null;
|
|
23
|
+
readonly resolveOpencodeAccounts?: typeof resolveOpencodeAccountsGracefully;
|
|
24
|
+
readonly materializeGlobalOpencodeAuth?: (key: string) => void;
|
|
25
|
+
readonly readActiveKey?: () => string | undefined;
|
|
26
|
+
readonly recordCooldown?: (canonicalId: string) => Promise<unknown>;
|
|
27
|
+
readonly resolveHealthyOpencodeAccount?: typeof resolveHealthyOpencodeAccount;
|
|
21
28
|
}
|
|
22
29
|
export declare function buildDefaultAgentArgs(command: AgentLaunchCommand, args?: readonly string[], permissionMode?: DashPermissionMode): readonly string[];
|
|
23
30
|
export declare function isAgentLaunchCommand(command: string | undefined): command is AgentLaunchCommand;
|
|
@@ -29,5 +36,5 @@ export declare function buildAgentRuntimeCommandOptions(input: {
|
|
|
29
36
|
readonly env?: NodeJS.ProcessEnv;
|
|
30
37
|
readonly secretsConfig: SecretsConfig | null;
|
|
31
38
|
}): RuntimeCommandOptions;
|
|
32
|
-
export declare function runAgentLaunchCommand(command: AgentLaunchCommand, args?: readonly string[], options?: AgentLaunchOptions, deps?: AgentLaunchDeps): number
|
|
39
|
+
export declare function runAgentLaunchCommand(command: AgentLaunchCommand, args?: readonly string[], options?: AgentLaunchOptions, deps?: AgentLaunchDeps): Promise<number>;
|
|
33
40
|
export declare function registerAgentLaunchCommand(cli: CAC, command: AgentLaunchCommand): void;
|
|
@@ -7,6 +7,12 @@ import { WP_AGENT_LAUNCHED_VIA_WP_ENV, WP_AGENT_LAUNCHED_VIA_WP_VALUE, } from "#
|
|
|
7
7
|
import { readSecretsConfig } from "#runtime/secrets-config.js";
|
|
8
8
|
import { resolveFreshAgentKitPackageRoot } from "#cli/commands/init/package-root";
|
|
9
9
|
import { ensureCodexUserPlugin } from "#cli/commands/init/scaffolders/codex-plugin/index.js";
|
|
10
|
+
import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
|
|
11
|
+
import { loadOpencodeAccountAvailability, pruneExpiredOpencodeAccountSuppressions, } from "#review/availability.js";
|
|
12
|
+
import { selectActiveOpencodeAccount } from "#review/opencode-account.js";
|
|
13
|
+
import { materializeOpencodeGoAuth } from "#review/opencode-auth.js";
|
|
14
|
+
import { describeOpencodeRotate, OPENCODE_ROTATE_FLAG, rotateActiveOpencodeAccount, } from "#cli/commands/opencode-rotate.js";
|
|
15
|
+
import { OPENCODE_SKIP_PROBE_ENV, resolveHealthyOpencodeAccount, } from "#cli/commands/opencode-probe.js";
|
|
10
16
|
export const AGENT_LAUNCH_COMMANDS = DASH_PROVIDERS;
|
|
11
17
|
export const DEFAULT_AGENT_SECRET_PROFILE = "preview";
|
|
12
18
|
export const AGENT_SECRET_PROFILE_ENV = "WP_AGENT_SECRET_PROFILE";
|
|
@@ -83,7 +89,77 @@ function ensureCodexSkillSurface(cwd, env = process.env) {
|
|
|
83
89
|
return `Codex plugin refresh failed (${result.kind}). Run \`wp install codex\` and retry.`;
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Choose the active opencode-go account, verify it against the real provider,
|
|
94
|
+
* and materialize its key into the global opencode `auth.json` before the TUI
|
|
95
|
+
* launches.
|
|
96
|
+
*
|
|
97
|
+
* A launch-time health probe (`resolveHealthyOpencodeAccount`) spawns a bounded
|
|
98
|
+
* `opencode run` against the candidate's isolated credential store; a detected
|
|
99
|
+
* usage limit cools that account with the provider's real reset window and the
|
|
100
|
+
* next account is probed instead. So a plain relaunch now *does* rotate past an
|
|
101
|
+
* exhausted account, and `wp opencode --wp-rotate` is only needed to force a
|
|
102
|
+
* swap off an account that is still answering.
|
|
103
|
+
*
|
|
104
|
+
* Honest limitation: interactive opencode is a long-lived TUI — the account is
|
|
105
|
+
* chosen once at launch, so a cap hit *mid-session* is still NOT auto-swapped.
|
|
106
|
+
* Only the review gate does in-flight failover, because only it observes every
|
|
107
|
+
* provider response for the life of the run.
|
|
108
|
+
*
|
|
109
|
+
* Never throws: a Doppler outage, a failed probe, or a materialize failure must
|
|
110
|
+
* not block the TUI from launching, so every failure is logged and swallowed.
|
|
111
|
+
*/
|
|
112
|
+
async function selectAndMaterializeOpencodeAccount(cwd, deps, secretProfile, env) {
|
|
113
|
+
try {
|
|
114
|
+
const resolution = (deps.resolveOpencodeAccounts ?? resolveOpencodeAccountsGracefully)({
|
|
115
|
+
cwd,
|
|
116
|
+
secretProfile,
|
|
117
|
+
});
|
|
118
|
+
if (!resolution.ok)
|
|
119
|
+
return;
|
|
120
|
+
const pruned = pruneExpiredOpencodeAccountSuppressions(loadOpencodeAccountAvailability());
|
|
121
|
+
const initial = selectActiveOpencodeAccount(resolution.accounts, pruned.state);
|
|
122
|
+
if (!initial)
|
|
123
|
+
return;
|
|
124
|
+
const probed = await (deps.resolveHealthyOpencodeAccount ?? resolveHealthyOpencodeAccount)(resolution.accounts, initial, { env });
|
|
125
|
+
for (const line of probed.lines)
|
|
126
|
+
console.error(line);
|
|
127
|
+
const selection = probed.selection;
|
|
128
|
+
(deps.materializeGlobalOpencodeAuth ?? ((key) => materializeOpencodeGoAuth(key)))(selection.account.key);
|
|
129
|
+
if (selection.cooled && probed.lines.length === 0) {
|
|
130
|
+
console.error(`wp opencode: all OpenCode Go accounts are within their usage cooldown; launching on the soonest-expiring account (${selection.account.id}).`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
135
|
+
console.error(`wp opencode: account selection failed, launching without it (${message}).`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Run the opencode-only pre-launch credential step and return the args to
|
|
140
|
+
* forward to the binary. `--wp-rotate` is consumed here and must never reach
|
|
141
|
+
* opencode, which would treat it as a prompt.
|
|
142
|
+
*/
|
|
143
|
+
async function prepareOpencodeLaunch(cwd, args, secretsConfig, deps, secretProfile, env) {
|
|
144
|
+
if (!args.includes(OPENCODE_ROTATE_FLAG)) {
|
|
145
|
+
if (secretsConfig !== null) {
|
|
146
|
+
await selectAndMaterializeOpencodeAccount(cwd, deps, secretProfile, env);
|
|
147
|
+
}
|
|
148
|
+
return args;
|
|
149
|
+
}
|
|
150
|
+
const outcome = await rotateActiveOpencodeAccount(cwd, secretProfile, deps);
|
|
151
|
+
for (const line of describeOpencodeRotate(outcome))
|
|
152
|
+
console.error(line);
|
|
153
|
+
// A successful rotation already materialized the next account, so the normal
|
|
154
|
+
// launch-time selection is skipped to avoid a second, redundant write. When
|
|
155
|
+
// rotation could not run, fall through to that selection so the launch still
|
|
156
|
+
// gets the best account it can rather than whatever the store happens to hold.
|
|
157
|
+
if (outcome.kind === "unavailable" && secretsConfig !== null) {
|
|
158
|
+
await selectAndMaterializeOpencodeAccount(cwd, deps, secretProfile, env);
|
|
159
|
+
}
|
|
160
|
+
return args.filter((arg) => arg !== OPENCODE_ROTATE_FLAG);
|
|
161
|
+
}
|
|
162
|
+
export async function runAgentLaunchCommand(command, args = [], options = {}, deps = {}) {
|
|
87
163
|
const cwd = options.cwd ?? process.cwd();
|
|
88
164
|
if (command === "codex") {
|
|
89
165
|
const skillSurfaceError = (deps.ensureCodexSkillSurface ?? ensureCodexSkillSurface)(cwd, options.env ?? process.env);
|
|
@@ -93,9 +169,19 @@ export function runAgentLaunchCommand(command, args = [], options = {}, deps = {
|
|
|
93
169
|
}
|
|
94
170
|
}
|
|
95
171
|
const secretsConfig = (deps.readSecretsConfig ?? readSecretsConfig)(cwd);
|
|
172
|
+
// Account discovery must resolve secrets through the same profile the launch
|
|
173
|
+
// itself uses (below, via buildAgentRuntimeCommandOptions). Without it no
|
|
174
|
+
// environment selector reaches the secret manager, which then falls back to
|
|
175
|
+
// its own ambient, path-scoped config — that resolves only in a checkout that
|
|
176
|
+
// happens to sit under the scoped directory and fails in every managed
|
|
177
|
+
// worktree, CI clone, and fresh checkout.
|
|
178
|
+
const secretProfile = resolveAgentSecretProfile(options.env ?? process.env);
|
|
179
|
+
const launchArgs = command === "opencode"
|
|
180
|
+
? await prepareOpencodeLaunch(cwd, args, secretsConfig, deps, secretProfile, options.env ?? process.env)
|
|
181
|
+
: args;
|
|
96
182
|
const runtimeOptions = buildAgentRuntimeCommandOptions({
|
|
97
183
|
command,
|
|
98
|
-
args,
|
|
184
|
+
args: launchArgs,
|
|
99
185
|
cwd,
|
|
100
186
|
env: options.env ?? process.env,
|
|
101
187
|
secretsConfig,
|
|
@@ -108,8 +194,11 @@ export function runAgentLaunchCommand(command, args = [], options = {}, deps = {
|
|
|
108
194
|
return result.status ?? 1;
|
|
109
195
|
}
|
|
110
196
|
export function registerAgentLaunchCommand(cli, command) {
|
|
197
|
+
const rotateHint = command === "opencode"
|
|
198
|
+
? ` (the selected OpenCode Go account is health-probed first and a capped one is rotated past automatically; pass \`${OPENCODE_ROTATE_FLAG}\` to force a rotation, or set \`${OPENCODE_SKIP_PROBE_ENV}=1\` to skip the probe)`
|
|
199
|
+
: "";
|
|
111
200
|
cli
|
|
112
|
-
.command(`${command} [...args]`, `Launch ${command} with provider-backed agent secrets and safe-by-default permissions (run \`wp yolo on\` for persistent full-auto)`)
|
|
201
|
+
.command(`${command} [...args]`, `Launch ${command} with provider-backed agent secrets and safe-by-default permissions (run \`wp yolo on\` for persistent full-auto)${rotateHint}`)
|
|
113
202
|
.allowUnknownOptions()
|
|
114
|
-
.action((args = []) => runAgentLaunchCommand(command, args, { cwd: process.cwd() }));
|
|
203
|
+
.action(async (args = []) => await runAgentLaunchCommand(command, args, { cwd: process.cwd() }));
|
|
115
204
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getAuditKindDescriptor } from "#audit/registry";
|
|
1
2
|
function optionsForCompositeRepoAudit(name, options) {
|
|
2
3
|
if (name !== "reference-parity-matrix" || options.strict !== true)
|
|
3
4
|
return options;
|
|
@@ -94,7 +95,13 @@ export async function runAuditDispatch(auditKind, targets, options, deps) {
|
|
|
94
95
|
let allOk = true;
|
|
95
96
|
for (const name of deps.knownRepoKinds) {
|
|
96
97
|
const result = await deps.runRepoAudit(name, root, optionsForCompositeRepoAudit(name, options));
|
|
97
|
-
|
|
98
|
+
// Signal-class kinds (registry.ts gateClass) still run and still
|
|
99
|
+
// report their findings via `results` below, but a failing signal
|
|
100
|
+
// kind must not flip the umbrella — only blocking-class failures do.
|
|
101
|
+
// Unclassified kinds default to blocking, matching the registry's
|
|
102
|
+
// own descriptor() default.
|
|
103
|
+
const gateClass = getAuditKindDescriptor(name)?.gateClass ?? "blocking";
|
|
104
|
+
if (!result.ok && gateClass !== "signal")
|
|
98
105
|
allOk = false;
|
|
99
106
|
results.push({ name, result });
|
|
100
107
|
}
|
|
@@ -15,6 +15,7 @@ import { createCliLogSink } from "#cli/commands/quality-log-store.js";
|
|
|
15
15
|
import { emitCliCommandOutput, runLoggedChildCommand } from "#cli/commands/quality-runner.js";
|
|
16
16
|
export const REPO_AUDIT_REGISTRY = {
|
|
17
17
|
"catalog-drift": async (root) => (await import("#audit/repo-guardrails")).auditCatalogDrift(root),
|
|
18
|
+
"typescript-version": async (root) => (await import("#audit/typescript-version")).auditTypescriptVersion(root),
|
|
18
19
|
"package-surface": async (root) => (await import("#audit/package-surface")).auditPackageSurface(root),
|
|
19
20
|
"command-surface": async (root) => (await import("#audit/command-surface")).auditCommandSurface(root),
|
|
20
21
|
"reference-parity-matrix": async (root, options) => (await import("#audit/reference-parity-matrix")).auditReferenceParityMatrix(root, undefined, {
|
|
@@ -123,6 +124,7 @@ export const REPO_AUDIT_REGISTRY = {
|
|
|
123
124
|
"secrets-config": async (root) => (await import("#audit/secrets-config")).auditSecretsConfig(root),
|
|
124
125
|
"consumer-agent-kit-dependency": async (root) => (await import("#audit/consumer-agent-kit-dependency")).auditConsumerAgentKitDependency(root),
|
|
125
126
|
"ci-test-perf": async (root) => (await import("#audit/ci-test-perf")).auditCiTestPerf(root),
|
|
127
|
+
"ci-guardrails-wiring": async (root) => (await import("#audit/ci-guardrails-wiring")).auditCiGuardrailsWiring(root),
|
|
126
128
|
"test-scan-perf": async (root, options) => (await import("#audit/test-scan-perf")).auditTestScanPerf(root, {
|
|
127
129
|
affectedFiles: options.affectedFiles,
|
|
128
130
|
}),
|
|
@@ -140,9 +142,14 @@ export const REPO_AUDIT_REGISTRY = {
|
|
|
140
142
|
draftTechDebt: options.draftTechDebt,
|
|
141
143
|
}),
|
|
142
144
|
"harness-overlay-evidence": async (root) => (await import("#audit/harness-overlay-evidence")).auditHarnessOverlayEvidence(root),
|
|
145
|
+
"host-substitution-risk": async (root) => (await import("#audit/host-substitution-risk")).auditHostSubstitutionRisk(root),
|
|
143
146
|
"legacy-workflow-identity": async (root) => (await import("#audit/legacy-workflow-identity")).auditLegacyWorkflowIdentity(root),
|
|
144
147
|
rules: async (root) => runContentAudit(root, "rule"),
|
|
145
148
|
skills: async (root) => runContentAudit(root, "skill"),
|
|
149
|
+
"changeset-required": async (root, options) => (await import("#audit/changeset-required")).auditChangesetRequired(root, {
|
|
150
|
+
baseRef: options.baseRef ?? options.base,
|
|
151
|
+
changedFiles: options.affectedFiles,
|
|
152
|
+
}),
|
|
146
153
|
};
|
|
147
154
|
async function runContentAudit(root, kind) {
|
|
148
155
|
const { auditContent } = await import("../../content/audit.js");
|
|
@@ -467,22 +474,31 @@ export function registerAuditCommand(cli) {
|
|
|
467
474
|
}
|
|
468
475
|
case "aggregate-result": {
|
|
469
476
|
const { formatRepoAuditReport } = await import("#audit/repo-guardrails");
|
|
477
|
+
const isSignal = (name) => getAuditKindDescriptor(name)?.gateClass === "signal";
|
|
470
478
|
for (const { name, result } of outcome.results) {
|
|
471
479
|
if (result.ok)
|
|
472
480
|
continue;
|
|
473
|
-
|
|
481
|
+
const label = isSignal(name) ? `${name} (signal, non-blocking)` : name;
|
|
482
|
+
sink.write(`\n[${label}]\n${formatRepoAuditReport(result)}\n`);
|
|
474
483
|
}
|
|
475
484
|
const failed = outcome.results.filter(({ result }) => !result.ok);
|
|
485
|
+
const blockingFailed = failed.filter(({ name }) => !isSignal(name));
|
|
486
|
+
const signalFailed = failed.filter(({ name }) => isSignal(name));
|
|
476
487
|
if (failed.length > 0) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
.join(", ")}
|
|
488
|
+
const parts = [];
|
|
489
|
+
if (blockingFailed.length > 0) {
|
|
490
|
+
parts.push(`${blockingFailed.length} blocking: ${blockingFailed.map(({ name }) => name).join(", ")}`);
|
|
491
|
+
}
|
|
492
|
+
if (signalFailed.length > 0) {
|
|
493
|
+
parts.push(`${signalFailed.length} signal/non-blocking: ${signalFailed.map(({ name }) => name).join(", ")}`);
|
|
494
|
+
}
|
|
495
|
+
sink.write(`\nguardrails: ${failed.length}/${outcome.results.length} audits reported findings (${parts.join("; ")})\n`);
|
|
480
496
|
}
|
|
481
497
|
exitCode = outcome.code;
|
|
482
498
|
summary =
|
|
483
499
|
outcome.code === 0
|
|
484
500
|
? "audit guardrails passed"
|
|
485
|
-
: `audit guardrails failed (${
|
|
501
|
+
: `audit guardrails failed (${blockingFailed.length}/${outcome.results.length})`;
|
|
486
502
|
break;
|
|
487
503
|
}
|
|
488
504
|
case "quality-exit": {
|
|
@@ -9,6 +9,7 @@ import { setTimeout as delay } from "node:timers/promises";
|
|
|
9
9
|
import { applyRuntimeProgressSnapshot, buildBlueprintProgressBridgeState, normalizeRuntimeTaskSnapshot, projectBlueprintLifecycleFromRuntime, readBlueprintExecutionArtifacts, runtimeStateSnapshotSchema, writeBlueprintExecutionArtifacts, writeBlueprintExecutionMetadata, } from "#index";
|
|
10
10
|
import { applyBlueprintLifecycleToFile, parseBlueprint } from "#local";
|
|
11
11
|
import { resolveBlueprintRoot } from "#utils/blueprint-root";
|
|
12
|
+
import { resolveBlueprintDocumentIdentity } from "#utils/document-paths.js";
|
|
12
13
|
import { clearBlueprintExecutionState, persistBlueprintExecutionArtifacts, persistBlueprintExecutionMetadata, persistBlueprintProgressBridgeState, readBlueprintExecutionArtifactsState, readBlueprintExecutionState, readBlueprintProgressBridgeState, readBlueprintRuntimeSnapshot, writeBlueprintRuntimeSnapshot, } from "./execution-io.js";
|
|
13
14
|
import { assertCompletionEvidence, mergeExecutionArtifacts, } from "./execution-state.js";
|
|
14
15
|
import { buildBlueprintExecutionControlCommand, buildBlueprintExecutionLaunchCommand, buildBlueprintExecutionRuntimePaths, buildBlueprintLaunchSpec, buildListTasksVerificationCommand, isMissingFileError, nowIsoTimestamp, parseRuntimeApiResponse, parseTeamExecutionId, resolveControlStatus, toProjectRelativePath, uniqueStrings, } from "./execution-spec.js";
|
|
@@ -114,22 +115,33 @@ export async function reconcileBlueprintRuntimeSnapshot(projectRoot, blueprintPa
|
|
|
114
115
|
const currentStatus = currentDir.split(blueprintsRoot + path.sep)[1]?.split(path.sep)[0];
|
|
115
116
|
const relativeSlug = slug.replace(/^[^/]+\//u, "");
|
|
116
117
|
const targetDir = path.join(blueprintsRoot, nextStatus, relativeSlug);
|
|
117
|
-
|
|
118
|
+
// A flat document owns only its own file; renaming its parent directory
|
|
119
|
+
// would move the entire lifecycle state directory. Lifecycle moves are
|
|
120
|
+
// shape-preserving: flat stays flat, folder moves as a folder.
|
|
121
|
+
const isFlatDocument = resolveBlueprintDocumentIdentity(blueprintsRoot, blueprintPath)?.shape === "flat";
|
|
122
|
+
const targetPath = isFlatDocument ? `${targetDir}.md` : path.join(targetDir, "_overview.md");
|
|
123
|
+
const documentMoved = blueprintPath !== targetPath;
|
|
118
124
|
const nextMarkdown = completionEvidence
|
|
119
125
|
? writeBlueprintExecutionArtifacts(result.markdown, completionEvidence)
|
|
120
126
|
: result.markdown;
|
|
121
|
-
if (
|
|
127
|
+
if (documentMoved && currentStatus && currentStatus !== nextStatus) {
|
|
122
128
|
const { mkdir, rename } = await import("node:fs/promises");
|
|
123
|
-
|
|
124
|
-
|
|
129
|
+
if (isFlatDocument) {
|
|
130
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
131
|
+
await rename(blueprintPath, targetPath);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
await mkdir(path.dirname(targetDir), { recursive: true });
|
|
135
|
+
await rename(currentDir, targetDir);
|
|
136
|
+
}
|
|
125
137
|
await writeFile(targetPath, nextMarkdown, "utf-8");
|
|
126
138
|
}
|
|
127
139
|
else {
|
|
128
140
|
await writeFile(blueprintPath, nextMarkdown, "utf-8");
|
|
129
141
|
}
|
|
130
142
|
return {
|
|
131
|
-
moved:
|
|
132
|
-
path:
|
|
143
|
+
moved: documentMoved,
|
|
144
|
+
path: documentMoved ? targetPath : blueprintPath,
|
|
133
145
|
status: result.execution.status,
|
|
134
146
|
};
|
|
135
147
|
}
|
|
@@ -24,9 +24,9 @@ import { getBlueprintDocumentPaths } from "#utils/document-paths.js";
|
|
|
24
24
|
import { resolveBlueprintRoot } from "#utils/blueprint-root.js";
|
|
25
25
|
import { reIngestProjection } from "#projection-ready.js";
|
|
26
26
|
import { assertAllTasksHaveCanonicalPassingEvidence } from "#verification.js";
|
|
27
|
-
import {
|
|
27
|
+
import { assertPromotionTrustProofCurrent, preparePromotionTrustGate, } from "#trust/promotion.js";
|
|
28
28
|
import matter from "gray-matter";
|
|
29
|
-
import { evaluateApprovalGate, formatApprovalGateRequirement } from "#lifecycle/audit";
|
|
29
|
+
import { assertCompletedApprovalGate, evaluateApprovalGate, formatApprovalGateRequirement, } from "#lifecycle/audit";
|
|
30
30
|
/**
|
|
31
31
|
* Module-level factory. `null` = use the production default (loadSyncCredentials
|
|
32
32
|
* from auth.ts + BlueprintSyncClient + ReplicaManager — lazy-imported so that
|
|
@@ -321,24 +321,38 @@ async function advanceTaskLocked(cwd, blueprintSlug, taskId, toStatus) {
|
|
|
321
321
|
* - Re-ingests into DB
|
|
322
322
|
*/
|
|
323
323
|
export async function promoteBlueprint(cwd, slug, toState) {
|
|
324
|
+
const adapter = await resolveSyncAdapterForCli(cwd);
|
|
325
|
+
// Pull platform state under the mutation lock, then release it before any
|
|
326
|
+
// Trust Dossier command spawns a child `wp`. Child startup/ingestion can
|
|
327
|
+
// legitimately need the same repo-scoped lock.
|
|
328
|
+
await withMarkdownWriteLock(cwd, () => runCliPlatformMutationSync(adapter, {
|
|
329
|
+
label: "wp_blueprint_promote",
|
|
330
|
+
ensureFreshSlug: slug,
|
|
331
|
+
}));
|
|
332
|
+
const found = findBlueprintDocument(resolveBlueprintRoot(cwd), slug);
|
|
333
|
+
if (!found) {
|
|
334
|
+
throw new Error(`Blueprint "${slug}" not found in any state directory under ${resolveBlueprintRoot(cwd)}`);
|
|
335
|
+
}
|
|
336
|
+
const crossesDraftApprovalBoundary = found.state === "draft" && (toState === "planned" || toState === "in-progress");
|
|
337
|
+
const trustProof = crossesDraftApprovalBoundary
|
|
338
|
+
? preparePromotionTrustGate({
|
|
339
|
+
repoRoot: cwd,
|
|
340
|
+
file: found.documentPath,
|
|
341
|
+
markdown: readFileSync(found.documentPath, "utf8"),
|
|
342
|
+
})
|
|
343
|
+
: null;
|
|
324
344
|
// F9/R7: cross-worktree markdown writes serialize via the repo-scoped lock.
|
|
325
|
-
|
|
345
|
+
// The proof is revalidated inside this lock before any lifecycle write.
|
|
346
|
+
return withMarkdownWriteLock(cwd, () => promoteBlueprintLocked(cwd, slug, toState, adapter, trustProof));
|
|
326
347
|
}
|
|
327
|
-
async function promoteBlueprintLocked(cwd, slug, toState) {
|
|
348
|
+
async function promoteBlueprintLocked(cwd, slug, toState, adapter, trustProof) {
|
|
328
349
|
const blueprintRoot = resolveBlueprintRoot(cwd);
|
|
329
350
|
const found = findBlueprintDocument(blueprintRoot, slug);
|
|
330
351
|
if (!found) {
|
|
331
352
|
throw new Error(`Blueprint "${slug}" not found in any state directory under ${blueprintRoot}`);
|
|
332
353
|
}
|
|
333
354
|
const { dir: currentDir, documentPath: currentDocumentPath, shape, state: currentState } = found;
|
|
334
|
-
|
|
335
|
-
// Markdown `_overview.md` is SSOT for task status + verification evidence.
|
|
336
|
-
// The projection DB is derived and can lag when agents edit task Status
|
|
337
|
-
// blocks without going through `task complete` (hash-skipping reingest then
|
|
338
|
-
// leaves stale todo rows). Do not fail completion on projection lag —
|
|
339
|
-
// reingest after the markdown gate so consumers see fresh rows.
|
|
340
|
-
if (toState === "completed") {
|
|
341
|
-
const markdown = readFileSync(currentDocumentPath, "utf8");
|
|
355
|
+
const assertCompletionReady = (markdown) => {
|
|
342
356
|
const blueprint = parseBlueprint(markdown, slug);
|
|
343
357
|
if (blueprint.tasks.length === 0) {
|
|
344
358
|
throw new Error(`Cannot promote "${slug}" to completed: zero-task blueprints cannot complete through the public lifecycle surface`);
|
|
@@ -349,30 +363,30 @@ async function promoteBlueprintLocked(cwd, slug, toState) {
|
|
|
349
363
|
throw new Error(`Cannot promote "${slug}" to completed: the following tasks are not done/dropped: ${list}`);
|
|
350
364
|
}
|
|
351
365
|
assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.filter((task) => task.status === "done").map((task) => task.id));
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
label: "wp_blueprint_promote",
|
|
363
|
-
ensureFreshSlug: slug,
|
|
364
|
-
});
|
|
366
|
+
// Completion parity with CI: the CLI completion owner uses the same
|
|
367
|
+
// audit-owned gate as the MCP funnel and execution engine, so all normal
|
|
368
|
+
// paths into `completed` enforce the same plan-purpose approval.
|
|
369
|
+
try {
|
|
370
|
+
assertCompletedApprovalGate(currentDocumentPath, markdown);
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
throw new Error(`Cannot complete "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
365
376
|
// Trust must be proven before any platform status_changed event is published.
|
|
366
377
|
let content = readFileSync(currentDocumentPath, "utf8");
|
|
367
378
|
let trustedSource = content;
|
|
379
|
+
if (toState === "completed") {
|
|
380
|
+
assertCompletionReady(content);
|
|
381
|
+
}
|
|
368
382
|
const crossesDraftApprovalBoundary = currentState === "draft" && (toState === "planned" || toState === "in-progress");
|
|
369
383
|
if (crossesDraftApprovalBoundary) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
trustedSource =
|
|
384
|
+
if (trustProof === null) {
|
|
385
|
+
throw new Error("Blueprint trust gate invalidated: no external gate proof is available");
|
|
386
|
+
}
|
|
387
|
+
assertPromotionTrustProofCurrent({ repoRoot: cwd, file: currentDocumentPath, markdown: content }, trustProof);
|
|
388
|
+
content = trustProof.trustedMarkdown;
|
|
389
|
+
trustedSource = trustProof.sourceMarkdown;
|
|
376
390
|
// Governance Piece 1 — HARD gate at the promotion boundary. One
|
|
377
391
|
// provenance-backed Claude/Codex/Grok or OpenCode-Go model approval can
|
|
378
392
|
// promote. Check AFTER the trust gate so trust failures surface first.
|
|
@@ -402,18 +416,23 @@ async function promoteBlueprintLocked(cwd, slug, toState) {
|
|
|
402
416
|
if (crossesDraftApprovalBoundary) {
|
|
403
417
|
const refreshedSource = readFileSync(currentDocumentPath, "utf8");
|
|
404
418
|
if (refreshedSource !== trustedSource) {
|
|
405
|
-
|
|
406
|
-
repoRoot: cwd,
|
|
407
|
-
file: currentDocumentPath,
|
|
408
|
-
markdown: refreshedSource,
|
|
409
|
-
});
|
|
410
|
-
const approvalEvaluation = evaluateApprovalGate(currentDocumentPath, matter(content).data.approvals);
|
|
411
|
-
if (!approvalEvaluation.satisfied) {
|
|
412
|
-
throw new Error(`Cannot promote "${slug}" to ${toState}: frontmatter \`approvals:\` does not satisfy the promotion reviewer mix (${formatApprovalGateRequirement(approvalEvaluation)}). Record approvals, commit the matching review ledger (e.g. reviews.md), and include tracked review transcript/artifact files referenced by each structured review entry — see catalog/agent/rules/pre-implementation.md.`);
|
|
413
|
-
}
|
|
419
|
+
throw new Error(`Cannot promote "${slug}" to ${toState}: blueprint changed after external trust gates ran; retry promotion`);
|
|
414
420
|
}
|
|
421
|
+
assertPromotionTrustProofCurrent({ repoRoot: cwd, file: currentDocumentPath, markdown: refreshedSource }, trustProof);
|
|
422
|
+
}
|
|
423
|
+
if (toState === "completed") {
|
|
424
|
+
// The platform pull can replace the document after the first completion
|
|
425
|
+
// check. Re-read and re-assert against the exact content written below.
|
|
426
|
+
content = readFileSync(currentDocumentPath, "utf8");
|
|
427
|
+
assertCompletionReady(content);
|
|
415
428
|
}
|
|
416
429
|
}
|
|
430
|
+
if (toState === "completed") {
|
|
431
|
+
// Best-effort projection refresh. Clear content hash first so a lagging
|
|
432
|
+
// tasks table is rewritten even when a prior corrupt UPDATE left the
|
|
433
|
+
// stored hash equal to current markdown.
|
|
434
|
+
await forceReIngestBlueprint(cwd, slug, currentDocumentPath);
|
|
435
|
+
}
|
|
417
436
|
// Always update local markdown + SQLite.
|
|
418
437
|
// Platform-first: these become derived artifacts; disabled: these are canonical.
|
|
419
438
|
// Update frontmatter in the current location first, then move
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BlueprintAuditResult, BlueprintSummary } from "#local";
|
|
2
2
|
import type { BlueprintTemplateEntry } from "#sync/types.js";
|
|
3
|
-
import type { AdvanceTaskResult, BlueprintCommandOptions, BlueprintLifecycleMutationResult, CreateBlueprintResult, ExportBlueprintResult, ExecuteBlueprintResult, MoveBlueprintResult, PromoteBlueprintResult, ShowBlueprintResult } from "./router.js";
|
|
3
|
+
import type { AdvanceTaskResult, BlueprintCommandOptions, BlueprintCanonicalizeResult, BlueprintLifecycleMutationResult, CreateBlueprintResult, ExportBlueprintResult, ExecuteBlueprintResult, MoveBlueprintResult, PromoteBlueprintResult, ShowBlueprintResult } from "./router.js";
|
|
4
4
|
/**
|
|
5
5
|
* Override the platform template fetcher — for tests only.
|
|
6
6
|
* Pass `null` to restore the production default.
|
|
@@ -21,6 +21,7 @@ export declare class BlueprintAuditFailedError extends Error {
|
|
|
21
21
|
interface BlueprintCommandDependencies {
|
|
22
22
|
advanceBlueprintTask: (slug: string, taskId: string, toStatus: string, options: BlueprintCommandOptions) => Promise<AdvanceTaskResult>;
|
|
23
23
|
auditBlueprints: (options: BlueprintCommandOptions) => Promise<BlueprintAuditResult>;
|
|
24
|
+
canonicalizeBlueprintBySlug: (slug: string, options: BlueprintCommandOptions) => Promise<BlueprintCanonicalizeResult>;
|
|
24
25
|
controlBlueprintExec: (action: "status" | "resume" | "stop", slug: string, options: BlueprintCommandOptions) => Promise<ExecuteBlueprintResult>;
|
|
25
26
|
readBlueprintExecutionLogs: (slug: string, options: BlueprintCommandOptions) => Promise<ExecuteBlueprintResult>;
|
|
26
27
|
createBlueprint: (goal: string, options: BlueprintCommandOptions) => Promise<CreateBlueprintResult>;
|
|
@@ -3,7 +3,9 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { executeBlueprintDbSubcommand } from "./db-commands.js";
|
|
6
|
+
import { getBlueprintSubcommandNames } from "./router-output.js";
|
|
6
7
|
import { listTemplates, resolveTemplate } from "./template-resolver.js";
|
|
8
|
+
import { TRUST_DOSSIER_SCAFFOLD } from "#trust/scaffold.js";
|
|
7
9
|
// ---------------------------------------------------------------------------
|
|
8
10
|
// Platform template fetcher (injectable for tests)
|
|
9
11
|
// ---------------------------------------------------------------------------
|
|
@@ -232,6 +234,15 @@ export async function executeBlueprintSubcommand(subcommand, args, options, deps
|
|
|
232
234
|
deps.printBlueprintOutput(options.json ? result : result.message, options.json);
|
|
233
235
|
return;
|
|
234
236
|
}
|
|
237
|
+
case "canonicalize": {
|
|
238
|
+
const slug = args[0];
|
|
239
|
+
if (!slug) {
|
|
240
|
+
throw new Error("Usage: wp blueprint canonicalize <slug>");
|
|
241
|
+
}
|
|
242
|
+
const result = await deps.canonicalizeBlueprintBySlug(slug, options);
|
|
243
|
+
deps.printBlueprintOutput(options.json ? { from: result.from, to: result.to, moved: result.moved } : result.message, options.json);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
235
246
|
case "start": {
|
|
236
247
|
const slug = args[0];
|
|
237
248
|
if (!slug) {
|
|
@@ -350,8 +361,16 @@ export async function executeBlueprintSubcommand(subcommand, args, options, deps
|
|
|
350
361
|
deps.printBlueprintOutput(options.json ? result : result.message, options.json);
|
|
351
362
|
return;
|
|
352
363
|
}
|
|
364
|
+
case "trust-dossier": {
|
|
365
|
+
// Read-only: prints an already-valid "## Trust Dossier" starter block
|
|
366
|
+
// (src/blueprint/trust/scaffold.ts). No file writes, no worktree/lock
|
|
367
|
+
// interaction — an author pastes the output into their overview and
|
|
368
|
+
// edits the example values.
|
|
369
|
+
deps.printBlueprintOutput(options.json ? { markdown: TRUST_DOSSIER_SCAFFOLD } : TRUST_DOSSIER_SCAFFOLD, options.json);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
353
372
|
default: {
|
|
354
|
-
throw new Error(`Unknown blueprint subcommand: ${subcommand}\n\nUse one of:
|
|
373
|
+
throw new Error(`Unknown blueprint subcommand: ${subcommand}\n\nUse one of: ${getBlueprintSubcommandNames().join(", ")}`);
|
|
355
374
|
}
|
|
356
375
|
}
|
|
357
376
|
}
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import type { Blueprint, BlueprintAuditResult, BlueprintSummary } from "#local";
|
|
2
2
|
import type { CreateBlueprintResult, ExecuteBlueprintResult, ShowBlueprintResult } from "./router.js";
|
|
3
|
+
/**
|
|
4
|
+
* Canonical registry of every subcommand `router-dispatch.ts` actually
|
|
5
|
+
* dispatches. This is the single source of truth for both the `--help`
|
|
6
|
+
* `Commands:` block (see `BLUEPRINT_HELP` below) and the unknown-subcommand
|
|
7
|
+
* error list — so a subcommand can't ship dispatched-but-undocumented
|
|
8
|
+
* again. `router-dispatch.test.ts` asserts this list stays equal to the
|
|
9
|
+
* live `case "…":` labels in the dispatch switch.
|
|
10
|
+
*/
|
|
11
|
+
export interface BlueprintSubcommandSpec {
|
|
12
|
+
/** Matches a `case "<name>":` label in router-dispatch.ts exactly. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/** One or more `Commands:` lines (without the leading indent). */
|
|
15
|
+
readonly usage: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export declare const BLUEPRINT_SUBCOMMANDS: readonly BlueprintSubcommandSpec[];
|
|
3
18
|
export declare function formatTaskLine(task: Blueprint["tasks"][number]): string;
|
|
4
19
|
export declare function getBlueprintHelpText(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Subcommand names in registry order, for the unknown-subcommand error
|
|
22
|
+
* message. Kept as a function (rather than exporting a derived constant)
|
|
23
|
+
* so the source of truth stays singular: `BLUEPRINT_SUBCOMMANDS`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getBlueprintSubcommandNames(): readonly string[];
|
|
5
26
|
export interface BlueprintInventorySummary {
|
|
6
27
|
total: number;
|
|
7
28
|
byStatus: Record<string, number>;
|