@webpresso/agent-kit 3.1.30 → 3.2.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.
Potentially problematic release.
This version of @webpresso/agent-kit might be problematic. Click here for more details.
- package/README.md +53 -15
- package/bin/_run.js +179 -19
- 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/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +90 -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 +8 -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 +43 -1
- 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 +65 -10
- 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.js +36 -11
- 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/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- 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 +62 -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 +10 -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 +34 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +6 -1
- package/dist/esm/cli/commands/agent-launch.js +82 -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 +17 -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 +6 -3
- 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 +24 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +221 -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 +1 -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/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 +11 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +56 -1
- 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 +51 -23
- 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 +7 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +58 -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-rotate.d.ts +49 -0
- package/dist/esm/cli/commands/opencode-rotate.js +135 -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/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +729 -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/setup-help.js +1 -1
- package/dist/esm/cli/commands/setup-repair-help.js +1 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.js +13 -8
- 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 +3 -3
- package/dist/esm/hooks/doctor.js +30 -15
- 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/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 +23 -8
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +74 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +78 -4
- 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 +4 -1
- package/dist/esm/mcp/tools/audit.js +30 -0
- package/dist/esm/mcp/tools/audits.d.ts +3 -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-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/review/authority.js +13 -5
- package/dist/esm/review/availability.d.ts +22 -0
- package/dist/esm/review/availability.js +107 -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.js +26 -9
- 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 +87 -6
- package/dist/esm/review/execution/supervisor.js +5 -0
- package/dist/esm/review/execution/types.d.ts +8 -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 +12 -0
- package/dist/esm/review/subject.js +74 -22
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/status/snapshot.js +30 -19
- 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 -1
- package/dist/esm/test-helpers/global-setup.js +68 -24
- 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/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +16 -13
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { validateBlueprintTrust } from "./validator.js";
|
|
5
5
|
import { parseTrustDossier } from "./dossier.js";
|
|
6
|
-
import { parseAllowedWpCommand } from "./gates.js";
|
|
7
|
-
import {
|
|
6
|
+
import { parseAllowedWpCommand, wpTestFileTargets } from "./gates.js";
|
|
7
|
+
import { ALLOWED_WP_COMMAND_TIMEOUT_MS, executeAllowedWpCommand, tailBounded, } from "./command-runner.js";
|
|
8
8
|
export { parseAllowedWpCommand };
|
|
9
|
-
|
|
10
|
-
const PROMOTION_GATE_STDIO_TAIL_LIMIT = 500;
|
|
11
|
-
export function applyPromotionTrustGate(input) {
|
|
9
|
+
export function preparePromotionTrustGate(input) {
|
|
12
10
|
const now = (input.now ?? new Date()).toISOString();
|
|
13
11
|
const syntacticMarkdown = upsertReadinessValue(upsertReadinessValue(input.markdown, "verified-at", now), "verified-head", "0123456789abcdef0123456789abcdef01234567");
|
|
14
12
|
const parsedBeforeHead = parseTrustDossier(syntacticMarkdown);
|
|
@@ -30,6 +28,16 @@ export function applyPromotionTrustGate(input) {
|
|
|
30
28
|
throw new Error(`Blueprint trust gate failed: ${preflight.violations.map((v) => `${v.section}: ${v.message}`).join("; ")}`);
|
|
31
29
|
const parsed = parseTrustDossier(markdown);
|
|
32
30
|
for (const gate of parsed.dossier?.gates ?? []) {
|
|
31
|
+
const deferredTarget = firstMissingTestTarget(input.repoRoot, gate.command);
|
|
32
|
+
if (deferredTarget !== undefined) {
|
|
33
|
+
// A `wp test --file <path>` gate whose target file does not exist yet is
|
|
34
|
+
// recorded as an honest non-`pass` result instead of aborting the whole
|
|
35
|
+
// promotion. `deferred` is a distinct failing state: it is only tolerated
|
|
36
|
+
// by the post-run validation below (allowDeferredGates), and the strict
|
|
37
|
+
// whole-repo completion audit still rejects it, so it can never merge.
|
|
38
|
+
markdown = updateGateLastResult(markdown, gate.gate, `deferred: test file not yet created (${deferredTarget})`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
33
41
|
runPromotionCommand(input.repoRoot, gate.command);
|
|
34
42
|
markdown = updateGateLastResult(markdown, gate.gate, `pass at ${now}`);
|
|
35
43
|
}
|
|
@@ -40,10 +48,27 @@ export function applyPromotionTrustGate(input) {
|
|
|
40
48
|
markdown,
|
|
41
49
|
promotionCandidate: true,
|
|
42
50
|
scanTaskAmbiguity: true,
|
|
51
|
+
allowDeferredGates: true,
|
|
43
52
|
});
|
|
44
53
|
if (!validated.ok)
|
|
45
54
|
throw new Error(`Blueprint trust gate failed: ${validated.violations.map((v) => `${v.section}: ${v.message}`).join("; ")}`);
|
|
46
|
-
return
|
|
55
|
+
return {
|
|
56
|
+
file: input.file,
|
|
57
|
+
sourceMarkdown: input.markdown,
|
|
58
|
+
verifiedHead: head,
|
|
59
|
+
trustedMarkdown: markdown,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function applyPromotionTrustGate(input) {
|
|
63
|
+
return preparePromotionTrustGate(input).trustedMarkdown;
|
|
64
|
+
}
|
|
65
|
+
export function assertPromotionTrustProofCurrent(input, proof) {
|
|
66
|
+
if (input.file !== proof.file || input.markdown !== proof.sourceMarkdown) {
|
|
67
|
+
throw new Error("Blueprint trust gate invalidated: the blueprint changed after external gates ran; retry promotion");
|
|
68
|
+
}
|
|
69
|
+
if (readHead(input.repoRoot) !== proof.verifiedHead) {
|
|
70
|
+
throw new Error("Blueprint trust gate invalidated: git HEAD changed after external gates ran; retry promotion");
|
|
71
|
+
}
|
|
47
72
|
}
|
|
48
73
|
function readHead(repoRoot) {
|
|
49
74
|
const testHead = process.env["WP_BLUEPRINT_TRUST_GATE_TEST_HEAD"];
|
|
@@ -61,123 +86,48 @@ function readHead(repoRoot) {
|
|
|
61
86
|
throw new Error("Blueprint trust gate failed: git HEAD is unavailable");
|
|
62
87
|
}
|
|
63
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns the first `wp test --file` target of `command` that does not exist on
|
|
91
|
+
* disk (resolved against `repoRoot`), or undefined when the gate is not a
|
|
92
|
+
* `wp test --file` gate or all its targets exist. A missing target is
|
|
93
|
+
* indistinguishable at `existsSync` time from "renamed / wrong path / deleted",
|
|
94
|
+
* so callers must treat it as a non-`pass` deferral, never a success.
|
|
95
|
+
*/
|
|
96
|
+
function firstMissingTestTarget(repoRoot, command) {
|
|
97
|
+
const targets = wpTestFileTargets(parseAllowedWpCommand(command));
|
|
98
|
+
return targets.find((target) => !existsSync(join(repoRoot, target)));
|
|
99
|
+
}
|
|
64
100
|
export function runPromotionCommand(repoRoot, command, deps = {}) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const spawn = deps.spawn ?? spawnSync;
|
|
71
|
-
const result = spawn(invocation.command, invocation.args, {
|
|
72
|
-
cwd: repoRoot,
|
|
73
|
-
encoding: "utf8",
|
|
74
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
75
|
-
timeout: PROMOTION_GATE_TIMEOUT_MS,
|
|
76
|
-
env: invocation.env,
|
|
101
|
+
const result = executeAllowedWpCommand({
|
|
102
|
+
command,
|
|
103
|
+
executionRoot: repoRoot,
|
|
104
|
+
logRoot: join(repoRoot, ".webpresso", "logs", "promotion-gates"),
|
|
105
|
+
...(deps.spawn ? { spawn: deps.spawn } : {}),
|
|
77
106
|
});
|
|
78
|
-
if (result.
|
|
107
|
+
if (result.outcome === "passed")
|
|
79
108
|
return;
|
|
80
|
-
|
|
81
|
-
const stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
82
|
-
const logPath = writePromotionGateLog(repoRoot, command, invocation, stdout, stderr, result);
|
|
83
|
-
throw new Error(formatPromotionGateFailure(command, stdout, stderr, result, logPath));
|
|
109
|
+
throw new Error(formatPromotionGateFailure(result));
|
|
84
110
|
}
|
|
85
|
-
function
|
|
86
|
-
if (binary === "./bin/wp" || binary === "wp") {
|
|
87
|
-
const localLauncher = resolveRepoWpLauncher(repoRoot);
|
|
88
|
-
if (localLauncher !== null) {
|
|
89
|
-
return {
|
|
90
|
-
command: localLauncher,
|
|
91
|
-
args: [...args],
|
|
92
|
-
env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
if (binary === "./bin/wp") {
|
|
96
|
-
return {
|
|
97
|
-
command: join(repoRoot, "bin", process.platform === "win32" ? "wp.cmd" : "wp"),
|
|
98
|
-
args: [...args],
|
|
99
|
-
env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const packagedLauncher = resolvePackagedWpLauncher();
|
|
103
|
-
return {
|
|
104
|
-
command: process.execPath,
|
|
105
|
-
args: [packagedLauncher, ...args],
|
|
106
|
-
env: { ...process.env },
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
command: binary,
|
|
111
|
-
args: [...args],
|
|
112
|
-
env: { ...process.env, PATH: `${repoRoot}/bin:${process.env["PATH"] ?? ""}` },
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
function resolveRepoWpLauncher(repoRoot) {
|
|
116
|
-
const launcher = join(repoRoot, "bin", process.platform === "win32" ? "wp.cmd" : "wp");
|
|
117
|
-
return existsSync(launcher) ? launcher : null;
|
|
118
|
-
}
|
|
119
|
-
function resolvePackagedWpLauncher() {
|
|
120
|
-
const launcher = resolvePackageAssetPreferred([
|
|
121
|
-
process.platform === "win32" ? "bin/wp.cmd" : "bin/wp",
|
|
122
|
-
"bin/wp",
|
|
123
|
-
]);
|
|
124
|
-
if (!existsSync(launcher)) {
|
|
125
|
-
throw new Error(`Promotion gate failed: packaged wp launcher is unavailable at ${launcher}`);
|
|
126
|
-
}
|
|
127
|
-
return launcher;
|
|
128
|
-
}
|
|
129
|
-
function formatPromotionGateFailure(command, stdout, stderr, result, logPath) {
|
|
111
|
+
function formatPromotionGateFailure(result) {
|
|
130
112
|
const details = [];
|
|
131
|
-
if (typeof result.
|
|
132
|
-
details.push(`exit=${result.
|
|
113
|
+
if (typeof result.exitCode === "number")
|
|
114
|
+
details.push(`exit=${result.exitCode}`);
|
|
133
115
|
if (result.signal)
|
|
134
116
|
details.push(`signal=${result.signal}`);
|
|
135
|
-
if (result.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
typeof result.error.code === "string"
|
|
140
|
-
? result.error.code
|
|
141
|
-
: null;
|
|
142
|
-
details.push(code ? `spawn_error=${code}` : `spawn_error=${result.error.message}`);
|
|
143
|
-
if (code === "ETIMEDOUT")
|
|
144
|
-
details.push(`timeout=${PROMOTION_GATE_TIMEOUT_MS}ms`);
|
|
117
|
+
if (result.errorMessage) {
|
|
118
|
+
details.push(`spawn_error=${result.failureCode ?? result.errorMessage}`);
|
|
119
|
+
if (result.outcome === "timed-out")
|
|
120
|
+
details.push(`timeout=${ALLOWED_WP_COMMAND_TIMEOUT_MS}ms`);
|
|
145
121
|
}
|
|
146
|
-
const stderrTail = tailBounded(stderr);
|
|
147
|
-
const stdoutTail = tailBounded(stdout);
|
|
122
|
+
const stderrTail = tailBounded(result.stderr);
|
|
123
|
+
const stdoutTail = tailBounded(result.stdout);
|
|
148
124
|
if (stderrTail.length > 0)
|
|
149
125
|
details.push(`stderr_tail=${JSON.stringify(stderrTail)}`);
|
|
150
126
|
if (stdoutTail.length > 0)
|
|
151
127
|
details.push(`stdout_tail=${JSON.stringify(stdoutTail)}`);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
function tailBounded(text) {
|
|
156
|
-
if (text.length <= PROMOTION_GATE_STDIO_TAIL_LIMIT)
|
|
157
|
-
return text.trim();
|
|
158
|
-
return text.slice(-PROMOTION_GATE_STDIO_TAIL_LIMIT).trim();
|
|
159
|
-
}
|
|
160
|
-
function writePromotionGateLog(repoRoot, command, invocation, stdout, stderr, result) {
|
|
161
|
-
const logsDir = join(repoRoot, ".webpresso", "logs", "promotion-gates");
|
|
162
|
-
mkdirSync(logsDir, { recursive: true });
|
|
163
|
-
const logPath = join(logsDir, `${Date.now()}-promotion-gate.log`);
|
|
164
|
-
const errorBlock = result.error
|
|
165
|
-
? `error: ${result.error.name}: ${result.error.message}\n`
|
|
166
|
-
: "error: none\n";
|
|
167
|
-
writeFileSync(logPath, [
|
|
168
|
-
`command: ${command}`,
|
|
169
|
-
`invocation: ${invocation.command} ${invocation.args.join(" ")}`,
|
|
170
|
-
`exit: ${result.status ?? "null"}`,
|
|
171
|
-
`signal: ${result.signal ?? "null"}`,
|
|
172
|
-
`timeout_ms: ${PROMOTION_GATE_TIMEOUT_MS}`,
|
|
173
|
-
errorBlock.trimEnd(),
|
|
174
|
-
"--- stderr ---",
|
|
175
|
-
stderr,
|
|
176
|
-
"--- stdout ---",
|
|
177
|
-
stdout,
|
|
178
|
-
"",
|
|
179
|
-
].join("\n"), "utf8");
|
|
180
|
-
return logPath;
|
|
128
|
+
if (result.logPath)
|
|
129
|
+
details.push(`log=${result.logPath}`);
|
|
130
|
+
return `Promotion gate failed (${result.command}): ${details.join("; ")}`;
|
|
181
131
|
}
|
|
182
132
|
function upsertReadinessValue(markdown, key, value) {
|
|
183
133
|
const re = new RegExp(`^- ${key}: .*$`, "mu");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust Dossier authoring scaffold.
|
|
3
|
+
*
|
|
4
|
+
* `dossier.ts` / `evidence.ts` / `gates.ts` / `ambiguity.ts` define strict
|
|
5
|
+
* validation for a blueprint's `## Trust Dossier` section, but nothing
|
|
6
|
+
* generates a starting template. `TRUST_DOSSIER_SCAFFOLD` is a complete,
|
|
7
|
+
* already-valid worked example — proven by `scaffold.test.ts` to pass
|
|
8
|
+
* `validateBlueprintTrust` under both the strict configuration
|
|
9
|
+
* `wp audit blueprint-trust` uses and the draft-preflight configuration
|
|
10
|
+
* `wp_blueprint_put` / `preparePromotionTrustGate` use — so an author can
|
|
11
|
+
* paste it in and edit the example values instead of hand-deriving the
|
|
12
|
+
* contract from source.
|
|
13
|
+
*
|
|
14
|
+
* `verified-at` / `verified-head` stay as inert, obviously-fake placeholders:
|
|
15
|
+
* `preparePromotionTrustGate` (./promotion.ts) overwrites both with the real
|
|
16
|
+
* timestamp/HEAD on every promotion attempt, so there is no need for this
|
|
17
|
+
* pure, dependency-free module to shell out to git.
|
|
18
|
+
*/
|
|
19
|
+
export declare const TRUST_DOSSIER_SCAFFOLD: string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust Dossier authoring scaffold.
|
|
3
|
+
*
|
|
4
|
+
* `dossier.ts` / `evidence.ts` / `gates.ts` / `ambiguity.ts` define strict
|
|
5
|
+
* validation for a blueprint's `## Trust Dossier` section, but nothing
|
|
6
|
+
* generates a starting template. `TRUST_DOSSIER_SCAFFOLD` is a complete,
|
|
7
|
+
* already-valid worked example — proven by `scaffold.test.ts` to pass
|
|
8
|
+
* `validateBlueprintTrust` under both the strict configuration
|
|
9
|
+
* `wp audit blueprint-trust` uses and the draft-preflight configuration
|
|
10
|
+
* `wp_blueprint_put` / `preparePromotionTrustGate` use — so an author can
|
|
11
|
+
* paste it in and edit the example values instead of hand-deriving the
|
|
12
|
+
* contract from source.
|
|
13
|
+
*
|
|
14
|
+
* `verified-at` / `verified-head` stay as inert, obviously-fake placeholders:
|
|
15
|
+
* `preparePromotionTrustGate` (./promotion.ts) overwrites both with the real
|
|
16
|
+
* timestamp/HEAD on every promotion attempt, so there is no need for this
|
|
17
|
+
* pure, dependency-free module to shell out to git.
|
|
18
|
+
*/
|
|
19
|
+
const PLACEHOLDER_HEAD = "0".repeat(40);
|
|
20
|
+
export const TRUST_DOSSIER_SCAFFOLD = `## Trust Dossier
|
|
21
|
+
|
|
22
|
+
<!-- Trust Dossier scaffold — generated by \`wp blueprint trust-dossier\`. -->
|
|
23
|
+
<!-- Replace every example value below, then delete these comment lines before promotion. -->
|
|
24
|
+
<!-- Constraints enforced by \`wp audit blueprint-trust\` / \`wp_blueprint_put\`: -->
|
|
25
|
+
<!-- - All 5 "###" subsections below are required, verbatim, exactly once each. -->
|
|
26
|
+
<!-- - Material Claims "repo:<path>" evidence must be a bare, existing repo-relative path -->
|
|
27
|
+
<!-- (no ":line" suffix, no leading "/", no "../"). -->
|
|
28
|
+
<!-- - "Residual Unknowns" must render exactly \`None.\` when nothing is outstanding. -->
|
|
29
|
+
<!-- - Promotion Gates "Command" must be a \`wp\` facade call: \`wp typecheck\`, \`wp lint\` -->
|
|
30
|
+
<!-- (no extra args), \`wp test --file <path>\` (repeatable), or -->
|
|
31
|
+
<!-- \`wp audit <kind> [--json|--affected|--branch|--base <val>]\`. -->
|
|
32
|
+
<!-- - No TBD, TODO, "decide during implementation", "open question", or <placeholder> tokens -->
|
|
33
|
+
<!-- anywhere in the 5 subsections below. -->
|
|
34
|
+
|
|
35
|
+
### Readiness Verdict
|
|
36
|
+
|
|
37
|
+
- promotion-ready: true
|
|
38
|
+
- unresolved-count: 0
|
|
39
|
+
- verified-at: 1970-01-01T00:00:00.000Z
|
|
40
|
+
- verified-head: ${PLACEHOLDER_HEAD}
|
|
41
|
+
- trust-gate-version: v1
|
|
42
|
+
|
|
43
|
+
### Material Claims
|
|
44
|
+
|
|
45
|
+
| ID | Claim | Evidence |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| C1 | Example material claim — replace with a real, falsifiable claim this blueprint depends on. | repo:README.md |
|
|
48
|
+
|
|
49
|
+
### Material Decisions
|
|
50
|
+
|
|
51
|
+
| ID | Decision | Chosen option | Rejected alternatives | Rationale |
|
|
52
|
+
| --- | --- | --- | --- | --- |
|
|
53
|
+
| D1 | Example material decision — replace with a real decision this blueprint locks in. | Example chosen option. | Example rejected alternative and why it lost. | Example rationale for the chosen option. |
|
|
54
|
+
|
|
55
|
+
### Promotion Gates
|
|
56
|
+
|
|
57
|
+
| Gate | Command | Expected outcome | Last result |
|
|
58
|
+
| --- | --- | --- | --- |
|
|
59
|
+
| typecheck | wp typecheck | Typecheck passes with zero errors. | pass (scaffold example — rerun and record the real result before promotion) |
|
|
60
|
+
|
|
61
|
+
### Residual Unknowns
|
|
62
|
+
|
|
63
|
+
None.
|
|
64
|
+
`;
|
|
@@ -51,7 +51,8 @@ export function validateBlueprintTrust(input) {
|
|
|
51
51
|
message: error instanceof Error ? error.message : String(error),
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
|
-
if (input.requirePassingGates !== false &&
|
|
54
|
+
if (input.requirePassingGates !== false &&
|
|
55
|
+
!isAcceptableGateResult(gate.lastResult, input.allowDeferredGates))
|
|
55
56
|
violations.push({
|
|
56
57
|
section: "Promotion Gates",
|
|
57
58
|
claimId: gate.gate,
|
|
@@ -64,3 +65,8 @@ export function validateBlueprintTrust(input) {
|
|
|
64
65
|
violations: violations.map((v) => ({ ...v, file: input.file })),
|
|
65
66
|
};
|
|
66
67
|
}
|
|
68
|
+
function isAcceptableGateResult(lastResult, allowDeferredGates) {
|
|
69
|
+
if (/^pass\b/iu.test(lastResult))
|
|
70
|
+
return true;
|
|
71
|
+
return allowDeferredGates === true && /^deferred:/iu.test(lastResult);
|
|
72
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { access, readFile, writeFile } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { parseBlueprint } from "#core/parser";
|
|
9
|
+
import { getBlueprintDocumentCandidates } from "./document-paths.js";
|
|
9
10
|
import { resolveBlueprintRoot } from "./blueprint-root.js";
|
|
10
11
|
/**
|
|
11
12
|
* Validates that all tasks in a plan are complete.
|
|
@@ -111,7 +112,13 @@ export async function archiveBlueprint(slug, projectPath) {
|
|
|
111
112
|
if (isAlreadyCompleted(slug)) {
|
|
112
113
|
return { success: false, error: "Plan is already completed" };
|
|
113
114
|
}
|
|
114
|
-
const paths = buildPlanPaths(projectPath, slug);
|
|
115
|
+
const paths = await buildPlanPaths(projectPath, slug);
|
|
116
|
+
if (paths.ambiguousPaths) {
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
error: `Plan ${slug} exists in both flat and folder forms; remove one before archiving: ${paths.ambiguousPaths.join(", ")}`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
115
122
|
// Check if plan exists
|
|
116
123
|
const planExists = await checkPlanExists(paths.sourcePath);
|
|
117
124
|
if (!planExists) {
|
|
@@ -132,16 +139,31 @@ export async function archiveBlueprint(slug, projectPath) {
|
|
|
132
139
|
/**
|
|
133
140
|
* Builds source and target paths for archival.
|
|
134
141
|
*
|
|
142
|
+
* Resolves the existing flat or folder document via the shared candidates;
|
|
143
|
+
* when neither shape exists, falls back to the canonical folder path so the
|
|
144
|
+
* caller reports the same "Plan not found" error as before.
|
|
145
|
+
*
|
|
135
146
|
* @param projectPath - Root path of the project
|
|
136
147
|
* @param slug - Plan slug
|
|
137
148
|
* @returns Path information
|
|
138
149
|
*/
|
|
139
|
-
function buildPlanPaths(projectPath, slug) {
|
|
150
|
+
async function buildPlanPaths(projectPath, slug) {
|
|
140
151
|
const plansDir = resolveBlueprintRoot(projectPath);
|
|
141
|
-
const
|
|
152
|
+
const candidates = getBlueprintDocumentCandidates(plansDir, slug);
|
|
153
|
+
const existing = [];
|
|
154
|
+
for (const candidate of candidates) {
|
|
155
|
+
if (await checkPlanExists(candidate))
|
|
156
|
+
existing.push(candidate);
|
|
157
|
+
}
|
|
158
|
+
const sourcePath = existing[0] ?? candidates.at(-1) ?? path.join(plansDir, slug, "_overview.md");
|
|
142
159
|
const sourceDir = path.dirname(sourcePath);
|
|
143
|
-
|
|
144
|
-
|
|
160
|
+
return {
|
|
161
|
+
sourcePath,
|
|
162
|
+
sourceDir,
|
|
163
|
+
targetDir: sourceDir,
|
|
164
|
+
planName: slug,
|
|
165
|
+
...(existing.length > 1 ? { ambiguousPaths: existing } : {}),
|
|
166
|
+
};
|
|
145
167
|
}
|
|
146
168
|
/**
|
|
147
169
|
* Extracts plan name from slug (removes status prefix).
|
|
@@ -8,6 +8,13 @@ export interface BlueprintDocumentPath {
|
|
|
8
8
|
slug: string;
|
|
9
9
|
state: BlueprintStatus;
|
|
10
10
|
}
|
|
11
|
+
export interface BlueprintDocumentIdentity extends BlueprintDocumentPath {
|
|
12
|
+
documentPath: string;
|
|
13
|
+
folderDirectory: string;
|
|
14
|
+
folderPath: string;
|
|
15
|
+
lifecycleSlug: string;
|
|
16
|
+
stableSlug: string;
|
|
17
|
+
}
|
|
11
18
|
export declare function normalizeBlueprintPath(filePath: string): string;
|
|
12
19
|
export declare function isBlueprintStatus(value: string | undefined): value is BlueprintStatus;
|
|
13
20
|
export declare function isBlueprintSlugSegment(value: string | undefined): value is string;
|
|
@@ -18,5 +25,16 @@ export declare function getBlueprintDocumentPaths(root: string, state: Blueprint
|
|
|
18
25
|
flat: string;
|
|
19
26
|
folder: string;
|
|
20
27
|
};
|
|
28
|
+
export declare function resolveBlueprintDocumentIdentity(root: string, documentPath: string): BlueprintDocumentIdentity | null;
|
|
29
|
+
export declare function folderBlueprintRequiredMessage(identity: {
|
|
30
|
+
readonly documentPath: string;
|
|
31
|
+
readonly lifecycleSlug: string;
|
|
32
|
+
readonly shape: BlueprintShape;
|
|
33
|
+
}): string;
|
|
34
|
+
export declare function assertFolderBlueprintIdentity(identity: {
|
|
35
|
+
readonly documentPath: string;
|
|
36
|
+
readonly lifecycleSlug: string;
|
|
37
|
+
readonly shape: BlueprintShape;
|
|
38
|
+
}): void;
|
|
21
39
|
export declare function getBlueprintDocumentCandidates(root: string, slug: string): string[];
|
|
22
40
|
export declare function getBlueprintAlternateDocumentPath(root: string, filePath: string): string | null;
|
|
@@ -22,7 +22,7 @@ export function isBlueprintSlugSegment(value) {
|
|
|
22
22
|
export function parseBlueprintDocumentRelativePath(filePath) {
|
|
23
23
|
const normalized = normalizeBlueprintPath(filePath);
|
|
24
24
|
const parts = normalized.split("/").filter((segment) => segment.length > 0);
|
|
25
|
-
const [state, second
|
|
25
|
+
const [state, second] = parts;
|
|
26
26
|
if (!isBlueprintStatus(state)) {
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
@@ -38,13 +38,16 @@ export function parseBlueprintDocumentRelativePath(filePath) {
|
|
|
38
38
|
state,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
if (parts.length
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
if (parts.length >= 3 && parts.at(-1) === BLUEPRINT_OVERVIEW_FILENAME) {
|
|
42
|
+
const slugSegments = parts.slice(1, -1);
|
|
43
|
+
if (!slugSegments.every((segment) => isBlueprintSlugSegment(segment))) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const slug = slugSegments.join("/");
|
|
44
47
|
return {
|
|
45
|
-
relativePath: `${state}/${
|
|
48
|
+
relativePath: `${state}/${slug}/${BLUEPRINT_OVERVIEW_FILENAME}`,
|
|
46
49
|
shape: "folder",
|
|
47
|
-
slug
|
|
50
|
+
slug,
|
|
48
51
|
state,
|
|
49
52
|
};
|
|
50
53
|
}
|
|
@@ -53,13 +56,13 @@ export function parseBlueprintDocumentRelativePath(filePath) {
|
|
|
53
56
|
export function isBlueprintSupportingMarkdownRelativePath(filePath) {
|
|
54
57
|
const normalized = normalizeBlueprintPath(filePath);
|
|
55
58
|
const parts = normalized.split("/").filter((segment) => segment.length > 0);
|
|
56
|
-
const [state
|
|
57
|
-
return (parts.length
|
|
59
|
+
const [state] = parts;
|
|
60
|
+
return (parts.length >= 3 &&
|
|
58
61
|
isBlueprintStatus(state) &&
|
|
59
|
-
isBlueprintSlugSegment(
|
|
60
|
-
typeof
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
parts.slice(1, -1).every((segment) => isBlueprintSlugSegment(segment)) &&
|
|
63
|
+
typeof parts.at(-1) === "string" &&
|
|
64
|
+
parts.at(-1).endsWith(".md") &&
|
|
65
|
+
parts.at(-1) !== BLUEPRINT_OVERVIEW_FILENAME);
|
|
63
66
|
}
|
|
64
67
|
export function getBlueprintDocumentPaths(root, state, slug) {
|
|
65
68
|
const directory = path.join(root, state, slug);
|
|
@@ -69,6 +72,36 @@ export function getBlueprintDocumentPaths(root, state, slug) {
|
|
|
69
72
|
folder: path.join(directory, BLUEPRINT_OVERVIEW_FILENAME),
|
|
70
73
|
};
|
|
71
74
|
}
|
|
75
|
+
export function resolveBlueprintDocumentIdentity(root, documentPath) {
|
|
76
|
+
const relativePath = normalizeBlueprintPath(path.relative(root, documentPath));
|
|
77
|
+
const parsed = parseBlueprintDocumentRelativePath(relativePath);
|
|
78
|
+
if (!parsed)
|
|
79
|
+
return null;
|
|
80
|
+
const paths = getBlueprintDocumentPaths(root, parsed.state, parsed.slug);
|
|
81
|
+
return {
|
|
82
|
+
...parsed,
|
|
83
|
+
documentPath: parsed.shape === "flat" ? paths.flat : paths.folder,
|
|
84
|
+
folderDirectory: paths.directory,
|
|
85
|
+
folderPath: paths.folder,
|
|
86
|
+
lifecycleSlug: `${parsed.state}/${parsed.slug}`,
|
|
87
|
+
stableSlug: parsed.slug,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function folderBlueprintRequiredMessage(identity) {
|
|
91
|
+
if (identity.shape === "folder") {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
`Blueprint evidence requires folder shape; ${identity.documentPath} is a flat blueprint.`,
|
|
96
|
+
`Run: wp blueprint canonicalize ${identity.lifecycleSlug}`,
|
|
97
|
+
"Then commit the canonicalized path and rerun the evidence command.",
|
|
98
|
+
].join(" ");
|
|
99
|
+
}
|
|
100
|
+
export function assertFolderBlueprintIdentity(identity) {
|
|
101
|
+
const message = folderBlueprintRequiredMessage(identity);
|
|
102
|
+
if (message)
|
|
103
|
+
throw new Error(message);
|
|
104
|
+
}
|
|
72
105
|
export function getBlueprintDocumentCandidates(root, slug) {
|
|
73
106
|
const normalized = normalizeBlueprintPath(slug);
|
|
74
107
|
const segments = normalized.split("/").filter((segment) => segment.length > 0);
|
|
@@ -187,6 +187,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
|
|
|
187
187
|
classification: "direct-fallback-by-design",
|
|
188
188
|
reason: "internal serialization wrapper spawned by the auto-update, wp update, and setup lanes; never agent-facing",
|
|
189
189
|
},
|
|
190
|
+
"optional-tool-refresh": {
|
|
191
|
+
command: "optional-tool-refresh",
|
|
192
|
+
classification: "direct-fallback-by-design",
|
|
193
|
+
reason: "internal detached worker spawned by provider launch freshness; never agent-facing",
|
|
194
|
+
},
|
|
190
195
|
init: {
|
|
191
196
|
command: "init",
|
|
192
197
|
classification: "unsafe-credentialed-destructive",
|
|
@@ -229,6 +234,11 @@ export const CLI_MCP_PARITY_CLASSIFICATION = {
|
|
|
229
234
|
classification: "direct-fallback-by-design",
|
|
230
235
|
reason: "long-running local preview server remains a direct CLI workflow",
|
|
231
236
|
},
|
|
237
|
+
"public:readiness": {
|
|
238
|
+
command: "public:readiness",
|
|
239
|
+
classification: "direct-fallback-by-design",
|
|
240
|
+
reason: "public package readiness performs local package/tarball checks and is intentionally not a read-only MCP workflow",
|
|
241
|
+
},
|
|
232
242
|
qa: {
|
|
233
243
|
command: "qa",
|
|
234
244
|
classification: "existing-mcp",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CliCommand } from "@webpresso/cli-contract";
|
|
2
2
|
export declare const blueprintCommands: {
|
|
3
3
|
audit: CliCommand<Record<string, unknown>>;
|
|
4
|
+
canonicalize: CliCommand<Record<string, unknown>>;
|
|
4
5
|
exec: CliCommand<Record<string, unknown>>;
|
|
5
6
|
finalize: CliCommand<Record<string, unknown>>;
|
|
6
7
|
list: CliCommand<Record<string, unknown>>;
|
|
@@ -35,6 +35,7 @@ function createBlueprintAuditCommand() {
|
|
|
35
35
|
}
|
|
36
36
|
export const blueprintCommands = {
|
|
37
37
|
audit: createBlueprintAuditCommand(),
|
|
38
|
+
canonicalize: placeholderCommand(SCOPE, "canonicalize", "Canonicalize a flat blueprint to folder shape"),
|
|
38
39
|
exec: placeholderGroup(SCOPE, "exec", "Execute a blueprint", {
|
|
39
40
|
logs: placeholderCommand(SCOPE, "logs", "Show blueprint execution logs"),
|
|
40
41
|
resume: placeholderCommand(SCOPE, "resume", "Resume blueprint execution"),
|
package/dist/esm/cli/cli.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Lazy-loads subcommand modules based on the first argv to keep startup
|
|
6
6
|
* cheap. Modeled on apps/cli-wp/src/cli.ts.
|
|
7
7
|
*/
|
|
8
|
-
declare const SUPPORTED_COMMANDS: readonly ["blueprint", "browser", "dash", "dash-helper-start", "dash-helper-complete", "claude", "codex", "grok", "opencode", "yolo", "config", "secrets", "roadmap", "status", "review", "sync", "audit", "qa", "ultragoal", "compile", "rule", "skill", "skills", "docs", "setup", "init", "dev", "deploy", "preview", "cleanup", "migrate", "doctor", "err", "test", "e2e", "ci", "ci-preflight", "typecheck", "lint", "format", "logs", "session-id", "session-info", "session", "tech-debt", "pr", "fleet", "worktree", "mcp", "hook", "hooks", "self-install-guard", "gain", "bench", "install", "add", "remove", "update", "exec", "run"];
|
|
8
|
+
declare const SUPPORTED_COMMANDS: readonly ["blueprint", "browser", "dash", "dash-helper-start", "dash-helper-complete", "claude", "codex", "grok", "opencode", "yolo", "config", "secrets", "roadmap", "status", "review", "sync", "audit", "qa", "ultragoal", "compile", "rule", "skill", "skills", "docs", "setup", "init", "dev", "deploy", "preview", "cleanup", "migrate", "doctor", "err", "test", "e2e", "ci", "ci-preflight", "public:readiness", "typecheck", "lint", "format", "logs", "session-id", "session-info", "session", "tech-debt", "pr", "fleet", "worktree", "mcp", "hook", "hooks", "self-install-guard", "optional-tool-refresh", "gain", "bench", "install", "add", "remove", "update", "exec", "run"];
|
|
9
9
|
export type SupportedCommand = (typeof SUPPORTED_COMMANDS)[number];
|
|
10
10
|
type RootHelpEntry = {
|
|
11
11
|
readonly command: string;
|
package/dist/esm/cli/cli.js
CHANGED
|
@@ -55,6 +55,7 @@ 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",
|
|
@@ -70,6 +71,7 @@ const SUPPORTED_COMMANDS = [
|
|
|
70
71
|
"hook",
|
|
71
72
|
"hooks",
|
|
72
73
|
"self-install-guard",
|
|
74
|
+
"optional-tool-refresh",
|
|
73
75
|
"gain",
|
|
74
76
|
"bench",
|
|
75
77
|
"install",
|
|
@@ -111,6 +113,10 @@ const ROOT_HELP_CORE_ENTRIES = [
|
|
|
111
113
|
command: "ci-preflight",
|
|
112
114
|
description: "Run the locally-runnable required CI checks fail-fast (the pre-push gate)",
|
|
113
115
|
},
|
|
116
|
+
{
|
|
117
|
+
command: "public:readiness",
|
|
118
|
+
description: "Run the public package readiness gate through the portable wp surface",
|
|
119
|
+
},
|
|
114
120
|
{ command: "logs", description: "Print persisted raw output from recent quality runs" },
|
|
115
121
|
{ command: "dev", description: "Run a manifest-backed development target" },
|
|
116
122
|
{ command: "status", description: "Show unified local readiness and next action" },
|
|
@@ -364,6 +370,10 @@ const COMMAND_REGISTRARS = {
|
|
|
364
370
|
const { registerCiPreflightCommand } = await import("./commands/ci-preflight.js");
|
|
365
371
|
registerCiPreflightCommand(cli);
|
|
366
372
|
},
|
|
373
|
+
"public:readiness": async (cli) => {
|
|
374
|
+
const { registerPublicReadinessCommand } = await import("./commands/public-readiness.js");
|
|
375
|
+
registerPublicReadinessCommand(cli);
|
|
376
|
+
},
|
|
367
377
|
typecheck: async (cli) => {
|
|
368
378
|
const { registerTypecheckCommand } = await import("./commands/typecheck.js");
|
|
369
379
|
registerTypecheckCommand(cli);
|
|
@@ -422,6 +432,15 @@ const COMMAND_REGISTRARS = {
|
|
|
422
432
|
const { registerSelfInstallGuardCommand } = await import("./commands/self-install-guard.js");
|
|
423
433
|
registerSelfInstallGuardCommand(cli);
|
|
424
434
|
},
|
|
435
|
+
"optional-tool-refresh": async (cli) => {
|
|
436
|
+
cli
|
|
437
|
+
.command("optional-tool-refresh <tool> [...args]", "Internal detached optional-tool freshness worker")
|
|
438
|
+
.allowUnknownOptions()
|
|
439
|
+
.action(async (tool, args = []) => {
|
|
440
|
+
const { runOptionalToolRefreshWorker } = await import("./optional-tool-freshness.js");
|
|
441
|
+
process.exitCode = runOptionalToolRefreshWorker([tool, ...args]);
|
|
442
|
+
});
|
|
443
|
+
},
|
|
425
444
|
gain: async (cli) => {
|
|
426
445
|
const { registerGainCommand } = await import("./commands/gain/index.js");
|
|
427
446
|
registerGainCommand(cli);
|
|
@@ -636,6 +655,21 @@ export async function main() {
|
|
|
636
655
|
console.error(formatUnknownCommandError(command, SUPPORTED_COMMANDS));
|
|
637
656
|
return 1;
|
|
638
657
|
}
|
|
658
|
+
// `wp run wait` is the read-only workflow-run waiter, intercepted before the
|
|
659
|
+
// package-manager `run` facade (which maps `wp run <script>` to `vp run
|
|
660
|
+
// <script>`). Without this, `wait` would be treated as a package script name.
|
|
661
|
+
// Errors resolve to an exit code here, mirroring main()'s catch below, so a
|
|
662
|
+
// bad flag never rejects the main() promise.
|
|
663
|
+
if (resolvedCommand === "run" && argv[3] === "wait") {
|
|
664
|
+
const { runRunWaitCommand } = await import("./commands/run-wait.js");
|
|
665
|
+
try {
|
|
666
|
+
return await runRunWaitCommand(argv.slice(4));
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
670
|
+
return 1;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
639
673
|
await COMMAND_REGISTRARS[resolvedCommand](cli, resolvedCommand);
|
|
640
674
|
if (resolvedCommand === "session") {
|
|
641
675
|
const { runSessionCommand } = await import("./commands/session.js");
|