@webpresso/agent-kit 3.1.30 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +183 -20
- package/catalog/AGENTS.md.tpl +3 -3
- package/catalog/agent/agents/implementer.md +45 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
- package/catalog/agent/rules/pre-implementation.md +28 -1
- package/catalog/agent/rules/supported-agent-clis.md +9 -0
- package/catalog/agent/rules/typescript-version.md +50 -0
- package/catalog/agent/skills/codex/SKILL.md +25 -1
- package/catalog/agent/skills/verify/SKILL.md +4 -2
- package/catalog/docs/templates/blueprint.md +1 -1
- package/dist/esm/audit/agents.js +1 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
- package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
- package/dist/esm/audit/ci-test-perf.js +30 -0
- package/dist/esm/audit/command-surface.js +4 -2
- package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
- package/dist/esm/audit/host-substitution-risk.js +256 -0
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +13 -1
- package/dist/esm/audit/toolchain-isolation.js +1 -2
- package/dist/esm/audit/typescript-version.d.ts +2 -0
- package/dist/esm/audit/typescript-version.js +123 -0
- package/dist/esm/blueprint/core/parser.js +101 -2
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
- package/dist/esm/blueprint/core/validation/criteria.js +41 -2
- package/dist/esm/blueprint/core/validation/state.js +25 -14
- package/dist/esm/blueprint/db/migrations/run.js +4 -2
- package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
- package/dist/esm/blueprint/lifecycle/audit.js +87 -20
- package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
- package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
- package/dist/esm/blueprint/lifecycle/local.js +19 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
- package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/gates.d.ts +7 -0
- package/dist/esm/blueprint/trust/gates.js +20 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
- package/dist/esm/blueprint/trust/promotion.js +74 -112
- package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
- package/dist/esm/blueprint/trust/scaffold.js +64 -0
- package/dist/esm/blueprint/trust/validator.d.ts +1 -0
- package/dist/esm/blueprint/trust/validator.js +7 -1
- package/dist/esm/blueprint/utils/archive.js +27 -5
- package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
- package/dist/esm/blueprint/utils/document-paths.js +45 -12
- package/dist/esm/build/cli-mcp-parity.js +15 -0
- package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
- package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +43 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
- package/dist/esm/cli/commands/agent-launch.js +93 -4
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit-core.js +8 -1
- package/dist/esm/cli/commands/audit.js +21 -5
- package/dist/esm/cli/commands/blueprint/execution.js +18 -6
- package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
- package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
- package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
- package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
- package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
- package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
- package/dist/esm/cli/commands/blueprint/router.js +13 -1
- package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
- package/dist/esm/cli/commands/ci-preflight.js +30 -6
- package/dist/esm/cli/commands/compile.js +5 -31
- package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
- package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
- package/dist/esm/cli/commands/doctor.js +5 -0
- package/dist/esm/cli/commands/hook.d.ts +5 -0
- package/dist/esm/cli/commands/hook.js +15 -9
- package/dist/esm/cli/commands/init/config.js +2 -2
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
- package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
- package/dist/esm/cli/commands/init/convergence-state.js +54 -0
- package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
- package/dist/esm/cli/commands/init/convergence.js +57 -0
- package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
- package/dist/esm/cli/commands/init/git-convergence.js +168 -0
- package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
- package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
- package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
- package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
- package/dist/esm/cli/commands/init/host-visibility.js +94 -5
- package/dist/esm/cli/commands/init/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/index.js +326 -173
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
- package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
- package/dist/esm/cli/commands/init/merge.d.ts +1 -0
- package/dist/esm/cli/commands/init/merge.js +30 -1
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
- package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
- package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
- package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
- package/dist/esm/cli/commands/opencode-rotate.js +147 -0
- package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
- package/dist/esm/cli/commands/public-readiness.js +42 -0
- package/dist/esm/cli/commands/quality-runner.js +3 -3
- package/dist/esm/cli/commands/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +751 -209
- package/dist/esm/cli/commands/run-wait.d.ts +2 -0
- package/dist/esm/cli/commands/run-wait.js +122 -0
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/setup-help.js +1 -1
- package/dist/esm/cli/commands/setup-repair-help.js +1 -1
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/cli/direct-provider-launch.js +9 -5
- package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
- package/dist/esm/cli/optional-tool-freshness.js +219 -9
- package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
- package/dist/esm/daemon/domains/git-actions.js +136 -0
- package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
- package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
- package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
- package/dist/esm/daemon/domains/sessions-search.js +29 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
- package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
- package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
- package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
- package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/git-handlers.js +27 -0
- package/dist/esm/daemon/handlers/product.d.ts +7 -0
- package/dist/esm/daemon/handlers/product.js +10 -119
- package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
- package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
- package/dist/esm/daemon/handlers/shared.d.ts +5 -0
- package/dist/esm/daemon/handlers/shared.js +16 -0
- package/dist/esm/daemon/methods.d.ts +8 -1
- package/dist/esm/daemon/methods.js +7 -0
- package/dist/esm/daemon/protocol/contract.d.ts +43 -0
- package/dist/esm/daemon/protocol/contract.js +26 -0
- package/dist/esm/daemon/protocol/index.d.ts +1 -1
- package/dist/esm/daemon/protocol/index.js +1 -1
- package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
- package/dist/esm/hooks/__conformance__/matrix.js +137 -0
- package/dist/esm/hooks/conformance/batch.js +5 -0
- package/dist/esm/hooks/doctor.d.ts +5 -3
- package/dist/esm/hooks/doctor.js +67 -16
- package/dist/esm/hooks/permission-request/index.js +9 -3
- package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
- package/dist/esm/hooks/permission-request/policy.js +13 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
- package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/logger.js +16 -0
- package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/runner.js +68 -16
- package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
- package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
- package/dist/esm/hooks/shared/guard-outcome.js +215 -0
- package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
- package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
- package/dist/esm/hooks/shared/types.d.ts +8 -5
- package/dist/esm/hooks/shared/types.js +50 -9
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
- package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
- package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
- package/dist/esm/mcp/blueprint/registration.js +3 -2
- package/dist/esm/mcp/server.d.ts +1 -0
- package/dist/esm/mcp/server.js +27 -0
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +5 -0
- package/dist/esm/mcp/tools/_registry.js +10 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/result.js +44 -1
- package/dist/esm/mcp/tools/audit.d.ts +5 -1
- package/dist/esm/mcp/tools/audit.js +42 -0
- package/dist/esm/mcp/tools/audits.d.ts +4 -0
- package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
- package/dist/esm/mcp/tools/ci-preflight.js +183 -0
- package/dist/esm/mcp/tools/pr-upsert.js +39 -1
- package/dist/esm/mcp/tools/review-run.d.ts +28 -0
- package/dist/esm/mcp/tools/review-run.js +216 -0
- package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
- package/dist/esm/mcp/tools/run-wait.js +593 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/mcp/tools/session-id.d.ts +3 -0
- package/dist/esm/mcp/tools/session-id.js +45 -0
- package/dist/esm/mcp/tools/session-info.d.ts +3 -0
- package/dist/esm/mcp/tools/session-info.js +49 -0
- package/dist/esm/output-transforms/audit.d.ts +23 -0
- package/dist/esm/output-transforms/audit.js +47 -0
- package/dist/esm/output-transforms/index.js +2 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +27 -14
- package/dist/esm/review/availability.d.ts +33 -0
- package/dist/esm/review/availability.js +122 -20
- package/dist/esm/review/delivery-verifier.d.ts +27 -0
- package/dist/esm/review/delivery-verifier.js +115 -0
- package/dist/esm/review/events.d.ts +3 -2
- package/dist/esm/review/events.js +23 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +36 -10
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/output-policy.d.ts +2 -0
- package/dist/esm/review/execution/output-policy.js +12 -0
- package/dist/esm/review/execution/review-checkout.js +121 -21
- package/dist/esm/review/execution/supervisor.js +36 -4
- package/dist/esm/review/execution/types.d.ts +9 -0
- package/dist/esm/review/opencode-account.d.ts +24 -0
- package/dist/esm/review/opencode-account.js +68 -0
- package/dist/esm/review/opencode-auth.d.ts +33 -0
- package/dist/esm/review/opencode-auth.js +79 -0
- package/dist/esm/review/subject.d.ts +25 -0
- package/dist/esm/review/subject.js +116 -26
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +32 -25
- package/dist/esm/test/duration-sequencer.d.ts +20 -0
- package/dist/esm/test/duration-sequencer.js +54 -0
- package/dist/esm/test/shard-durations.json +922 -0
- package/dist/esm/test/worker-budget.d.ts +6 -0
- package/dist/esm/test/worker-budget.js +59 -1
- package/dist/esm/test-helpers/global-setup.d.ts +1 -2
- package/dist/esm/test-helpers/global-setup.js +79 -32
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/ultragoal/service.d.ts +7 -0
- package/dist/esm/ultragoal/service.js +42 -2
- package/dist/esm/utils/write-json-file.js +3 -1
- package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
- package/dist/esm/worktrees/codex-project-trust.js +56 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/dist/esm/worktrees/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +18 -13
|
@@ -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>;
|
|
@@ -1,8 +1,18 @@
|
|
|
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
|
*/
|
|
5
13
|
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { runCommand, isRunFailure } from "#mcp/tools/_shared/run-command.js";
|
|
15
|
+
import { redactText } from "#mcp/tools/_shared/redact.js";
|
|
6
16
|
export function gitDiff(cwd) {
|
|
7
17
|
const out = execFileSync("git", ["status", "--porcelain=v1", "-uall", "--no-renames"], {
|
|
8
18
|
cwd,
|
|
@@ -45,3 +55,129 @@ export function gitCommit(cwd, message, paths, options) {
|
|
|
45
55
|
}).trim();
|
|
46
56
|
return { commitSha };
|
|
47
57
|
}
|
|
58
|
+
/** Same default timeoutMs `src/mcp/tools/pr-upsert.ts` already uses for `gh` calls. */
|
|
59
|
+
const GH_TIMEOUT_MS = 120_000;
|
|
60
|
+
function defaultCurrentBranch(cwd) {
|
|
61
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
62
|
+
cwd,
|
|
63
|
+
encoding: "utf8",
|
|
64
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
65
|
+
}).trim();
|
|
66
|
+
if (!branch || branch === "HEAD") {
|
|
67
|
+
throw new Error("git.createPr requires a named branch (HEAD is detached)");
|
|
68
|
+
}
|
|
69
|
+
return branch;
|
|
70
|
+
}
|
|
71
|
+
function defaultGh(args, cwd) {
|
|
72
|
+
return runCommand("gh", args, { cwd, timeoutMs: GH_TIMEOUT_MS });
|
|
73
|
+
}
|
|
74
|
+
function outcomeText(outcome) {
|
|
75
|
+
if (isRunFailure(outcome))
|
|
76
|
+
return outcome.error.message;
|
|
77
|
+
return [outcome.stdout, outcome.stderr].filter(Boolean).join("\n");
|
|
78
|
+
}
|
|
79
|
+
function outcomeOk(outcome) {
|
|
80
|
+
return !isRunFailure(outcome) && outcome.exitCode === 0;
|
|
81
|
+
}
|
|
82
|
+
function parseTarget(outcome) {
|
|
83
|
+
if (!outcomeOk(outcome))
|
|
84
|
+
return undefined;
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(outcome.stdout);
|
|
87
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
88
|
+
return undefined;
|
|
89
|
+
const value = parsed;
|
|
90
|
+
return {
|
|
91
|
+
number: typeof value.number === "number" ? value.number : undefined,
|
|
92
|
+
url: typeof value.url === "string" ? value.url : undefined,
|
|
93
|
+
title: typeof value.title === "string" ? value.title : undefined,
|
|
94
|
+
state: typeof value.state === "string" ? value.state : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Mirrors `src/mcp/tools/pr-upsert.ts`'s `isNoPr` string-matching (do not reinvent). */
|
|
102
|
+
function isNoPr(outcome) {
|
|
103
|
+
if (outcomeOk(outcome))
|
|
104
|
+
return false;
|
|
105
|
+
const text = outcomeText(outcome).toLowerCase();
|
|
106
|
+
return (text.includes("no pull requests found") ||
|
|
107
|
+
text.includes("no pull request found") ||
|
|
108
|
+
text.includes("could not find pull request"));
|
|
109
|
+
}
|
|
110
|
+
/** Mirrors `src/mcp/tools/pr-upsert.ts`'s `isCreateCollision` string-matching (do not reinvent). */
|
|
111
|
+
function isCreateCollision(outcome) {
|
|
112
|
+
if (outcomeOk(outcome))
|
|
113
|
+
return false;
|
|
114
|
+
const text = outcomeText(outcome).toLowerCase();
|
|
115
|
+
return text.includes("already exists") || text.includes("a pull request already");
|
|
116
|
+
}
|
|
117
|
+
function throwGhFailure(action, outcome) {
|
|
118
|
+
const detail = redactText(outcomeText(outcome)) || "unknown error";
|
|
119
|
+
throw new Error(`git.createPr: gh ${action} failed: ${detail}`);
|
|
120
|
+
}
|
|
121
|
+
function requireUrl(target, context) {
|
|
122
|
+
if (!target.url) {
|
|
123
|
+
throw new Error(`git.createPr: ${context} did not return a PR url`);
|
|
124
|
+
}
|
|
125
|
+
return target.url;
|
|
126
|
+
}
|
|
127
|
+
function buildEditArgs(branch, title, body) {
|
|
128
|
+
const args = ["pr", "edit", branch, "--title", title];
|
|
129
|
+
if (body !== undefined)
|
|
130
|
+
args.push("--body", body);
|
|
131
|
+
return args;
|
|
132
|
+
}
|
|
133
|
+
function buildCreateArgs(branch, base, title, body) {
|
|
134
|
+
const args = ["pr", "create", "--head", branch, "--base", base, "--title", title];
|
|
135
|
+
if (body !== undefined)
|
|
136
|
+
args.push("--body", body);
|
|
137
|
+
return args;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Upsert semantics (the frozen daemon contract has no create/update `mode`
|
|
141
|
+
* field, see Trust Dossier MD2): edit the PR for the current branch if one
|
|
142
|
+
* already exists, else create it against the repo's default branch. Every
|
|
143
|
+
* success path re-fetches the canonical PR view rather than trusting
|
|
144
|
+
* `gh pr create`'s stdout alone (matches `src/mcp/tools/pr-upsert.ts`'s
|
|
145
|
+
* canonical-refetch pattern) so a create-collision (two callers racing)
|
|
146
|
+
* still returns the real PR instead of throwing.
|
|
147
|
+
*/
|
|
148
|
+
export async function gitCreatePr(cwd, title, body, deps) {
|
|
149
|
+
const currentBranch = deps?.currentBranch ?? defaultCurrentBranch;
|
|
150
|
+
const gh = deps?.gh ?? defaultGh;
|
|
151
|
+
const branch = currentBranch(cwd);
|
|
152
|
+
const preflight = await gh(["pr", "view", branch, "--json", "number,url,title,state"], cwd);
|
|
153
|
+
if (outcomeOk(preflight)) {
|
|
154
|
+
const target = parseTarget(preflight);
|
|
155
|
+
if (!target)
|
|
156
|
+
throwGhFailure("pr view (preflight)", preflight);
|
|
157
|
+
const edit = await gh(buildEditArgs(branch, title, body), cwd);
|
|
158
|
+
if (!outcomeOk(edit))
|
|
159
|
+
throwGhFailure("pr edit", edit);
|
|
160
|
+
return { url: requireUrl(target, "pr view (preflight)"), number: target.number };
|
|
161
|
+
}
|
|
162
|
+
if (!isNoPr(preflight))
|
|
163
|
+
throwGhFailure("pr view (preflight)", preflight);
|
|
164
|
+
const repoView = await gh(["repo", "view", "--json", "defaultBranchRef", "-q", ".defaultBranchRef.name"], cwd);
|
|
165
|
+
if (!outcomeOk(repoView))
|
|
166
|
+
throwGhFailure("repo view (default branch)", repoView);
|
|
167
|
+
const base = repoView.stdout.trim();
|
|
168
|
+
if (!base) {
|
|
169
|
+
throw new Error("git.createPr: could not resolve the repository default branch");
|
|
170
|
+
}
|
|
171
|
+
const create = await gh(buildCreateArgs(branch, base, title, body), cwd);
|
|
172
|
+
if (!outcomeOk(create) && !isCreateCollision(create)) {
|
|
173
|
+
throwGhFailure("pr create", create);
|
|
174
|
+
}
|
|
175
|
+
const canonical = await gh(["pr", "view", branch, "--json", "number,url,title,state"], cwd);
|
|
176
|
+
const canonicalTarget = parseTarget(canonical);
|
|
177
|
+
if (!canonicalTarget)
|
|
178
|
+
throwGhFailure("pr view (canonical)", canonical);
|
|
179
|
+
return {
|
|
180
|
+
url: requireUrl(canonicalTarget, "pr view (canonical)"),
|
|
181
|
+
number: canonicalTarget.number,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Cross-process run-registry interop between wp dash and the desktop app (1.6).
|
|
3
3
|
* Reuses the existing dash run-registry store paths — no second registry.
|
|
4
4
|
*/
|
|
5
|
+
import type { DashRun } from "#cli/commands/dash/run-types.js";
|
|
6
|
+
import type { SessionsListResult, SessionSummary } from "#daemon/protocol/contract.js";
|
|
5
7
|
export type RunRegistrySnapshot = {
|
|
6
8
|
readonly root: string;
|
|
7
9
|
readonly runIds: readonly string[];
|
|
@@ -12,3 +14,17 @@ export type RunRegistrySnapshot = {
|
|
|
12
14
|
* `root` should be the directory that contains per-run JSON sidecars.
|
|
13
15
|
*/
|
|
14
16
|
export declare function listRunRegistry(root: string): RunRegistrySnapshot;
|
|
17
|
+
/** Pure mapping from a dash run to the daemon's UI-facing session shape. */
|
|
18
|
+
export declare function toSessionSummary(run: DashRun): SessionSummary;
|
|
19
|
+
export interface ListDaemonSessionsOptions {
|
|
20
|
+
readonly cwd?: string;
|
|
21
|
+
readonly limit?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Daemon `sessions.list` backing implementation: reads the real dash
|
|
25
|
+
* run-registry (no second registry) and maps it to the daemon wire shape.
|
|
26
|
+
* Complexity: O(n log n) for the updatedAt-descending sort (n = dash runs at
|
|
27
|
+
* one repo root — small/bounded); no additional filesystem scans beyond the
|
|
28
|
+
* single locked registry read `readDashRuns` already performs.
|
|
29
|
+
*/
|
|
30
|
+
export declare function listDaemonSessions(options?: ListDaemonSessionsOptions): Promise<SessionsListResult>;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
+
import { readDashRuns } from "#cli/commands/dash/run-registry.js";
|
|
7
8
|
/**
|
|
8
9
|
* List run ids under a dash-compatible registry root.
|
|
9
10
|
* `root` should be the directory that contains per-run JSON sidecars.
|
|
@@ -27,3 +28,47 @@ export function listRunRegistry(root) {
|
|
|
27
28
|
}
|
|
28
29
|
return { root, runIds: runIds.sort(), readable: true };
|
|
29
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Daemon `providerIdSchema` (src/daemon/protocol/contract.ts) only accepts
|
|
33
|
+
* 'claude' | 'codex' | 'opencode' | 'fleet'. `DashRun.provider` can also be
|
|
34
|
+
* 'grok' (src/cli/commands/dash/provider-capabilities.ts DASH_PROVIDERS).
|
|
35
|
+
* `parseUiMethodResult` is `.strict()` and would throw on the whole response
|
|
36
|
+
* for one out-of-enum row, so an unrecognized provider is omitted (the field
|
|
37
|
+
* is optional on `SessionSummary`) rather than passed through or fabricated.
|
|
38
|
+
*/
|
|
39
|
+
const KNOWN_DAEMON_PROVIDERS = new Set([
|
|
40
|
+
"claude",
|
|
41
|
+
"codex",
|
|
42
|
+
"opencode",
|
|
43
|
+
"fleet",
|
|
44
|
+
]);
|
|
45
|
+
function isKnownDaemonProvider(provider) {
|
|
46
|
+
return KNOWN_DAEMON_PROVIDERS.has(provider);
|
|
47
|
+
}
|
|
48
|
+
/** Pure mapping from a dash run to the daemon's UI-facing session shape. */
|
|
49
|
+
export function toSessionSummary(run) {
|
|
50
|
+
return {
|
|
51
|
+
id: run.id,
|
|
52
|
+
...(isKnownDaemonProvider(run.provider) ? { provider: run.provider } : {}),
|
|
53
|
+
label: run.label,
|
|
54
|
+
state: run.state,
|
|
55
|
+
cwd: run.cwd,
|
|
56
|
+
permissionMode: run.permissionMode,
|
|
57
|
+
updatedAt: run.updatedAt,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Daemon `sessions.list` backing implementation: reads the real dash
|
|
62
|
+
* run-registry (no second registry) and maps it to the daemon wire shape.
|
|
63
|
+
* Complexity: O(n log n) for the updatedAt-descending sort (n = dash runs at
|
|
64
|
+
* one repo root — small/bounded); no additional filesystem scans beyond the
|
|
65
|
+
* single locked registry read `readDashRuns` already performs.
|
|
66
|
+
*/
|
|
67
|
+
export async function listDaemonSessions(options = {}) {
|
|
68
|
+
const { runs } = await readDashRuns({ cwd: options.cwd });
|
|
69
|
+
const sorted = [...runs].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
70
|
+
const limited = typeof options.limit === "number" && options.limit >= 0
|
|
71
|
+
? sorted.slice(0, options.limit)
|
|
72
|
+
: sorted;
|
|
73
|
+
return { sessions: limited.map(toSessionSummary) };
|
|
74
|
+
}
|
|
@@ -18,3 +18,14 @@ export declare function searchSessions(backend: SessionSearchBackend, options: {
|
|
|
18
18
|
readonly query: string;
|
|
19
19
|
readonly limit?: number;
|
|
20
20
|
}): readonly SessionSearchHit[];
|
|
21
|
+
/**
|
|
22
|
+
* Production backend: opens the real `SessionMemoryStore` FTS index (porter
|
|
23
|
+
* → trigram → Levenshtein fallback, same engine behind `wp_session_search`)
|
|
24
|
+
* and reuses its indexed search — never a table scan. A fresh store is
|
|
25
|
+
* opened and closed per call, mirroring the exact pattern
|
|
26
|
+
* `src/mcp/tools/session-restore.ts`'s `buildRecallPayload` already uses for
|
|
27
|
+
* the identical class (avoids a stale open SQLite/WAL handle). Caching a
|
|
28
|
+
* store across daemon-lifetime calls is intentionally deferred (YAGNI) until
|
|
29
|
+
* there is evidence of per-RPC latency pressure.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createSessionMemorySearchBackend(indexDbPath: string): SessionSearchBackend;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Session search domain (Task 3.7).
|
|
3
3
|
* Pure ranking/filter helpers + injectable FTS backend (session-memory store).
|
|
4
4
|
*/
|
|
5
|
+
import { SessionMemoryStore } from "#session-memory/store.js";
|
|
5
6
|
/** Normalize and bound a search query; empty → no search. */
|
|
6
7
|
export function normalizeSearchQuery(query) {
|
|
7
8
|
return query.trim().replace(/\s+/gu, " ").slice(0, 200);
|
|
@@ -14,3 +15,31 @@ export function searchSessions(backend, options) {
|
|
|
14
15
|
const limit = Math.min(Math.max(options.limit ?? 20, 1), 100);
|
|
15
16
|
return backend.search(q, limit);
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Production backend: opens the real `SessionMemoryStore` FTS index (porter
|
|
20
|
+
* → trigram → Levenshtein fallback, same engine behind `wp_session_search`)
|
|
21
|
+
* and reuses its indexed search — never a table scan. A fresh store is
|
|
22
|
+
* opened and closed per call, mirroring the exact pattern
|
|
23
|
+
* `src/mcp/tools/session-restore.ts`'s `buildRecallPayload` already uses for
|
|
24
|
+
* the identical class (avoids a stale open SQLite/WAL handle). Caching a
|
|
25
|
+
* store across daemon-lifetime calls is intentionally deferred (YAGNI) until
|
|
26
|
+
* there is evidence of per-RPC latency pressure.
|
|
27
|
+
*/
|
|
28
|
+
export function createSessionMemorySearchBackend(indexDbPath) {
|
|
29
|
+
return {
|
|
30
|
+
search(query, limit) {
|
|
31
|
+
const store = new SessionMemoryStore(indexDbPath);
|
|
32
|
+
try {
|
|
33
|
+
return store.search({ query, limit }).map((hit) => ({
|
|
34
|
+
id: hit.id,
|
|
35
|
+
source: hit.source,
|
|
36
|
+
text: hit.text,
|
|
37
|
+
score: hit.score,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
store.close();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|