/** @jsxImportSource react */ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import { render } from "ink"; import type { Plan } from "../../Plan.ts"; import { type PlanStatusSession, Cli } from "../Cli.ts"; import type { ApplyEvent } from "../Event.ts"; import { ApprovePlan } from "./components/ApprovePlan.tsx"; import { Plan as PlanComponent } from "./components/Plan.tsx"; import { PlanProgress } from "./components/PlanProgress.tsx"; export const inkCLI = () => Layer.succeed( Cli, Cli.of({ approvePlan, displayPlan, startApplySession, }), ); const approvePlan = Effect.fn(function*
(plan: P) {
let approved = false;
const { waitUntilExit } = render(
(plan: P): Effect.Effect (plan: P) {
// Print the plan preview once into scrollback before mounting the
// animated progress region (mirrors LoggingCli, which prints the plan
// lines at session start — `alchemy dev`/`--yes` runs never call
// displayPlan/approvePlan, so without this the TTY path shows no plan at
// all). Rendering + immediately unmounting leaves the frame behind and
// releases the Ink instance for PlanProgress; forwarded runtime logs then
// insert BETWEEN the preview and the live region instead of piling above
// the entire transcript.
yield* displayPlan(plan);
const listeners = new Set<(event: ApplyEvent) => void>();
const { unmount } = render(