@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
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const RUN_WAIT_HELP = "wp run wait \u2014 read-only GitHub workflow-run waiter (push/dispatch runs, e.g. production deploys on main)\n\nUsage:\n wp run wait --run-id <id> [options]\n wp run wait --sha <head-sha> [--workflow <file-or-name>] [options]\n\nSelector (exactly one required):\n --run-id <id> Watch a specific workflow run by database id\n --sha <sha> Watch the newest run for a head commit SHA\n --workflow <file|name> Filter --sha selection to one workflow\n\nOptions:\n --until <target> run-terminal (default) | all-jobs-terminal\n --poll-interval-ms <n> Poll interval (default 15000)\n --timeout-ms <n> Overall watch deadline (max 300000, default 120000)\n --max-output-bytes <n> Per-command output budget\n --cwd <path> Project root or descendant\n --json Emit exactly one JSON document (default output)\n -h, --help Show this help\n\nRead-only: issues only `gh` GET calls; never mutates run state.";
|
|
2
|
+
export declare function runRunWaitCommand(args: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { RUN_WAIT_UNTIL_VALUES, runRunWait, } from "#mcp/tools/run-wait.js";
|
|
2
|
+
// `wp run wait` is intercepted in cli.ts before the package-manager `run`
|
|
3
|
+
// facade (which maps `wp run <script>` to `vp run <script>`). This module owns
|
|
4
|
+
// only the `wait` subcommand and keeps arg parsing dependency-free so the
|
|
5
|
+
// interception path stays cheap.
|
|
6
|
+
export const RUN_WAIT_HELP = `wp run wait — read-only GitHub workflow-run waiter (push/dispatch runs, e.g. production deploys on main)
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
wp run wait --run-id <id> [options]
|
|
10
|
+
wp run wait --sha <head-sha> [--workflow <file-or-name>] [options]
|
|
11
|
+
|
|
12
|
+
Selector (exactly one required):
|
|
13
|
+
--run-id <id> Watch a specific workflow run by database id
|
|
14
|
+
--sha <sha> Watch the newest run for a head commit SHA
|
|
15
|
+
--workflow <file|name> Filter --sha selection to one workflow
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--until <target> run-terminal (default) | all-jobs-terminal
|
|
19
|
+
--poll-interval-ms <n> Poll interval (default 15000)
|
|
20
|
+
--timeout-ms <n> Overall watch deadline (max 300000, default 120000)
|
|
21
|
+
--max-output-bytes <n> Per-command output budget
|
|
22
|
+
--cwd <path> Project root or descendant
|
|
23
|
+
--json Emit exactly one JSON document (default output)
|
|
24
|
+
-h, --help Show this help
|
|
25
|
+
|
|
26
|
+
Read-only: issues only \`gh\` GET calls; never mutates run state.`;
|
|
27
|
+
function parsePositiveInteger(value, name) {
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const parsed = Number.parseInt(value, 10);
|
|
31
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
32
|
+
throw new Error(`${name} must be a positive integer`);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
function normalizeUntil(value) {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
const normalized = value.replaceAll("-", "_");
|
|
40
|
+
if (!RUN_WAIT_UNTIL_VALUES.includes(normalized)) {
|
|
41
|
+
throw new Error(`Unknown run wait target: ${value}`);
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
const VALUE_FLAGS = new Set([
|
|
46
|
+
"--run-id",
|
|
47
|
+
"--sha",
|
|
48
|
+
"--workflow",
|
|
49
|
+
"--until",
|
|
50
|
+
"--poll-interval-ms",
|
|
51
|
+
"--timeout-ms",
|
|
52
|
+
"--max-output-bytes",
|
|
53
|
+
"--cwd",
|
|
54
|
+
]);
|
|
55
|
+
function parseFlags(args) {
|
|
56
|
+
const values = {};
|
|
57
|
+
let help = false;
|
|
58
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
59
|
+
const arg = args[index];
|
|
60
|
+
if (arg === undefined)
|
|
61
|
+
continue;
|
|
62
|
+
if (arg === "--help" || arg === "-h") {
|
|
63
|
+
help = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg === "--json")
|
|
67
|
+
continue; // JSON is the only output shape
|
|
68
|
+
const eq = arg.indexOf("=");
|
|
69
|
+
if (eq !== -1 && arg.startsWith("--")) {
|
|
70
|
+
const key = arg.slice(0, eq);
|
|
71
|
+
if (VALUE_FLAGS.has(key)) {
|
|
72
|
+
values[key] = arg.slice(eq + 1);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (VALUE_FLAGS.has(arg)) {
|
|
77
|
+
const next = args[index + 1];
|
|
78
|
+
if (next === undefined)
|
|
79
|
+
throw new Error(`${arg} requires a value`);
|
|
80
|
+
values[arg] = next;
|
|
81
|
+
index += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Unknown run wait option: ${arg}`);
|
|
85
|
+
}
|
|
86
|
+
return { values, help };
|
|
87
|
+
}
|
|
88
|
+
export async function runRunWaitCommand(args) {
|
|
89
|
+
const { values, help } = parseFlags(args);
|
|
90
|
+
if (help) {
|
|
91
|
+
console.log(RUN_WAIT_HELP);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
const runId = values["--run-id"];
|
|
95
|
+
const input = {
|
|
96
|
+
sha: values["--sha"],
|
|
97
|
+
runId: runId === undefined ? undefined : runId,
|
|
98
|
+
workflow: values["--workflow"],
|
|
99
|
+
until: normalizeUntil(values["--until"]),
|
|
100
|
+
pollIntervalMs: parsePositiveInteger(values["--poll-interval-ms"], "--poll-interval-ms"),
|
|
101
|
+
timeoutMs: parsePositiveInteger(values["--timeout-ms"], "--timeout-ms"),
|
|
102
|
+
maxOutputBytes: parsePositiveInteger(values["--max-output-bytes"], "--max-output-bytes"),
|
|
103
|
+
cwd: values["--cwd"],
|
|
104
|
+
};
|
|
105
|
+
let payload;
|
|
106
|
+
try {
|
|
107
|
+
payload = await runRunWait(input);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const issues = error.issues;
|
|
111
|
+
if (Array.isArray(issues)) {
|
|
112
|
+
const message = issues
|
|
113
|
+
.map((issue) => (typeof issue.message === "string" ? issue.message : String(issue.message)))
|
|
114
|
+
.join("; ");
|
|
115
|
+
console.error(`wp run wait: ${message}`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
121
|
+
return payload.status === "success" ? 0 : 1;
|
|
122
|
+
}
|
|
@@ -8,7 +8,7 @@ export function getSetupHelpText(version) {
|
|
|
8
8
|
"Options:",
|
|
9
9
|
" --with <skills> Comma-separated opt-in skills and/or presets to install (non-interactive). Presets: example-skill, lore-commits, playwright-mcp, rtk, vision plus the special token `all`. Legacy compatibility presets are intentionally omitted; use wp install oh-my opencode when you want WP-owned refreshes. Opt-in skills are listed by 'wp skill list'. ",
|
|
10
10
|
" --without <skills> Comma-separated opt-in skills and/or presets to opt out of for this run. ",
|
|
11
|
-
" --host <hosts> Comma-separated host targets: codex, claude, opencode, all, none ",
|
|
11
|
+
" --host <hosts> Comma-separated host targets: codex, claude, opencode, grok, all, none ",
|
|
12
12
|
" --dry-run Show what would change without writing anything ",
|
|
13
13
|
" --cwd <dir> Working tree to scaffold into (default: process.cwd()) ",
|
|
14
14
|
" -h, --help Display this message ",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const PRIMARY_SETUP_OPTIONS = [
|
|
6
6
|
" --with <skills> Comma-separated opt-in skills and/or presets to install (non-interactive). Presets: example-skill, lore-commits, playwright-mcp, rtk, vision plus the special token `all`. Legacy compatibility presets are intentionally omitted; use wp install oh-my opencode when you want WP-owned refreshes. Opt-in skills are listed by 'wp skill list'. ",
|
|
7
7
|
" --without <skills> Comma-separated opt-in skills and/or presets to opt out of for this run. ",
|
|
8
|
-
" --host <hosts> Comma-separated host targets: codex, claude, opencode, all, none ",
|
|
8
|
+
" --host <hosts> Comma-separated host targets: codex, claude, opencode, grok, all, none ",
|
|
9
9
|
" --prune Widen stale agent-kit plugin-cache pruning from the default host set (claude, codex, opencode) to every supported host while retaining recent versions ",
|
|
10
10
|
" --dry-run Show what would change without writing anything ",
|
|
11
11
|
" --cwd <dir> Working tree to scaffold into (default: process.cwd()) ",
|
|
@@ -5,6 +5,7 @@ import { execFileSync, spawnSync } from "node:child_process";
|
|
|
5
5
|
import { existsSync, mkdirSync } from "node:fs";
|
|
6
6
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
7
7
|
import { findGitRoot } from "#cli/commands/init/detect-consumer.js";
|
|
8
|
+
import { removeCodexProjectTrust, removeCodexProjectTrusts, } from "#worktrees/codex-project-trust.js";
|
|
8
9
|
import { shortId } from "#shared-utils/short-id.js";
|
|
9
10
|
import { readConfig } from "#cli/commands/init/config";
|
|
10
11
|
import { getProjectRoot } from "#cli/utils";
|
|
@@ -15,7 +16,7 @@ import { hydrateWorktreeDependencies, reconcileWorktreeDependencies, } from "#wo
|
|
|
15
16
|
import { projectWorktreeAgentSurfaces } from "#worktrees/project-agent-surfaces.js";
|
|
16
17
|
import { assertNotLocalMainBranch } from "#worktrees/main-ownership.js";
|
|
17
18
|
import { buildWorktreeInventory, canonicalizeWorktreePath, classifyMembership, isManagedWorktreePath, resolveRepoIdentity, } from "#worktrees/identity.js";
|
|
18
|
-
import { findRegistryCandidatesByPath,
|
|
19
|
+
import { findRegistryCandidatesByPath, planStaleWorktreeRegistryPrune, readWorktreeRegistry, removeWorktreeRegistryEntries, repoScopedPathPredicate, upsertWorktreeRegistryEntry, } from "#worktrees/registry.js";
|
|
19
20
|
function parseDepsOption(value) {
|
|
20
21
|
if (value === undefined || value === null || value === "")
|
|
21
22
|
return undefined;
|
|
@@ -443,7 +444,7 @@ function handleRefresh(opts) {
|
|
|
443
444
|
}
|
|
444
445
|
console.log(`Refreshed ${updated} managed worktree entr${updated === 1 ? "y" : "ies"} for ${repoRoot}.`);
|
|
445
446
|
}
|
|
446
|
-
function handlePrune(opts) {
|
|
447
|
+
async function handlePrune(opts) {
|
|
447
448
|
if (!opts.all)
|
|
448
449
|
throw new Error("Usage: wp worktree prune --all");
|
|
449
450
|
// Repository-scoped, three-state prune: only THIS repo's entries that are
|
|
@@ -451,10 +452,12 @@ function handlePrune(opts) {
|
|
|
451
452
|
// other-repo, and live-legacy entries are retained.
|
|
452
453
|
const repoRoot = resolveWorktreeRepoRoot(opts.repo ?? opts.cwd ?? process.cwd());
|
|
453
454
|
const inventory = buildWorktreeInventory(repoRoot);
|
|
454
|
-
const result =
|
|
455
|
+
const result = planStaleWorktreeRegistryPrune({
|
|
455
456
|
inventory,
|
|
456
457
|
scope: { authoritativeRepoKey: resolveRepoIdentity(repoRoot) },
|
|
457
458
|
});
|
|
459
|
+
await removeCodexProjectTrusts(result.removed.map((entry) => entry.path));
|
|
460
|
+
result.commit();
|
|
458
461
|
console.log(`Pruned ${result.removed.length} stale managed registry entr${result.removed.length === 1 ? "y" : "ies"}.`);
|
|
459
462
|
for (const warning of result.warnings)
|
|
460
463
|
console.log(`warning: ${warning}`);
|
|
@@ -527,7 +530,7 @@ function handleRebind(args, opts) {
|
|
|
527
530
|
console.log(` worktree_owner_branch: ${binding.branch}`);
|
|
528
531
|
console.log(` path: ${binding.path}`);
|
|
529
532
|
}
|
|
530
|
-
function handleRemove(nameOrPath, opts) {
|
|
533
|
+
async function handleRemove(nameOrPath, opts) {
|
|
531
534
|
const cwd = opts.cwd ?? process.cwd();
|
|
532
535
|
const callerRoot = resolveWorktreeRepoRoot(cwd);
|
|
533
536
|
const entries = listEntries(callerRoot);
|
|
@@ -548,6 +551,7 @@ function handleRemove(nameOrPath, opts) {
|
|
|
548
551
|
if (result.status !== 0) {
|
|
549
552
|
throw new Error("git worktree remove failed");
|
|
550
553
|
}
|
|
554
|
+
await removeCodexProjectTrust(resolved);
|
|
551
555
|
removeWorktreeRegistryEntries(repoScopedPathPredicate(resolved, { authoritativeRepoKey: resolveRepoIdentity(repoRoot) }));
|
|
552
556
|
}
|
|
553
557
|
function isDirty(path) {
|
|
@@ -563,7 +567,7 @@ function currentBranch(repoRoot) {
|
|
|
563
567
|
});
|
|
564
568
|
return result.status === 0 ? String(result.stdout ?? "").trim() : "";
|
|
565
569
|
}
|
|
566
|
-
function handleMergeCleanup(nameOrPath, opts) {
|
|
570
|
+
async function handleMergeCleanup(nameOrPath, opts) {
|
|
567
571
|
const cwd = opts.cwd ?? process.cwd();
|
|
568
572
|
const callerRoot = resolveWorktreeRepoRoot(cwd);
|
|
569
573
|
const entries = listEntries(callerRoot);
|
|
@@ -628,6 +632,7 @@ function handleMergeCleanup(nameOrPath, opts) {
|
|
|
628
632
|
if (removeResult.status !== 0) {
|
|
629
633
|
throw new Error("git worktree remove failed");
|
|
630
634
|
}
|
|
635
|
+
await removeCodexProjectTrust(resolved);
|
|
631
636
|
removeWorktreeRegistryEntries(repoScopedPathPredicate(resolved, { authoritativeRepoKey: resolveRepoIdentity(repoRoot) }));
|
|
632
637
|
const fetchResult = spawnSync("git", [...plan.fetchArgs], { cwd: repoRoot, stdio: "inherit" });
|
|
633
638
|
if (fetchResult.status !== 0) {
|
|
@@ -665,7 +670,7 @@ export async function executeWorktreeSubcommand(subcommand, args, opts) {
|
|
|
665
670
|
handleRefresh(opts);
|
|
666
671
|
return;
|
|
667
672
|
case "prune":
|
|
668
|
-
handlePrune(opts);
|
|
673
|
+
await handlePrune(opts);
|
|
669
674
|
return;
|
|
670
675
|
case "migrate":
|
|
671
676
|
handleMigrate(opts);
|
|
@@ -682,7 +687,7 @@ export async function executeWorktreeSubcommand(subcommand, args, opts) {
|
|
|
682
687
|
if (!nameOrPath) {
|
|
683
688
|
throw new Error("Usage: wp worktree remove <branch-or-path> [--force]");
|
|
684
689
|
}
|
|
685
|
-
handleRemove(nameOrPath, opts);
|
|
690
|
+
await handleRemove(nameOrPath, opts);
|
|
686
691
|
return;
|
|
687
692
|
}
|
|
688
693
|
case "merge-cleanup": {
|
|
@@ -690,7 +695,7 @@ export async function executeWorktreeSubcommand(subcommand, args, opts) {
|
|
|
690
695
|
if (!nameOrPath) {
|
|
691
696
|
throw new Error("Usage: wp worktree merge-cleanup <branch-or-path> [--base <ref>] [--dry-run]");
|
|
692
697
|
}
|
|
693
|
-
handleMergeCleanup(nameOrPath, opts);
|
|
698
|
+
await handleMergeCleanup(nameOrPath, opts);
|
|
694
699
|
return;
|
|
695
700
|
}
|
|
696
701
|
default: {
|
|
@@ -79,8 +79,8 @@ function validateDirectProviderLaunch(provider, cwd) {
|
|
|
79
79
|
? `Run \`${installCommand}\` and retry.`
|
|
80
80
|
: `Install ${ownershipName} and retry.`);
|
|
81
81
|
}
|
|
82
|
-
// External PATH hosts (Grok) do not run optional-tool freshness
|
|
83
|
-
const vpCommand = resolveGlobalCapableVpCommand();
|
|
82
|
+
// External PATH hosts (Grok) do not run optional-tool freshness.
|
|
83
|
+
const vpCommand = adapter ? resolveGlobalCapableVpCommand() : null;
|
|
84
84
|
if (adapter && !vpCommand) {
|
|
85
85
|
throw new DirectProviderLaunchPreparationError("No global-capable Vite+ runner found.", "Reinstall @webpresso/agent-kit and retry.");
|
|
86
86
|
}
|
|
@@ -105,10 +105,14 @@ function ensureCodexSessionTooling(repoRoot) {
|
|
|
105
105
|
throw new DirectProviderLaunchPreparationError(`Codex project MCP server ${project.server} is unavailable: ${project.reason}`, "Run `wp setup --yes-global` from this repo to provision project MCP servers, then retry.");
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
+
const PROVIDER_SESSION_PREFLIGHTS = {
|
|
109
|
+
claude: () => undefined,
|
|
110
|
+
codex: ensureCodexSessionTooling,
|
|
111
|
+
grok: () => undefined,
|
|
112
|
+
opencode: () => undefined,
|
|
113
|
+
};
|
|
108
114
|
function ensureProviderSessionTooling(provider, cwd) {
|
|
109
|
-
|
|
110
|
-
return;
|
|
111
|
-
ensureCodexSessionTooling(cwd);
|
|
115
|
+
PROVIDER_SESSION_PREFLIGHTS[provider](cwd);
|
|
112
116
|
}
|
|
113
117
|
export function isBareInteractiveProviderLaunch(args) {
|
|
114
118
|
return parseDashPermissionMode(args).args.length === 0;
|
|
@@ -15,6 +15,16 @@ export interface OptionalToolFreshnessCache {
|
|
|
15
15
|
readonly version: 1;
|
|
16
16
|
readonly tools: Partial<Record<ManagedToolName, OptionalToolFreshnessRecord>>;
|
|
17
17
|
}
|
|
18
|
+
export interface OptionalToolRefreshLeaseRecord {
|
|
19
|
+
readonly pid: number | null;
|
|
20
|
+
readonly startedAt: number;
|
|
21
|
+
readonly heartbeatAt: number;
|
|
22
|
+
readonly expiresAt: number;
|
|
23
|
+
}
|
|
24
|
+
export interface OptionalToolRefreshLeaseCache {
|
|
25
|
+
readonly version: 1;
|
|
26
|
+
readonly tools: Partial<Record<ManagedToolName, OptionalToolRefreshLeaseRecord>>;
|
|
27
|
+
}
|
|
18
28
|
export type OptionalToolFreshnessMode = "force" | "if-needed";
|
|
19
29
|
export type OptionalToolFreshnessResult = {
|
|
20
30
|
readonly kind: "missing";
|
|
@@ -66,9 +76,22 @@ export interface OptionalToolFreshnessDeps {
|
|
|
66
76
|
}) => SpawnSyncReturns<string>;
|
|
67
77
|
readonly readCache?: () => OptionalToolFreshnessCache;
|
|
68
78
|
readonly writeCache?: (cache: OptionalToolFreshnessCache) => void;
|
|
79
|
+
readonly readLeaseCache?: () => OptionalToolRefreshLeaseCache;
|
|
80
|
+
readonly writeLeaseCache?: (cache: OptionalToolRefreshLeaseCache) => void;
|
|
81
|
+
readonly clearLease?: (tool: ManagedToolName) => void;
|
|
82
|
+
readonly spawnDetached?: (command: string, args: readonly string[], options: {
|
|
83
|
+
readonly cwd: string;
|
|
84
|
+
}) => {
|
|
85
|
+
readonly pid?: number | undefined;
|
|
86
|
+
readonly unref?: () => void;
|
|
87
|
+
};
|
|
69
88
|
}
|
|
70
89
|
export declare function readOptionalToolFreshnessCache(path?: string): OptionalToolFreshnessCache;
|
|
71
90
|
export declare function writeOptionalToolFreshnessCache(cache: OptionalToolFreshnessCache, path?: string): void;
|
|
91
|
+
export declare function readOptionalToolRefreshLeaseCache(path?: string): OptionalToolRefreshLeaseCache;
|
|
92
|
+
export declare function writeOptionalToolRefreshLeaseCache(cache: OptionalToolRefreshLeaseCache, path?: string): void;
|
|
93
|
+
export declare function clearOptionalToolRefreshLease(tool: ManagedToolName, path?: string): void;
|
|
94
|
+
export declare function optionalToolRefreshLeaseRecord(pid: number | null, now: number): OptionalToolRefreshLeaseRecord;
|
|
72
95
|
export declare function supportsOptionalToolFreshness(adapter: OptionalToolAdapter): boolean;
|
|
73
96
|
/**
|
|
74
97
|
* Resolve a WP-managed optional-tool adapter for a dash provider.
|
|
@@ -78,4 +101,8 @@ export declare function tryResolveProviderToolAdapter(provider: DashProvider): O
|
|
|
78
101
|
export declare function resolveProviderToolAdapter(provider: DashProvider): OptionalToolAdapter;
|
|
79
102
|
export declare function refreshOptionalToolSync(input: OptionalToolFreshnessInput, deps?: OptionalToolFreshnessDeps): OptionalToolFreshnessResult;
|
|
80
103
|
export declare function ensureOptionalToolFreshnessForLaunch(input: Omit<OptionalToolFreshnessInput, "mode">, deps?: OptionalToolFreshnessDeps): Promise<OptionalToolFreshnessResult>;
|
|
104
|
+
export declare function runOptionalToolRefreshWorker(args: readonly string[], deps?: OptionalToolFreshnessDeps & {
|
|
105
|
+
readonly readOwnershipState?: () => ToolingOwnershipState;
|
|
106
|
+
readonly resolveVpCommand?: () => GlobalCapableVpCommandInput | null;
|
|
107
|
+
}): number;
|
|
81
108
|
export {};
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { mkdirSync, existsSync, rmSync } from "node:fs";
|
|
2
3
|
import { dirname } from "node:path";
|
|
3
|
-
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { UPDATE_CHECK_INTERVAL_MS } from "#cli/auto-update/cache.js";
|
|
5
|
+
import { buildSelfInvocationCommand } from "#cli/auto-update/self-invocation.js";
|
|
5
6
|
import { compareVersions } from "#cli/auto-update/version.js";
|
|
6
|
-
import { appendGlobalCapableVpArgs } from "#cli/global-vp.js";
|
|
7
|
-
import { optionalToolCanonicalCommand, resolveOptionalTool, } from "#cli/optional-tools.js";
|
|
8
|
-
import { isUserOwnedTool, } from "#cli/tooling-ownership.js";
|
|
7
|
+
import { appendGlobalCapableVpArgs, resolveGlobalCapableVpCommand, } from "#cli/global-vp.js";
|
|
8
|
+
import { optionalToolCanonicalCommand, OPTIONAL_TOOL_ADAPTERS, resolveOptionalTool, } from "#cli/optional-tools.js";
|
|
9
|
+
import { isUserOwnedTool, readToolingOwnershipState, } from "#cli/tooling-ownership.js";
|
|
9
10
|
import { getSurfacePath, withLock } from "#paths/state-root.js";
|
|
10
11
|
import { readTrustedJsonFile } from "#shared-utils/read-json-file.js";
|
|
11
12
|
import { writeJsonFileAtomic } from "#shared-utils/write-json-file.js";
|
|
12
13
|
const OPTIONAL_TOOL_FRESHNESS_CACHE = "optional-tool-freshness.json";
|
|
14
|
+
const OPTIONAL_TOOL_REFRESH_LEASE = "optional-tool-refresh-lease.json";
|
|
13
15
|
const VERSION_RE = /v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?/u;
|
|
16
|
+
const UNKNOWN_VERSION = "unknown";
|
|
14
17
|
/**
|
|
15
18
|
* Hot-path budget for version probes (`<tool> --version`) and registry
|
|
16
19
|
* latest-version queries (`vp info <pkg>`). These run on the dash/launch turn
|
|
@@ -30,9 +33,15 @@ export const OPTIONAL_TOOL_UPDATE_TIMEOUT_MS = 600_000;
|
|
|
30
33
|
function emptyCache() {
|
|
31
34
|
return { version: 1, tools: {} };
|
|
32
35
|
}
|
|
36
|
+
function emptyLeaseCache() {
|
|
37
|
+
return { version: 1, tools: {} };
|
|
38
|
+
}
|
|
33
39
|
function cachePath() {
|
|
34
40
|
return getSurfacePath(OPTIONAL_TOOL_FRESHNESS_CACHE, "user");
|
|
35
41
|
}
|
|
42
|
+
function leasePath() {
|
|
43
|
+
return getSurfacePath(OPTIONAL_TOOL_REFRESH_LEASE, "user");
|
|
44
|
+
}
|
|
36
45
|
export function readOptionalToolFreshnessCache(path = cachePath()) {
|
|
37
46
|
if (!existsSync(path))
|
|
38
47
|
return emptyCache();
|
|
@@ -61,6 +70,44 @@ export function writeOptionalToolFreshnessCache(cache, path = cachePath()) {
|
|
|
61
70
|
mkdirSync(dirname(path), { recursive: true });
|
|
62
71
|
writeJsonFileAtomic(path, cache);
|
|
63
72
|
}
|
|
73
|
+
export function readOptionalToolRefreshLeaseCache(path = leasePath()) {
|
|
74
|
+
if (!existsSync(path))
|
|
75
|
+
return emptyLeaseCache();
|
|
76
|
+
try {
|
|
77
|
+
const parsed = readTrustedJsonFile(path);
|
|
78
|
+
if (parsed.version !== 1 || parsed.tools === null || typeof parsed.tools !== "object") {
|
|
79
|
+
return emptyLeaseCache();
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
version: 1,
|
|
83
|
+
tools: Object.fromEntries(Object.entries(parsed.tools).filter((entry) => {
|
|
84
|
+
const record = entry[1];
|
|
85
|
+
return (record !== undefined &&
|
|
86
|
+
(typeof record.pid === "number" || record.pid === null) &&
|
|
87
|
+
typeof record.startedAt === "number" &&
|
|
88
|
+
typeof record.heartbeatAt === "number" &&
|
|
89
|
+
typeof record.expiresAt === "number");
|
|
90
|
+
})),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return emptyLeaseCache();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function writeOptionalToolRefreshLeaseCache(cache, path = leasePath()) {
|
|
98
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
99
|
+
writeJsonFileAtomic(path, cache);
|
|
100
|
+
}
|
|
101
|
+
export function clearOptionalToolRefreshLease(tool, path = leasePath()) {
|
|
102
|
+
const cache = readOptionalToolRefreshLeaseCache(path);
|
|
103
|
+
const tools = { ...cache.tools };
|
|
104
|
+
delete tools[tool];
|
|
105
|
+
if (Object.keys(tools).length === 0) {
|
|
106
|
+
rmSync(path, { force: true });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
writeOptionalToolRefreshLeaseCache({ version: 1, tools }, path);
|
|
110
|
+
}
|
|
64
111
|
function nextCache(cache, tool, record) {
|
|
65
112
|
return {
|
|
66
113
|
version: 1,
|
|
@@ -70,6 +117,15 @@ function nextCache(cache, tool, record) {
|
|
|
70
117
|
},
|
|
71
118
|
};
|
|
72
119
|
}
|
|
120
|
+
function nextLeaseCache(cache, tool, record) {
|
|
121
|
+
return {
|
|
122
|
+
version: 1,
|
|
123
|
+
tools: {
|
|
124
|
+
...cache.tools,
|
|
125
|
+
[tool]: record,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
73
129
|
function readFreshRecord(cache, tool, observedVersion, now) {
|
|
74
130
|
const record = cache.tools[tool];
|
|
75
131
|
if (!record)
|
|
@@ -80,6 +136,14 @@ function readFreshRecord(cache, tool, observedVersion, now) {
|
|
|
80
136
|
return null;
|
|
81
137
|
return record;
|
|
82
138
|
}
|
|
139
|
+
function readFreshLaunchRecord(cache, tool, now) {
|
|
140
|
+
const record = cache.tools[tool];
|
|
141
|
+
if (!record)
|
|
142
|
+
return null;
|
|
143
|
+
if (now - record.checkedAt > UPDATE_CHECK_INTERVAL_MS)
|
|
144
|
+
return null;
|
|
145
|
+
return record;
|
|
146
|
+
}
|
|
83
147
|
function parseVersion(output) {
|
|
84
148
|
const match = VERSION_RE.exec(output.trim());
|
|
85
149
|
return match?.[0] ?? null;
|
|
@@ -142,6 +206,72 @@ function persistRecord(deps, adapter, record) {
|
|
|
142
206
|
function cachedWarnings(record) {
|
|
143
207
|
return record.status === "warning" && record.warning ? [record.warning] : [];
|
|
144
208
|
}
|
|
209
|
+
function launchCacheResult(adapter, record, fromCache) {
|
|
210
|
+
return {
|
|
211
|
+
kind: "ready",
|
|
212
|
+
adapter,
|
|
213
|
+
observedVersion: record?.observedVersion ?? UNKNOWN_VERSION,
|
|
214
|
+
latestVersion: record?.latestVersion ?? null,
|
|
215
|
+
updated: false,
|
|
216
|
+
warnings: record ? cachedWarnings(record) : [],
|
|
217
|
+
fromCache,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function refreshLeaseIsActive(record, now) {
|
|
221
|
+
return record !== undefined && record.expiresAt > now;
|
|
222
|
+
}
|
|
223
|
+
export function optionalToolRefreshLeaseRecord(pid, now) {
|
|
224
|
+
return {
|
|
225
|
+
pid,
|
|
226
|
+
startedAt: now,
|
|
227
|
+
heartbeatAt: now,
|
|
228
|
+
// The worker may run a real package update with the foreground update
|
|
229
|
+
// deadline, so the launch lease must cover that blocking child.
|
|
230
|
+
expiresAt: now + OPTIONAL_TOOL_UPDATE_TIMEOUT_MS + 60_000,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function defaultSpawnDetached(command, args, options) {
|
|
234
|
+
const child = spawn(command, [...args], {
|
|
235
|
+
cwd: options.cwd,
|
|
236
|
+
detached: true,
|
|
237
|
+
env: process.env,
|
|
238
|
+
stdio: "ignore",
|
|
239
|
+
windowsHide: true,
|
|
240
|
+
});
|
|
241
|
+
child.unref();
|
|
242
|
+
return child;
|
|
243
|
+
}
|
|
244
|
+
function spawnOptionalToolRefreshWorker(input, deps, now) {
|
|
245
|
+
const readLease = deps.readLeaseCache ?? readOptionalToolRefreshLeaseCache;
|
|
246
|
+
const writeLease = deps.writeLeaseCache ?? writeOptionalToolRefreshLeaseCache;
|
|
247
|
+
const clearLease = deps.clearLease ?? clearOptionalToolRefreshLease;
|
|
248
|
+
const lease = readLease();
|
|
249
|
+
if (refreshLeaseIsActive(lease.tools[input.adapter.id], now))
|
|
250
|
+
return;
|
|
251
|
+
const command = buildSelfInvocationCommand([
|
|
252
|
+
"optional-tool-refresh",
|
|
253
|
+
input.adapter.id,
|
|
254
|
+
"--cwd",
|
|
255
|
+
input.cwd,
|
|
256
|
+
]);
|
|
257
|
+
try {
|
|
258
|
+
writeLease(nextLeaseCache(lease, input.adapter.id, optionalToolRefreshLeaseRecord(null, now)));
|
|
259
|
+
const child = (deps.spawnDetached ?? defaultSpawnDetached)(command[0], command.slice(1), {
|
|
260
|
+
cwd: input.cwd,
|
|
261
|
+
});
|
|
262
|
+
child.unref?.();
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
clearLease(input.adapter.id);
|
|
266
|
+
persistRecord(deps, input.adapter, {
|
|
267
|
+
checkedAt: now,
|
|
268
|
+
observedVersion: UNKNOWN_VERSION,
|
|
269
|
+
latestVersion: null,
|
|
270
|
+
status: "warning",
|
|
271
|
+
warning: `${input.adapter.ownershipName} optional refresh could not start; launching without freshness proof.${error instanceof Error ? ` ${error.message}.` : ""}`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
145
275
|
export function supportsOptionalToolFreshness(adapter) {
|
|
146
276
|
return adapter.namespace === "base" && Boolean(adapter.packageName && adapter.executableName);
|
|
147
277
|
}
|
|
@@ -312,8 +442,88 @@ export function refreshOptionalToolSync(input, deps = {}) {
|
|
|
312
442
|
};
|
|
313
443
|
}
|
|
314
444
|
export async function ensureOptionalToolFreshnessForLaunch(input, deps = {}) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
445
|
+
if (!isUserOwnedTool(input.ownershipState, input.adapter.id)) {
|
|
446
|
+
return {
|
|
447
|
+
kind: "unmanaged",
|
|
448
|
+
adapter: input.adapter,
|
|
449
|
+
observedVersion: UNKNOWN_VERSION,
|
|
450
|
+
warning: `${input.adapter.ownershipName} is installed but not WP-owned. Run \`${optionalToolCanonicalCommand(input.adapter)}\` to let WP keep it current.`,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return withLock("user", () => {
|
|
454
|
+
const now = deps.now?.() ?? Date.now();
|
|
455
|
+
const readCache = deps.readCache ?? readOptionalToolFreshnessCache;
|
|
456
|
+
const cache = readCache();
|
|
457
|
+
const fresh = readFreshLaunchRecord(cache, input.adapter.id, now);
|
|
458
|
+
if (fresh)
|
|
459
|
+
return launchCacheResult(input.adapter, fresh, true);
|
|
460
|
+
const stale = cache.tools[input.adapter.id] ?? null;
|
|
461
|
+
spawnOptionalToolRefreshWorker(input, deps, now);
|
|
462
|
+
return launchCacheResult(input.adapter, stale, stale !== null);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
export function runOptionalToolRefreshWorker(args, deps = {}) {
|
|
466
|
+
const [tool, ...rest] = args;
|
|
467
|
+
const adapter = OPTIONAL_TOOL_ADAPTERS.find((candidate) => candidate.id === tool);
|
|
468
|
+
if (!adapter || !supportsOptionalToolFreshness(adapter)) {
|
|
469
|
+
console.error(`optional-tool-refresh: unsupported tool ${tool ?? "<missing>"}`);
|
|
470
|
+
return 2;
|
|
471
|
+
}
|
|
472
|
+
let cwd = process.cwd();
|
|
473
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
474
|
+
const arg = rest[index];
|
|
475
|
+
if (arg === "--cwd") {
|
|
476
|
+
const value = rest[index + 1];
|
|
477
|
+
if (!value) {
|
|
478
|
+
console.error("optional-tool-refresh: --cwd requires a value");
|
|
479
|
+
return 2;
|
|
480
|
+
}
|
|
481
|
+
cwd = value;
|
|
482
|
+
index += 1;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
console.error(`optional-tool-refresh: unsupported argument ${arg}`);
|
|
486
|
+
return 2;
|
|
487
|
+
}
|
|
488
|
+
const now = deps.now?.() ?? Date.now();
|
|
489
|
+
const writeLease = deps.writeLeaseCache ?? writeOptionalToolRefreshLeaseCache;
|
|
490
|
+
const readLease = deps.readLeaseCache ?? readOptionalToolRefreshLeaseCache;
|
|
491
|
+
const clearLease = deps.clearLease ?? clearOptionalToolRefreshLease;
|
|
492
|
+
writeLease(nextLeaseCache(readLease(), adapter.id, optionalToolRefreshLeaseRecord(typeof process.pid === "number" ? process.pid : null, now)));
|
|
493
|
+
try {
|
|
494
|
+
const vpCommand = deps.resolveVpCommand !== undefined
|
|
495
|
+
? deps.resolveVpCommand()
|
|
496
|
+
: resolveGlobalCapableVpCommand();
|
|
497
|
+
if (!vpCommand) {
|
|
498
|
+
persistRecord(deps, adapter, {
|
|
499
|
+
checkedAt: now,
|
|
500
|
+
observedVersion: UNKNOWN_VERSION,
|
|
501
|
+
latestVersion: null,
|
|
502
|
+
status: "warning",
|
|
503
|
+
warning: `${adapter.ownershipName} optional refresh skipped because no global-capable Vite+ runner was found.`,
|
|
504
|
+
});
|
|
505
|
+
return 0;
|
|
506
|
+
}
|
|
507
|
+
const result = refreshOptionalToolSync({
|
|
508
|
+
adapter,
|
|
509
|
+
mode: "if-needed",
|
|
510
|
+
ownershipState: deps.readOwnershipState?.() ?? readToolingOwnershipState(),
|
|
511
|
+
vpCommand,
|
|
512
|
+
cwd,
|
|
513
|
+
}, deps);
|
|
514
|
+
if (result.kind === "missing") {
|
|
515
|
+
persistRecord(deps, adapter, {
|
|
516
|
+
checkedAt: now,
|
|
517
|
+
observedVersion: UNKNOWN_VERSION,
|
|
518
|
+
latestVersion: null,
|
|
519
|
+
status: "warning",
|
|
520
|
+
warning: result.warning,
|
|
521
|
+
});
|
|
522
|
+
return 0;
|
|
523
|
+
}
|
|
524
|
+
return 0;
|
|
525
|
+
}
|
|
526
|
+
finally {
|
|
527
|
+
clearLease(adapter.id);
|
|
528
|
+
}
|
|
319
529
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Daemon-side git helpers for review panel RPCs (Task 1.5).
|
|
3
3
|
* Runs real git via child_process — never re-implements porcelain.
|
|
4
|
+
*
|
|
5
|
+
* `gitCreatePr` additionally spawns `gh` (same class of utility-binary spawn
|
|
6
|
+
* as the `git` calls above — see Trust Dossier Material Decision MD1 on the
|
|
7
|
+
* desktop-daemon-rpc-completion blueprint overview for why this does not
|
|
8
|
+
* cross the daemon's "no agent-child spawn" invariant). It adapts
|
|
9
|
+
* the gh-wrapping logic from `src/mcp/tools/pr-upsert.ts` (preflight,
|
|
10
|
+
* collision detection, canonical re-fetch, output redaction) to this
|
|
11
|
+
* module's synchronous-git / injectable-deps pattern.
|
|
4
12
|
*/
|
|
13
|
+
import { type RunOutcome } from "#mcp/tools/_shared/run-command.js";
|
|
5
14
|
export type DiffFile = {
|
|
6
15
|
readonly path: string;
|
|
7
16
|
readonly status: string;
|
|
@@ -16,3 +25,23 @@ export declare function gitCommit(cwd: string, message: string, paths?: readonly
|
|
|
16
25
|
}): {
|
|
17
26
|
readonly commitSha: string;
|
|
18
27
|
};
|
|
28
|
+
export type CreatePrResult = {
|
|
29
|
+
readonly url: string;
|
|
30
|
+
readonly number?: number;
|
|
31
|
+
};
|
|
32
|
+
export interface GitCreatePrDeps {
|
|
33
|
+
/** Resolve the current branch name; throws on detached HEAD. Injectable for tests. */
|
|
34
|
+
readonly currentBranch?: (cwd: string) => string;
|
|
35
|
+
/** Invoke `gh` with args in `cwd`. Injectable for tests (no real `gh` binary required). */
|
|
36
|
+
readonly gh?: (args: readonly string[], cwd: string) => Promise<RunOutcome>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Upsert semantics (the frozen daemon contract has no create/update `mode`
|
|
40
|
+
* field, see Trust Dossier MD2): edit the PR for the current branch if one
|
|
41
|
+
* already exists, else create it against the repo's default branch. Every
|
|
42
|
+
* success path re-fetches the canonical PR view rather than trusting
|
|
43
|
+
* `gh pr create`'s stdout alone (matches `src/mcp/tools/pr-upsert.ts`'s
|
|
44
|
+
* canonical-refetch pattern) so a create-collision (two callers racing)
|
|
45
|
+
* still returns the real PR instead of throwing.
|
|
46
|
+
*/
|
|
47
|
+
export declare function gitCreatePr(cwd: string, title: string, body: string | undefined, deps?: GitCreatePrDeps): Promise<CreatePrResult>;
|