/** * Execution Plan Manager, tracks live progress for multi-step agent tasks. * * Plans are stored as JSON at .goodvibes/plans/.json and rendered as * markdown for injection into the model's context. Self-contained, no * orchestrator dependency. */ export type PlanItemStatus = 'pending' | 'in_progress' | 'complete' | 'failed' | 'skipped'; export interface PlanItem { id: string; phase: string; description: string; status: PlanItemStatus; agentId?: string; dependencies?: string[]; } export type ExecutionPlanStatus = 'draft' | 'active' | 'complete' | 'failed' | 'dismissed'; export interface ExecutionPlan { id: string; title: string; createdAt: string; updatedAt: string; sessionId?: string | undefined; status: ExecutionPlanStatus; items: PlanItem[]; specPath?: string; awaitingPlan?: boolean; /** ISO timestamp set when the plan was dismissed/archived (status 'dismissed'). */ dismissedAt?: string; /** The status the plan held immediately before it was dismissed, preserved so * a completed/failed record is not silently rewritten to look like a draft. */ dismissedFrom?: Exclude; } /** * Outcome of {@link ExecutionPlanManager.dismiss}. Dismiss is intentionally * refused mid-execution: an in-flight plan must be stopped via the workstream * cancel path first, so `dismiss` never silently abandons running agent work. * * - `no-active-plan` , nothing to dismiss; no mutation. * - `requires-cancel` , the active plan is mid-execution ('active'); * refused. Cancel the workstream first, then dismiss. * - `dismissed` , a proposal/awaiting-approval or terminal plan was * archived: retained on disk with status 'dismissed' * + `dismissedAt`, and the active pointer cleared so a * later `/plan ` starts fresh. */ export type DismissPlanOutcome = 'no-active-plan' | 'requires-cancel' | 'dismissed'; export interface DismissPlanResult { readonly outcome: DismissPlanOutcome; /** The archived plan (only when `outcome === 'dismissed'`). */ readonly plan?: ExecutionPlan; /** The plan that blocked dismissal (only when `outcome === 'requires-cancel'`). */ readonly blockedBy?: ExecutionPlan; } export interface ExecutionPlanParseIssue { readonly line: number; readonly text: string; readonly reason: string; } export type ParsedExecutionPlan = Partial & { parseIssues?: ExecutionPlanParseIssue[] | undefined; }; export declare class ExecutionPlanManager { private readonly projectRoot; private readonly plansDir; private readonly activeFile; private lastCreatedAtMs; constructor(projectRoot: string); /** Load plan from disk (.goodvibes/plans/.json). Returns null if not found. */ load(planId: string): ExecutionPlan | null; /** Save plan to disk. Creates directories as needed. */ save(plan: ExecutionPlan): void; /** Get the active plan for the current session, if any. */ getActive(sessionId?: string): ExecutionPlan | null; private setActive; /** Create a new plan and set it as active. */ create(title: string, items: Omit[], sessionId?: string): ExecutionPlan; /** Update a plan item's status (and optionally assign an agent). */ updateItem(planId: string, itemId: string, status: PlanItemStatus, agentId?: string): void; /** * Dismiss (archive) the active plan for the current session. * * Honest semantics for every plan state: * - No active plan → no-op (`{ outcome: 'no-active-plan' }`), nothing written. * - Active/mid-execution ('active') → refused (`{ outcome: 'requires-cancel' }`). * A running plan must be stopped via the workstream cancel path first; * dismiss never abandons in-flight agent work by fiat. * - Proposal/awaiting-approval ('draft') or a terminal record ('complete' / * 'failed') → archived: the plan is RETAINED on disk with status * 'dismissed', a `dismissedAt` timestamp, and `dismissedFrom` recording * the prior status; the active pointer is cleared so a later * `/plan ` starts a fresh plan. Nothing is deleted. */ dismiss(sessionId?: string): DismissPlanResult; /** List all plans (reads directory, excludes active.json). */ list(): ExecutionPlan[]; private nextCreatedAt; /** * Render plan as markdown for injection into model context. * * Format: * # Plan Title * ## Phase 1: Setup [COMPLETE] * - [x] Description, COMPLETE (agent-id) */ toMarkdown(plan: ExecutionPlan): string; /** * Parse a markdown execution plan written by the model into structured format. * Robust to minor formatting variations models may produce. */ parseFromMarkdown(markdown: string): ParsedExecutionPlan; /** Human-readable summary: "Phase 2: Implementation, 1/3 complete" */ getSummary(plan: ExecutionPlan): string; /** * Replace all items in a plan with new items. * Used when the model provides a detailed plan in response to /plan. * Dependencies expressed as description strings are resolved to item IDs. */ replaceItems(planId: string, items: Omit[]): void; /** Get next actionable items: dependencies met, status=pending. */ getNextItems(plan: ExecutionPlan): PlanItem[]; } //# sourceMappingURL=execution-plan.d.ts.map