@webpresso/agent-kit 3.1.26 → 3.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/catalog/AGENTS.md.tpl +7 -11
- package/catalog/agent/commands/verify.md +2 -2
- package/catalog/agent/rules/changeset-release.md +4 -2
- package/catalog/agent/rules/ci-cost-local-first.md +139 -0
- package/catalog/agent/rules/ci-test-perf.md +5 -1
- package/catalog/agent/rules/cmd-execution.md +9 -0
- package/catalog/agent/rules/pre-implementation.md +2 -1
- package/catalog/agent/rules/test-scan-perf.md +80 -0
- package/catalog/agent/rules/workflow-skills-routing.md +9 -4
- package/catalog/agent/skills/autopilot/SKILL.md +48 -11
- package/catalog/agent/skills/claude/SKILL.md +7 -0
- package/catalog/agent/skills/codex/SKILL.md +7 -0
- package/catalog/agent/skills/fix/SKILL.md +36 -8
- package/catalog/agent/skills/grok/SKILL.md +7 -0
- package/catalog/agent/skills/investigate/SKILL.md +12 -1
- package/catalog/agent/skills/pll/SKILL.md +53 -48
- package/catalog/agent/skills/team/SKILL.md +26 -9
- package/catalog/agent/skills/ultragoal/SKILL.md +147 -19
- package/catalog/agent/skills/verify/SKILL.md +89 -51
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +8 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.js +25 -1
- package/dist/esm/audit/blueprint-pr-coverage.js +68 -7
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +3 -0
- package/dist/esm/audit/roadmap-links.js +6 -0
- package/dist/esm/audit/test-scan-perf.d.ts +18 -0
- package/dist/esm/audit/test-scan-perf.js +275 -0
- package/dist/esm/blueprint/lifecycle/engine.d.ts +9 -0
- package/dist/esm/blueprint/lifecycle/engine.js +53 -6
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +93 -12
- package/dist/esm/blueprint/markdown/helpers.js +6 -2
- package/dist/esm/ci/act-replay.js +110 -7
- package/dist/esm/ci/act-runner.d.ts +4 -0
- package/dist/esm/ci/act-runner.js +85 -26
- package/dist/esm/ci/act-worktree-git.d.ts +11 -0
- package/dist/esm/ci/act-worktree-git.js +219 -0
- package/dist/esm/ci/native-session-memory-cache.d.ts +9 -1
- package/dist/esm/ci/native-session-memory-cache.js +9 -1
- package/dist/esm/ci/vitest-ci-shards.d.ts +6 -4
- package/dist/esm/ci/vitest-ci-shards.js +6 -4
- package/dist/esm/cli/auto-update/guard-tombstone.d.ts +25 -0
- package/dist/esm/cli/auto-update/guard-tombstone.js +67 -0
- package/dist/esm/cli/auto-update/installer.d.ts +4 -17
- package/dist/esm/cli/auto-update/installer.js +6 -57
- package/dist/esm/cli/auto-update/self-invocation.d.ts +37 -0
- package/dist/esm/cli/auto-update/self-invocation.js +62 -0
- package/dist/esm/cli/commands/audit.js +4 -0
- package/dist/esm/cli/commands/blueprint/mutations.js +35 -18
- package/dist/esm/cli/commands/blueprint/router.js +27 -2
- package/dist/esm/cli/commands/ci.d.ts +8 -1
- package/dist/esm/cli/commands/ci.js +80 -21
- package/dist/esm/cli/commands/dash/index.js +3 -3
- package/dist/esm/cli/commands/dash/keymap.js +1 -0
- package/dist/esm/cli/commands/dash/launch-preferences.d.ts +15 -0
- package/dist/esm/cli/commands/dash/launch-preferences.js +46 -17
- package/dist/esm/cli/commands/dash/plan-once-attention.d.ts +32 -0
- package/dist/esm/cli/commands/dash/plan-once-attention.js +50 -0
- package/dist/esm/cli/commands/dash/plan-once-label.d.ts +7 -0
- package/dist/esm/cli/commands/dash/plan-once-label.js +5 -0
- package/dist/esm/cli/commands/dash/plan-once-membership.d.ts +67 -0
- package/dist/esm/cli/commands/dash/plan-once-membership.js +151 -0
- package/dist/esm/cli/commands/dash/plan-once-merge.d.ts +143 -0
- package/dist/esm/cli/commands/dash/plan-once-merge.js +238 -0
- package/dist/esm/cli/commands/dash/plan-once-schema.d.ts +73 -0
- package/dist/esm/cli/commands/dash/plan-once-schema.js +119 -0
- package/dist/esm/cli/commands/dash/plan-once.d.ts +142 -0
- package/dist/esm/cli/commands/dash/plan-once.js +261 -0
- package/dist/esm/cli/commands/dash/tui/attention-hud.d.ts +9 -0
- package/dist/esm/cli/commands/dash/tui/attention-hud.js +9 -0
- package/dist/esm/cli/commands/dash/tui/chat-bridge.d.ts +41 -0
- package/dist/esm/cli/commands/dash/tui/chat-bridge.js +73 -0
- package/dist/esm/cli/commands/dash/tui/chat-mode.d.ts +47 -0
- package/dist/esm/cli/commands/dash/tui/chat-mode.js +98 -0
- package/dist/esm/cli/commands/dash/tui/frame.d.ts +3 -0
- package/dist/esm/cli/commands/dash/tui/frame.js +3 -1
- package/dist/esm/cli/commands/dash/tui/ipc-protocol.d.ts +87 -0
- package/dist/esm/cli/commands/dash/tui/ipc-protocol.js +128 -0
- package/dist/esm/cli/commands/dash/tui/native-host.d.ts +34 -0
- package/dist/esm/cli/commands/dash/tui/native-host.js +256 -0
- package/dist/esm/cli/commands/dash/tui/native-path.d.ts +11 -0
- package/dist/esm/cli/commands/dash/tui/native-path.js +41 -0
- package/dist/esm/cli/commands/dash/tui/palette.d.ts +1 -1
- package/dist/esm/cli/commands/dash/tui/palette.js +14 -0
- package/dist/esm/cli/commands/dash/tui/runtime.js +311 -4
- package/dist/esm/cli/commands/dash/tui/state.d.ts +41 -0
- package/dist/esm/cli/commands/dash/tui/state.js +257 -4
- package/dist/esm/cli/commands/format.js +0 -2
- package/dist/esm/cli/commands/init/config.d.ts +0 -1
- package/dist/esm/cli/commands/init/config.js +1 -4
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.d.ts +5 -6
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/claude.js +6 -13
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.d.ts +0 -2
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/ir.js +0 -2
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.d.ts +6 -3
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/merge.js +39 -26
- package/dist/esm/cli/commands/init/scaffolders/agent-kit-global/index.js +1 -1
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +61 -23
- package/dist/esm/cli/commands/logs.d.ts +15 -0
- package/dist/esm/cli/commands/logs.js +192 -3
- package/dist/esm/cli/commands/package-manager.js +2 -1
- package/dist/esm/cli/commands/quality-log-store.d.ts +1 -0
- package/dist/esm/cli/commands/quality-log-store.js +187 -51
- package/dist/esm/cli/commands/review.js +5 -8
- package/dist/esm/cli/commands/secrets.d.ts +1 -0
- package/dist/esm/cli/commands/secrets.js +4 -1
- package/dist/esm/cli/commands/self-install-guard.d.ts +14 -32
- package/dist/esm/cli/commands/self-install-guard.js +17 -63
- package/dist/esm/cli/direct-provider-launch.js +25 -1
- package/dist/esm/hooks/doctor.d.ts +15 -0
- package/dist/esm/hooks/doctor.js +56 -0
- package/dist/esm/hooks/guard-switch/index.js +8 -4
- package/dist/esm/hooks/post-tool/lint-after-edit.js +6 -4
- package/dist/esm/hooks/precompact/index.js +5 -3
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +4 -4
- package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.d.ts +0 -1
- package/dist/esm/hooks/pretool-guard/validators/mcp-redirect.js +8 -5
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.d.ts +9 -7
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +126 -14
- package/dist/esm/hooks/sessionstart/index.d.ts +10 -0
- package/dist/esm/hooks/sessionstart/index.js +61 -4
- package/dist/esm/hooks/shared/types.d.ts +11 -0
- package/dist/esm/hooks/shared/types.js +21 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/audit.d.ts +2 -1
- package/dist/esm/mcp/tools/audit.js +10 -0
- package/dist/esm/mcp/tools/audits.d.ts +1 -0
- package/dist/esm/mcp/tools/ci-act.js +9 -11
- package/dist/esm/paths/state-root.js +9 -0
- package/dist/esm/review/authority.js +17 -2
- package/dist/esm/review/availability.js +18 -5
- package/dist/esm/review/subject.d.ts +15 -0
- package/dist/esm/review/subject.js +105 -10
- package/dist/esm/runtime/executor.d.ts +1 -0
- package/dist/esm/runtime/executor.js +2 -1
- package/dist/esm/secret-gate/runner.d.ts +1 -0
- package/dist/esm/secret-gate/runner.js +4 -1
- package/dist/esm/session-memory/current-session.js +2 -0
- package/dist/esm/session-memory/native-runtime.js +5 -3
- package/dist/esm/session-memory/native-warm-markers.d.ts +18 -0
- package/dist/esm/session-memory/native-warm-markers.js +22 -0
- package/dist/esm/session-memory/sync/types.d.ts +4 -0
- package/dist/esm/session-memory/types.d.ts +3 -1
- package/dist/esm/session-memory/types.js +2 -0
- package/dist/esm/ultragoal/runtime.js +44 -2
- package/dist/esm/worktrees/current.d.ts +1 -0
- package/dist/esm/worktrees/current.js +1 -0
- package/dist/esm/worktrees/project-agent-surfaces.d.ts +1 -0
- package/dist/esm/worktrees/project-agent-surfaces.js +2 -1
- package/package.json +17 -14
|
@@ -161,6 +161,31 @@ async function reIngestDb(cwd) {
|
|
|
161
161
|
return;
|
|
162
162
|
await reIngestProjection(cwd);
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Force reingest of one blueprint document even when the stored content hash
|
|
166
|
+
* still matches. Used on complete so task-table lag cannot block finalize.
|
|
167
|
+
*/
|
|
168
|
+
async function forceReIngestBlueprint(cwd, slug, documentPath) {
|
|
169
|
+
const target = dbPath(cwd);
|
|
170
|
+
if (!existsSync(target))
|
|
171
|
+
return;
|
|
172
|
+
try {
|
|
173
|
+
const conn = openDb(target);
|
|
174
|
+
try {
|
|
175
|
+
// Invalidate hash so ingestAll's skip-if-unchanged path rewrites tasks.
|
|
176
|
+
conn.db.prepare(`UPDATE blueprints SET content_hash = NULL WHERE slug = ?`).run(slug);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
conn.close();
|
|
180
|
+
}
|
|
181
|
+
await reIngestProjection(cwd);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Projection is best-effort for complete; markdown gate already passed.
|
|
185
|
+
// Swallow so a broken local projection cannot block lifecycle completion.
|
|
186
|
+
void documentPath;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
164
189
|
/**
|
|
165
190
|
* Update `status:` in YAML frontmatter. Preserves everything else verbatim.
|
|
166
191
|
*/
|
|
@@ -306,7 +331,12 @@ async function promoteBlueprintLocked(cwd, slug, toState) {
|
|
|
306
331
|
throw new Error(`Blueprint "${slug}" not found in any state directory under ${blueprintRoot}`);
|
|
307
332
|
}
|
|
308
333
|
const { dir: currentDir, documentPath: currentDocumentPath, shape, state: currentState } = found;
|
|
309
|
-
// Guard: refuse to complete if any tasks are not done/dropped
|
|
334
|
+
// Guard: refuse to complete if any tasks are not done/dropped.
|
|
335
|
+
// Markdown `_overview.md` is SSOT for task status + verification evidence.
|
|
336
|
+
// The projection DB is derived and can lag when agents edit task Status
|
|
337
|
+
// blocks without going through `task complete` (hash-skipping reingest then
|
|
338
|
+
// leaves stale todo rows). Do not fail completion on projection lag —
|
|
339
|
+
// reingest after the markdown gate so consumers see fresh rows.
|
|
310
340
|
if (toState === "completed") {
|
|
311
341
|
const markdown = readFileSync(currentDocumentPath, "utf8");
|
|
312
342
|
const blueprint = parseBlueprint(markdown, slug);
|
|
@@ -319,23 +349,10 @@ async function promoteBlueprintLocked(cwd, slug, toState) {
|
|
|
319
349
|
throw new Error(`Cannot promote "${slug}" to completed: the following tasks are not done/dropped: ${list}`);
|
|
320
350
|
}
|
|
321
351
|
assertAllTasksHaveCanonicalPassingEvidence(markdown, blueprint.tasks.filter((task) => task.status === "done").map((task) => task.id));
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
try {
|
|
327
|
-
openTasks = conn.db
|
|
328
|
-
.prepare(`SELECT task_id, status FROM tasks WHERE blueprint_slug = ? AND status NOT IN ('done', 'dropped')`)
|
|
329
|
-
.all(slug);
|
|
330
|
-
}
|
|
331
|
-
finally {
|
|
332
|
-
conn.close();
|
|
333
|
-
}
|
|
334
|
-
if (openTasks.length > 0) {
|
|
335
|
-
const list = openTasks.map((t) => `${t.task_id} (${t.status})`).join(", ");
|
|
336
|
-
throw new Error(`Cannot promote "${slug}" to completed: the following tasks are not done: ${list}`);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
352
|
+
// Best-effort projection refresh. Clear content hash first so a lagging
|
|
353
|
+
// tasks table is rewritten even when a prior corrupt UPDATE left the
|
|
354
|
+
// stored hash equal to current markdown.
|
|
355
|
+
await forceReIngestBlueprint(cwd, slug, currentDocumentPath);
|
|
339
356
|
}
|
|
340
357
|
// Platform freshness must be resolved before trust validation so the proof
|
|
341
358
|
// applies to the current replica, not stale local markdown.
|
|
@@ -5,7 +5,9 @@ import { parseBlueprintForDb } from "#db/parser/blueprint-db-parser";
|
|
|
5
5
|
import { blueprintToSpecKit } from "#export/spec-kit/index";
|
|
6
6
|
import { getProjectRoot } from "#cli/utils";
|
|
7
7
|
import { commandExists as defaultCommandExists } from "#runtime/command-exists.js";
|
|
8
|
-
import { clearBlueprintWorktreeOwnership, ensureBlueprintOwnerWorktree, } from "#worktrees/manager.js";
|
|
8
|
+
import { adoptBlueprintOwnerWorktree, clearBlueprintWorktreeOwnership, ensureBlueprintOwnerWorktree, } from "#worktrees/manager.js";
|
|
9
|
+
import { resolveCurrentManagedWorktreeContext } from "#worktrees/current.js";
|
|
10
|
+
import { canonicalizeWorktreePath, WORKTREE_PROBE_TIMEOUT_MS } from "#worktrees/identity.js";
|
|
9
11
|
import { resolveBlueprintRoot } from "#utils/blueprint-root";
|
|
10
12
|
import { getBlueprintDocumentPaths } from "#utils/document-paths.js";
|
|
11
13
|
import { applyBlueprintLifecycleToFile, BlueprintCreationService, BlueprintService, complexitySchema, relativeBlueprintSlug, parseBlueprint, planStatusSchema, runBlueprintAudit, resolveBlueprintFile, serializeBlueprint, validateAllTasksDone, } from "#local";
|
|
@@ -392,7 +394,16 @@ export async function startBlueprint(slug, options = {}) {
|
|
|
392
394
|
if (source.blueprint.status !== "planned") {
|
|
393
395
|
throw new Error(`Blueprint ${source.slug} must be planned before start (current: ${source.blueprint.status}).`);
|
|
394
396
|
}
|
|
395
|
-
const
|
|
397
|
+
const relativeSlug = relativeBlueprintSlug(slug);
|
|
398
|
+
const currentManaged = resolveCurrentManagedWorktreeContext(projectRoot);
|
|
399
|
+
const canonicalProjectRoot = canonicalizeWorktreePath(projectRoot);
|
|
400
|
+
const currentBranch = readCurrentGitBranch(projectRoot);
|
|
401
|
+
const binding = currentManaged &&
|
|
402
|
+
currentManaged.kind === "owner" &&
|
|
403
|
+
currentManaged.worktreePath === canonicalProjectRoot &&
|
|
404
|
+
currentBranch === `bp/${relativeSlug}`
|
|
405
|
+
? adoptBlueprintOwnerWorktree(currentManaged.repoRoot, relativeSlug, currentManaged.worktreePath)
|
|
406
|
+
: ensureBlueprintOwnerWorktree(projectRoot, relativeSlug);
|
|
396
407
|
const result = await applyLifecycleMutation(slug, {
|
|
397
408
|
type: "start",
|
|
398
409
|
worktreeOwnerId: binding.id,
|
|
@@ -404,6 +415,20 @@ export async function startBlueprint(slug, options = {}) {
|
|
|
404
415
|
ownerWorktreePath: binding.path,
|
|
405
416
|
};
|
|
406
417
|
}
|
|
418
|
+
function readCurrentGitBranch(cwd) {
|
|
419
|
+
try {
|
|
420
|
+
const branch = execFileSync("git", ["branch", "--show-current"], {
|
|
421
|
+
cwd,
|
|
422
|
+
encoding: "utf8",
|
|
423
|
+
timeout: WORKTREE_PROBE_TIMEOUT_MS,
|
|
424
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
425
|
+
}).trim();
|
|
426
|
+
return branch.length > 0 ? branch : null;
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
407
432
|
export async function parkBlueprint(slug, options = {}) {
|
|
408
433
|
const projectRoot = resolveProjectRoot(options.projectRoot);
|
|
409
434
|
const result = await applyLifecycleMutation(slug, { type: "park" }, projectRoot);
|
|
@@ -2,12 +2,14 @@ import type { CAC } from "cac";
|
|
|
2
2
|
import type { SecretGateCommandOptions, SecretGateRunResult } from "#secret-gate/runner.js";
|
|
3
3
|
import { type CiActEventName, type CiActExecutionMode } from "#ci/act-runner.js";
|
|
4
4
|
export declare const DEFAULT_CI_ACT_TIMEOUT_MS: number;
|
|
5
|
+
export declare const SMOKE_CI_ACT_TIMEOUT_MS = 30000;
|
|
5
6
|
export declare const MAX_CI_ACT_TIMEOUT_MS: number;
|
|
6
7
|
export declare const CI_COMMAND_HELP: string;
|
|
7
8
|
export interface CiActOptions {
|
|
8
9
|
readonly workflow?: string;
|
|
9
10
|
readonly workflowPath?: string;
|
|
10
11
|
readonly job?: string;
|
|
12
|
+
readonly matrix?: readonly string[];
|
|
11
13
|
readonly eventName?: CiActEventName;
|
|
12
14
|
readonly eventPath?: string;
|
|
13
15
|
readonly envProfile?: string;
|
|
@@ -17,6 +19,7 @@ export interface CiActOptions {
|
|
|
17
19
|
readonly platformImage?: string;
|
|
18
20
|
readonly execute?: boolean;
|
|
19
21
|
readonly timeoutMs?: number;
|
|
22
|
+
readonly smoke?: boolean;
|
|
20
23
|
}
|
|
21
24
|
export interface CiCommandConfig {
|
|
22
25
|
readonly command: string;
|
|
@@ -32,5 +35,9 @@ export declare function registerCiCommand(cli: CAC): void;
|
|
|
32
35
|
export declare function buildCiActCommand(options?: CiActOptions, cwd?: string): CiCommandConfig;
|
|
33
36
|
export declare function validateCiActCommand(..._legacyArgs: readonly unknown[]): string | null;
|
|
34
37
|
export declare function runCiActCommand(options?: CiActOptions, deps?: CiCommandDeps): Promise<number>;
|
|
35
|
-
export declare function normalizeCiActTimeoutMs(value: number | undefined
|
|
38
|
+
export declare function normalizeCiActTimeoutMs(value: number | undefined, options?: {
|
|
39
|
+
readonly smoke?: boolean;
|
|
40
|
+
}): number;
|
|
36
41
|
export declare function parseCiActTimeoutMs(value: unknown): number | undefined;
|
|
42
|
+
export declare function parseCiActExecutionMode(value: unknown): CiActExecutionMode | undefined;
|
|
43
|
+
export declare function parseCiActEventName(value: unknown): CiActEventName | undefined;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { buildPublicCiActCommand, resolveCiActExecutionMode, sanitizePublicCiActArgv, } from "#ci/act-runner.js";
|
|
1
|
+
import { buildPublicCiActCommand, preparePublicCiActExecution, resolveCiActExecutionMode, resolveCiActSecretEnvProfile, sanitizePublicCiActArgv, shouldForceCiActSecretGate, } from "#ci/act-runner.js";
|
|
2
2
|
import { redactText } from "#mcp/tools/_shared/redact.js";
|
|
3
3
|
import { runSecretGateCommand } from "#secret-gate/runner.js";
|
|
4
4
|
export const DEFAULT_CI_ACT_TIMEOUT_MS = 20 * 60_000;
|
|
5
|
+
export const SMOKE_CI_ACT_TIMEOUT_MS = 30_000;
|
|
5
6
|
export const MAX_CI_ACT_TIMEOUT_MS = 60 * 60_000;
|
|
6
7
|
export const CI_COMMAND_HELP = [
|
|
7
8
|
"Run repository CI helpers through the portable, secret-safe wp surface.",
|
|
@@ -10,6 +11,8 @@ export const CI_COMMAND_HELP = [
|
|
|
10
11
|
"Examples:",
|
|
11
12
|
" wp ci act --workflow ci-e2e",
|
|
12
13
|
" wp ci act --workflow ci-e2e --execute",
|
|
14
|
+
" wp ci act --workflow ci-e2e --execute --smoke",
|
|
15
|
+
" wp ci act --workflow ci-e2e --job test --matrix target:beta",
|
|
13
16
|
" wp ci act --workflow-path .github/workflows/ci.yml --job test",
|
|
14
17
|
].join("\n");
|
|
15
18
|
export function registerCiCommand(cli) {
|
|
@@ -20,6 +23,7 @@ export function registerCiCommand(cli) {
|
|
|
20
23
|
})
|
|
21
24
|
.option("--workflow-path <path>", "Explicit workflow file path")
|
|
22
25
|
.option("--job <id>", "Workflow job id")
|
|
26
|
+
.option("--matrix <key:value>", "Run only matching act matrix selector(s) (repeatable)")
|
|
23
27
|
.option("--event-name <name>", "act event name: pull_request | push | workflow_dispatch")
|
|
24
28
|
.option("--event-path <path>", "Use an existing event JSON file")
|
|
25
29
|
.option("--env-profile <profile>", "Secret-gate runtime profile", { default: "secrets-only" })
|
|
@@ -28,6 +32,7 @@ export function registerCiCommand(cli) {
|
|
|
28
32
|
.option("--container-architecture <arch>", "act container architecture override")
|
|
29
33
|
.option("--platform-image <image>", "act runner image for ubicloud-standard-2")
|
|
30
34
|
.option("--timeout-ms <ms>", `act execution timeout in milliseconds (default: ${DEFAULT_CI_ACT_TIMEOUT_MS})`)
|
|
35
|
+
.option("--smoke", `Use a short ${SMOKE_CI_ACT_TIMEOUT_MS}ms timeout for targeted local proof`)
|
|
31
36
|
.option("--execute", "Run act; default is a redacted dry-run preview")
|
|
32
37
|
.option("--dry-run", "Print the resolved command without executing it")
|
|
33
38
|
.action((action, flags) => {
|
|
@@ -39,14 +44,16 @@ export function registerCiCommand(cli) {
|
|
|
39
44
|
workflow: flags.workflow,
|
|
40
45
|
workflowPath: flags.workflowPath,
|
|
41
46
|
job: flags.job,
|
|
42
|
-
|
|
47
|
+
matrix: parseCiActMatrixFlags(flags.matrix),
|
|
48
|
+
eventName: parseCiActEventName(flags.eventName),
|
|
43
49
|
envProfile: flags.envProfile,
|
|
44
50
|
secretProfile: flags.secretProfile,
|
|
45
|
-
mode: flags.mode,
|
|
51
|
+
mode: parseCiActExecutionMode(flags.mode),
|
|
46
52
|
containerArchitecture: flags.containerArchitecture,
|
|
47
53
|
platformImage: flags.platformImage,
|
|
48
54
|
eventPath: flags.eventPath,
|
|
49
55
|
execute: Boolean(flags.execute) && !flags.dryRun,
|
|
56
|
+
smoke: Boolean(flags.smoke),
|
|
50
57
|
timeoutMs: parseCiActTimeoutMs(flags.timeoutMs),
|
|
51
58
|
});
|
|
52
59
|
});
|
|
@@ -60,8 +67,8 @@ export function validateCiActCommand(..._legacyArgs) {
|
|
|
60
67
|
}
|
|
61
68
|
export async function runCiActCommand(options = {}, deps = {}) {
|
|
62
69
|
const cwd = deps.cwd ?? process.cwd();
|
|
63
|
-
const command = buildPublicCiActCommand({ ...options, cwd });
|
|
64
70
|
if (!options.execute) {
|
|
71
|
+
const command = buildPublicCiActCommand({ ...options, cwd });
|
|
65
72
|
const preview = sanitizePublicCiActArgv(command);
|
|
66
73
|
(deps.stdout ?? process.stdout).write(`${JSON.stringify({ command: preview.command, args: preview.args })}\n`);
|
|
67
74
|
return 0;
|
|
@@ -69,23 +76,34 @@ export async function runCiActCommand(options = {}, deps = {}) {
|
|
|
69
76
|
if (resolveCiActExecutionMode(options) === "replay") {
|
|
70
77
|
(deps.stderr ?? process.stderr).write("Warning: replay mode is a generated local approximation and is not security-equivalent to GitHub CI or OIDC.\n");
|
|
71
78
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
const prepared = preparePublicCiActExecution({ ...options, cwd });
|
|
80
|
+
try {
|
|
81
|
+
const secretProfile = resolveCiActSecretEnvProfile({ ...options, cwd }) ?? "preview";
|
|
82
|
+
const result = await (deps.run ?? runSecretGateCommand)({
|
|
83
|
+
cwd,
|
|
84
|
+
commandCwd: prepared.cwd,
|
|
85
|
+
sink: "act",
|
|
86
|
+
profile: secretProfile,
|
|
87
|
+
envProfile: options.envProfile ?? "secrets-only",
|
|
88
|
+
forceSecretGate: shouldForceCiActSecretGate(options.envProfile),
|
|
89
|
+
command: "act",
|
|
90
|
+
args: prepared.command.actArgs,
|
|
91
|
+
timeoutMs: normalizeCiActTimeoutMs(options.timeoutMs, { smoke: options.smoke }),
|
|
92
|
+
});
|
|
93
|
+
const stdout = redactText(result.stdout) ?? "";
|
|
94
|
+
const stderr = redactText(result.stderr) ?? "";
|
|
95
|
+
if (stdout)
|
|
96
|
+
(deps.stdout ?? process.stdout).write(stdout);
|
|
97
|
+
if (stderr)
|
|
98
|
+
(deps.stderr ?? process.stderr).write(stderr);
|
|
99
|
+
return result.exitCode;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
prepared.cleanup();
|
|
103
|
+
}
|
|
86
104
|
}
|
|
87
|
-
export function normalizeCiActTimeoutMs(value) {
|
|
88
|
-
const timeoutMs = value ?? DEFAULT_CI_ACT_TIMEOUT_MS;
|
|
105
|
+
export function normalizeCiActTimeoutMs(value, options = {}) {
|
|
106
|
+
const timeoutMs = value ?? (options.smoke ? SMOKE_CI_ACT_TIMEOUT_MS : DEFAULT_CI_ACT_TIMEOUT_MS);
|
|
89
107
|
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
|
|
90
108
|
throw new Error("--timeout-ms must be a positive integer");
|
|
91
109
|
}
|
|
@@ -100,7 +118,48 @@ export function parseCiActTimeoutMs(value) {
|
|
|
100
118
|
const parsed = typeof value === "number"
|
|
101
119
|
? value
|
|
102
120
|
: typeof value === "string"
|
|
103
|
-
?
|
|
121
|
+
? parseCiActTimeoutString(value)
|
|
104
122
|
: Number.NaN;
|
|
105
123
|
return normalizeCiActTimeoutMs(parsed);
|
|
106
124
|
}
|
|
125
|
+
export function parseCiActExecutionMode(value) {
|
|
126
|
+
if (value === undefined || value === null || value === "")
|
|
127
|
+
return undefined;
|
|
128
|
+
if (value === "direct" || value === "replay")
|
|
129
|
+
return value;
|
|
130
|
+
throw new Error('--mode must be "direct" or "replay"');
|
|
131
|
+
}
|
|
132
|
+
export function parseCiActEventName(value) {
|
|
133
|
+
if (value === undefined || value === null || value === "")
|
|
134
|
+
return undefined;
|
|
135
|
+
if (value === "pull_request" || value === "push" || value === "workflow_dispatch")
|
|
136
|
+
return value;
|
|
137
|
+
throw new Error('--event-name must be "pull_request", "push", or "workflow_dispatch"');
|
|
138
|
+
}
|
|
139
|
+
function parseCiActTimeoutString(value) {
|
|
140
|
+
const trimmed = value.trim();
|
|
141
|
+
if (!/^[1-9]\d*$/u.test(trimmed)) {
|
|
142
|
+
throw new Error("--timeout-ms must be a positive integer");
|
|
143
|
+
}
|
|
144
|
+
return Number(trimmed);
|
|
145
|
+
}
|
|
146
|
+
function parseCiActMatrixFlags(value) {
|
|
147
|
+
if (value === undefined)
|
|
148
|
+
return [];
|
|
149
|
+
if (typeof value === "string") {
|
|
150
|
+
if (value.trim().length === 0) {
|
|
151
|
+
throw new Error("--matrix must be provided as one or more key:value selectors");
|
|
152
|
+
}
|
|
153
|
+
return [value];
|
|
154
|
+
}
|
|
155
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
156
|
+
if (value.some((entry) => entry.trim().length === 0)) {
|
|
157
|
+
throw new Error("--matrix must be provided as one or more key:value selectors");
|
|
158
|
+
}
|
|
159
|
+
return [...value];
|
|
160
|
+
}
|
|
161
|
+
if (value === true) {
|
|
162
|
+
throw new Error("--matrix must be provided as one or more key:value selectors");
|
|
163
|
+
}
|
|
164
|
+
throw new Error("--matrix must be provided as one or more key:value selectors");
|
|
165
|
+
}
|
|
@@ -121,8 +121,8 @@ async function runMenuMode(cwd, launch) {
|
|
|
121
121
|
writeStartupFrame(label);
|
|
122
122
|
try {
|
|
123
123
|
const { runMenu } = await import("./menu.js");
|
|
124
|
-
//
|
|
125
|
-
// single menu at the
|
|
124
|
+
// Clear the startup ghost frame so the TUI (native Ratatui or pure-ANSI)
|
|
125
|
+
// paints a single menu at the home cursor instead of stacking below it.
|
|
126
126
|
if (process.stdout.isTTY)
|
|
127
127
|
process.stdout.write("\u001B[2J\u001B[3J\u001B[H");
|
|
128
128
|
const exitCode = await runMenu({
|
|
@@ -136,7 +136,7 @@ async function runMenuMode(cwd, launch) {
|
|
|
136
136
|
return exitCode;
|
|
137
137
|
}
|
|
138
138
|
catch (error) {
|
|
139
|
-
// The startup frame hid the cursor;
|
|
139
|
+
// The startup frame hid the cursor; restore it if the menu never paints.
|
|
140
140
|
if (process.stdout.isTTY)
|
|
141
141
|
process.stdout.write("\u001B[?25h");
|
|
142
142
|
throw error;
|
|
@@ -27,6 +27,7 @@ const MENU_BINDINGS = [
|
|
|
27
27
|
{ keys: "x", label: "actions", footerPriority: 0 },
|
|
28
28
|
{ keys: "⇥/^s", label: "sessions" },
|
|
29
29
|
{ keys: "b", label: "blueprints" },
|
|
30
|
+
{ keys: "c", label: "chat" },
|
|
30
31
|
{ keys: "d/^o", label: "mode" },
|
|
31
32
|
{ keys: "^p", label: "palette", footerPriority: 1 },
|
|
32
33
|
{ keys: "?/^x", label: "help" },
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type DashPermissionMode, type DashProvider } from "#cli/commands/dash/provider-capabilities.js";
|
|
2
|
+
import { type ControlPlaneChatMode } from "#cli/commands/dash/tui/chat-mode.js";
|
|
2
3
|
export type LaunchPreferences = {
|
|
3
4
|
readonly version: number;
|
|
4
5
|
readonly yolo: boolean;
|
|
6
|
+
readonly chatMode?: ControlPlaneChatMode;
|
|
5
7
|
};
|
|
6
8
|
export type YoloPreferenceState = {
|
|
7
9
|
readonly enabled: boolean;
|
|
@@ -9,9 +11,22 @@ export type YoloPreferenceState = {
|
|
|
9
11
|
/** The preference file exists but could not be parsed (fails safe to off). */
|
|
10
12
|
readonly corrupt: boolean;
|
|
11
13
|
};
|
|
14
|
+
export type ChatModePreferenceState = {
|
|
15
|
+
readonly mode: ControlPlaneChatMode;
|
|
16
|
+
readonly persisted: boolean;
|
|
17
|
+
/** The preference file exists but could not be parsed (fails safe to host-pane). */
|
|
18
|
+
readonly corrupt: boolean;
|
|
19
|
+
};
|
|
12
20
|
export declare function readYoloPreferenceState(): YoloPreferenceState;
|
|
13
21
|
export declare function readYoloPreference(): boolean;
|
|
14
22
|
export declare function writeYoloPreference(enabled: boolean): void;
|
|
23
|
+
/**
|
|
24
|
+
* Read persisted control-plane chat mode. Missing, corrupt, or invalid values
|
|
25
|
+
* fail safe to host-pane (never open integrated-chat chrome from bad prefs).
|
|
26
|
+
*/
|
|
27
|
+
export declare function readChatModePreferenceState(): ChatModePreferenceState;
|
|
28
|
+
export declare function readChatModePreference(): ControlPlaneChatMode;
|
|
29
|
+
export declare function writeChatModePreference(mode: ControlPlaneChatMode): void;
|
|
15
30
|
/**
|
|
16
31
|
* Precedence: explicit `--wp-permission-mode` > provider-native permission flags
|
|
17
32
|
* already in args > persisted yolo (true → 'dangerous') > 'safe'. When `provider`
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// User-scope persistence for
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// corrupt/missing read fails safe (yolo off).
|
|
1
|
+
// User-scope persistence for personal dash launch preferences (yolo + chat mode).
|
|
2
|
+
// Deliberately NOT the tracked .webpressorc.json — a repo file would let one
|
|
3
|
+
// contributor flip teammates into bypass via git pull. The file lives under the
|
|
4
|
+
// env-paths state root; corrupt/missing reads fail safe (yolo off, host-pane).
|
|
6
5
|
import { randomUUID } from "node:crypto";
|
|
7
6
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
8
7
|
import { dirname, join } from "node:path";
|
|
9
8
|
import { hasProviderNativePermissionFlag, parseDashPermissionMode, } from "#cli/commands/dash/provider-capabilities.js";
|
|
9
|
+
import { DEFAULT_CONTROL_PLANE_CHAT_MODE, isControlPlaneChatMode, } from "#cli/commands/dash/tui/chat-mode.js";
|
|
10
10
|
import { getStateRoot } from "#paths/state-root.js";
|
|
11
11
|
const LAUNCH_PREFERENCES_FILENAME = "launch-preferences.json";
|
|
12
12
|
const LAUNCH_PREFERENCES_VERSION = 1;
|
|
@@ -31,6 +31,23 @@ function readRawPreferences() {
|
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
/** Atomic read-merge-write so unknown fields survive partial updates. */
|
|
35
|
+
function writePreferencesPatch(patch) {
|
|
36
|
+
const path = launchPreferencesPath();
|
|
37
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
38
|
+
// A corrupt existing file is discarded (readRawPreferences → null).
|
|
39
|
+
const existing = readRawPreferences() ?? {};
|
|
40
|
+
const payload = { ...existing, version: LAUNCH_PREFERENCES_VERSION, ...patch };
|
|
41
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
42
|
+
try {
|
|
43
|
+
writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
44
|
+
renameSync(tmpPath, path);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
rmSync(tmpPath, { force: true });
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
34
51
|
export function readYoloPreferenceState() {
|
|
35
52
|
const path = launchPreferencesPath();
|
|
36
53
|
if (!existsSync(path))
|
|
@@ -44,21 +61,33 @@ export function readYoloPreference() {
|
|
|
44
61
|
return readYoloPreferenceState().enabled;
|
|
45
62
|
}
|
|
46
63
|
export function writeYoloPreference(enabled) {
|
|
64
|
+
writePreferencesPatch({ yolo: enabled });
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read persisted control-plane chat mode. Missing, corrupt, or invalid values
|
|
68
|
+
* fail safe to host-pane (never open integrated-chat chrome from bad prefs).
|
|
69
|
+
*/
|
|
70
|
+
export function readChatModePreferenceState() {
|
|
47
71
|
const path = launchPreferencesPath();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// wp's toggle. A corrupt existing file is discarded (readRawPreferences → null).
|
|
51
|
-
const existing = readRawPreferences() ?? {};
|
|
52
|
-
const payload = { ...existing, version: LAUNCH_PREFERENCES_VERSION, yolo: enabled };
|
|
53
|
-
const tmpPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
54
|
-
try {
|
|
55
|
-
writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
56
|
-
renameSync(tmpPath, path);
|
|
72
|
+
if (!existsSync(path)) {
|
|
73
|
+
return { mode: DEFAULT_CONTROL_PLANE_CHAT_MODE, persisted: false, corrupt: false };
|
|
57
74
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
const raw = readRawPreferences();
|
|
76
|
+
if (raw === null) {
|
|
77
|
+
return { mode: DEFAULT_CONTROL_PLANE_CHAT_MODE, persisted: false, corrupt: true };
|
|
61
78
|
}
|
|
79
|
+
const value = raw.chatMode;
|
|
80
|
+
if (typeof value === "string" && isControlPlaneChatMode(value)) {
|
|
81
|
+
return { mode: value, persisted: true, corrupt: false };
|
|
82
|
+
}
|
|
83
|
+
// File present but chatMode absent or invalid — not corrupt JSON.
|
|
84
|
+
return { mode: DEFAULT_CONTROL_PLANE_CHAT_MODE, persisted: true, corrupt: false };
|
|
85
|
+
}
|
|
86
|
+
export function readChatModePreference() {
|
|
87
|
+
return readChatModePreferenceState().mode;
|
|
88
|
+
}
|
|
89
|
+
export function writeChatModePreference(mode) {
|
|
90
|
+
writePreferencesPatch({ chatMode: mode });
|
|
62
91
|
}
|
|
63
92
|
/**
|
|
64
93
|
* Precedence: explicit `--wp-permission-mode` > provider-native permission flags
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-run fan-out attention projection — derives HUD/attention from
|
|
3
|
+
* registry-visible runs for a plan-once set. Does not introduce a parallel store.
|
|
4
|
+
*/
|
|
5
|
+
import { type AttentionItem, type AttentionSource } from "#cli/commands/dash/attention.js";
|
|
6
|
+
export type FanOutRunObservation = {
|
|
7
|
+
readonly runId: string;
|
|
8
|
+
readonly provider: string;
|
|
9
|
+
/** Registry / lifecycle state string (failed, running, starting, …). */
|
|
10
|
+
readonly state: string;
|
|
11
|
+
readonly error?: string | null;
|
|
12
|
+
readonly observedAt?: string;
|
|
13
|
+
readonly source?: Exclude<AttentionSource, "derived">;
|
|
14
|
+
};
|
|
15
|
+
export type FanOutAttentionProjection = {
|
|
16
|
+
readonly planId: string;
|
|
17
|
+
readonly total: number;
|
|
18
|
+
readonly failedCount: number;
|
|
19
|
+
readonly runningCount: number;
|
|
20
|
+
readonly items: readonly AttentionItem[];
|
|
21
|
+
/** One-line operator summary for attention strip. */
|
|
22
|
+
readonly summary: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Project multi-run fan-out state for attention/HUD consumption.
|
|
26
|
+
* Failed hosts are never dropped: they appear with inspect-failure action.
|
|
27
|
+
*/
|
|
28
|
+
export declare function projectFanOutAttention(input: {
|
|
29
|
+
readonly planId: string;
|
|
30
|
+
readonly runs: readonly FanOutRunObservation[];
|
|
31
|
+
readonly now?: string;
|
|
32
|
+
}): FanOutAttentionProjection;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-run fan-out attention projection — derives HUD/attention from
|
|
3
|
+
* registry-visible runs for a plan-once set. Does not introduce a parallel store.
|
|
4
|
+
*/
|
|
5
|
+
import { normalizeSessionState, projectAttention, } from "#cli/commands/dash/attention.js";
|
|
6
|
+
/**
|
|
7
|
+
* Project multi-run fan-out state for attention/HUD consumption.
|
|
8
|
+
* Failed hosts are never dropped: they appear with inspect-failure action.
|
|
9
|
+
*/
|
|
10
|
+
export function projectFanOutAttention(input) {
|
|
11
|
+
const now = input.now ?? new Date().toISOString();
|
|
12
|
+
const sessions = input.runs.map((run) => normalizeSessionState({
|
|
13
|
+
runId: run.runId,
|
|
14
|
+
provider: run.provider,
|
|
15
|
+
observations: [
|
|
16
|
+
{
|
|
17
|
+
source: run.source ?? "registry",
|
|
18
|
+
value: run.state,
|
|
19
|
+
observedAt: run.observedAt ?? now,
|
|
20
|
+
confidence: "high",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
}));
|
|
24
|
+
const signals = new Map(input.runs.flatMap((run) => {
|
|
25
|
+
if (run.state === "failed" || run.error) {
|
|
26
|
+
return [[run.runId, { failedVerification: true }]];
|
|
27
|
+
}
|
|
28
|
+
return [];
|
|
29
|
+
}));
|
|
30
|
+
const items = projectAttention(sessions, signals);
|
|
31
|
+
const failedCount = items.filter((item) => item.state === "failed").length;
|
|
32
|
+
const runningCount = items.filter((item) => item.state === "running" || item.state === "starting").length;
|
|
33
|
+
const failureBits = items
|
|
34
|
+
.filter((item) => item.state === "failed")
|
|
35
|
+
.map((item) => {
|
|
36
|
+
const err = input.runs.find((r) => r.runId === item.runId)?.error;
|
|
37
|
+
return err ? `${item.provider}:${err}` : `${item.provider}:failed`;
|
|
38
|
+
});
|
|
39
|
+
const summary = failedCount > 0
|
|
40
|
+
? `fan-out ${input.planId.slice(0, 8)} · ${runningCount}/${input.runs.length} ok · ${failedCount} failed · ${failureBits.join("; ")}`
|
|
41
|
+
: `fan-out ${input.planId.slice(0, 8)} · ${runningCount}/${input.runs.length} hosts · all ok`;
|
|
42
|
+
return {
|
|
43
|
+
planId: input.planId,
|
|
44
|
+
total: input.runs.length,
|
|
45
|
+
failedCount,
|
|
46
|
+
runningCount,
|
|
47
|
+
items,
|
|
48
|
+
summary,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared plan-once run label contract.
|
|
3
|
+
* Exact equality is SSOT for membership ↔ registry join; keep one helper.
|
|
4
|
+
*/
|
|
5
|
+
import type { PlanOnceHostEntry } from "#cli/commands/dash/plan-once-schema.js";
|
|
6
|
+
/** Durable run label: `plan-once:<planId>:<label|host>`. */
|
|
7
|
+
export declare function planOnceRunLabel(planId: string, host: PlanOnceHostEntry): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type MultiHostPlanProposal } from "#cli/commands/dash/plan-once-schema.js";
|
|
3
|
+
export declare const PLAN_MEMBERSHIP_SCHEMA_VERSION: 1;
|
|
4
|
+
export declare const planMembershipHostSchema: z.ZodObject<{
|
|
5
|
+
host: z.ZodEnum<{
|
|
6
|
+
claude: "claude";
|
|
7
|
+
codex: "codex";
|
|
8
|
+
grok: "grok";
|
|
9
|
+
opencode: "opencode";
|
|
10
|
+
}>;
|
|
11
|
+
workdir: z.ZodString;
|
|
12
|
+
label: z.ZodString;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
export type PlanMembershipHost = z.infer<typeof planMembershipHostSchema>;
|
|
15
|
+
export declare const planMembershipSchema: z.ZodObject<{
|
|
16
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
17
|
+
planId: z.ZodString;
|
|
18
|
+
hosts: z.ZodArray<z.ZodObject<{
|
|
19
|
+
host: z.ZodEnum<{
|
|
20
|
+
claude: "claude";
|
|
21
|
+
codex: "codex";
|
|
22
|
+
grok: "grok";
|
|
23
|
+
opencode: "opencode";
|
|
24
|
+
}>;
|
|
25
|
+
workdir: z.ZodString;
|
|
26
|
+
label: z.ZodString;
|
|
27
|
+
}, z.core.$strict>>;
|
|
28
|
+
confirmedAt: z.ZodString;
|
|
29
|
+
}, z.core.$strict>;
|
|
30
|
+
export type PlanMembership = z.infer<typeof planMembershipSchema>;
|
|
31
|
+
export declare const planMembershipStoreSchema: z.ZodObject<{
|
|
32
|
+
version: z.ZodLiteral<1>;
|
|
33
|
+
plans: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
34
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
35
|
+
planId: z.ZodString;
|
|
36
|
+
hosts: z.ZodArray<z.ZodObject<{
|
|
37
|
+
host: z.ZodEnum<{
|
|
38
|
+
claude: "claude";
|
|
39
|
+
codex: "codex";
|
|
40
|
+
grok: "grok";
|
|
41
|
+
opencode: "opencode";
|
|
42
|
+
}>;
|
|
43
|
+
workdir: z.ZodString;
|
|
44
|
+
label: z.ZodString;
|
|
45
|
+
}, z.core.$strict>>;
|
|
46
|
+
confirmedAt: z.ZodString;
|
|
47
|
+
}, z.core.$strict>>;
|
|
48
|
+
}, z.core.$strict>;
|
|
49
|
+
export type PlanMembershipStore = z.infer<typeof planMembershipStoreSchema>;
|
|
50
|
+
/** Pure: build immutable membership from a confirmed multi-host plan. */
|
|
51
|
+
export declare function buildPlanMembership(plan: MultiHostPlanProposal, confirmedAt?: string): PlanMembership;
|
|
52
|
+
/** Persist membership for a planId (idempotent overwrite for same planId). */
|
|
53
|
+
export declare function writePlanMembership(membership: PlanMembership, options?: {
|
|
54
|
+
readonly cwd?: string;
|
|
55
|
+
}): Promise<PlanMembership>;
|
|
56
|
+
/** Read membership for planId, or null if absent. */
|
|
57
|
+
export declare function readPlanMembership(planId: string, options?: {
|
|
58
|
+
readonly cwd?: string;
|
|
59
|
+
}): Promise<PlanMembership | null>;
|
|
60
|
+
/** List all durable plan memberships (operator / merge projection scan). */
|
|
61
|
+
export declare function listPlanMemberships(options?: {
|
|
62
|
+
readonly cwd?: string;
|
|
63
|
+
}): Promise<readonly PlanMembership[]>;
|
|
64
|
+
/** Test helper: clear membership store file content via rewrite empty. */
|
|
65
|
+
export declare function clearPlanMembershipStoreForTests(options: {
|
|
66
|
+
readonly cwd?: string;
|
|
67
|
+
}): Promise<void>;
|