From 3257b823cda52d07d3f5ea848584b19d1450e1dd Mon Sep 17 00:00:00 2001 From: chioarub Date: Tue, 9 Jun 2026 10:26:44 +0300 Subject: [PATCH 1/2] fix(typecheck): add wizard agent creation types --- .../new-agent-creation/CreateAgentWizard.tsx | 6 ++-- .../agents/new-agent-creation/types.ts | 27 ++++++++++++++++ .../wizard-steps/ColorStep.tsx | 10 +++--- .../wizard-steps/ConfirmStep.tsx | 7 +++-- .../wizard-steps/DescriptionStep.tsx | 4 +-- .../wizard-steps/LocationStep.tsx | 2 +- .../wizard-steps/MemoryStep.tsx | 4 +-- .../wizard-steps/MethodStep.tsx | 2 +- .../wizard-steps/ModelStep.tsx | 2 +- .../wizard-steps/PromptStep.tsx | 4 +-- .../wizard-steps/ToolsStep.tsx | 4 +-- .../wizard-steps/TypeStep.tsx | 6 ++-- src/components/wizard/WizardProvider.tsx | 8 ++--- src/components/wizard/types.ts | 31 +++++++++++++++++++ 14 files changed, 89 insertions(+), 28 deletions(-) create mode 100644 src/components/agents/new-agent-creation/types.ts create mode 100644 src/components/wizard/types.ts diff --git a/src/components/agents/new-agent-creation/CreateAgentWizard.tsx b/src/components/agents/new-agent-creation/CreateAgentWizard.tsx index 779deb50fb..8fd1e6330d 100644 --- a/src/components/agents/new-agent-creation/CreateAgentWizard.tsx +++ b/src/components/agents/new-agent-creation/CreateAgentWizard.tsx @@ -23,7 +23,7 @@ type Props = { onComplete: (message: string) => void; onCancel: () => void; }; -export function CreateAgentWizard(t0) { +export function CreateAgentWizard(t0: Props): React.ReactNode { const $ = _c(17); const { tools, @@ -77,14 +77,14 @@ export function CreateAgentWizard(t0) { const steps = t5; let t6; if ($[13] === Symbol.for("react.memo_cache_sentinel")) { - t6 = {}; + t6 = {} as AgentWizardData; $[13] = t6; } else { t6 = $[13]; } let t7; if ($[14] !== onCancel || $[15] !== steps) { - t7 = ; + t7 = steps={steps} initialData={t6} onComplete={_temp} onCancel={onCancel} title="Create new agent" showStepCounter={false} />; $[14] = onCancel; $[15] = steps; $[16] = t7; diff --git a/src/components/agents/new-agent-creation/types.ts b/src/components/agents/new-agent-creation/types.ts new file mode 100644 index 0000000000..fd9c48873c --- /dev/null +++ b/src/components/agents/new-agent-creation/types.ts @@ -0,0 +1,27 @@ +import type { AgentColorName } from '../../../tools/AgentTool/agentColorManager.js' +import type { AgentMemoryScope } from '../../../tools/AgentTool/agentMemory.js' +import type { CustomAgentDefinition } from '../../../tools/AgentTool/loadAgentsDir.js' +import type { SettingSource } from '../../../utils/settings/constants.js' + +export type GeneratedAgentDraft = { + identifier: string + whenToUse: string + systemPrompt: string +} + +export type AgentWizardData = { + location?: SettingSource + method?: 'generate' | 'manual' + generationPrompt?: string + isGenerating?: boolean + generatedAgent?: GeneratedAgentDraft + wasGenerated?: boolean + agentType?: string + systemPrompt?: string + whenToUse?: string + selectedTools?: string[] + selectedModel?: string + selectedColor?: AgentColorName + selectedMemory?: AgentMemoryScope + finalAgent?: CustomAgentDefinition +} diff --git a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx index 7ec7295840..049622b633 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx @@ -17,7 +17,7 @@ export function ColorStep() { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { @@ -34,9 +34,9 @@ export function ColorStep() { updateWizardData({ selectedColor: color, finalAgent: { - agentType: wizardData.agentType, - whenToUse: wizardData.whenToUse, - getSystemPrompt: () => wizardData.systemPrompt, + agentType: wizardData.agentType!, + whenToUse: wizardData.whenToUse!, + getSystemPrompt: () => wizardData.systemPrompt!, tools: wizardData.selectedTools, ...(wizardData.selectedModel ? { model: wizardData.selectedModel @@ -44,7 +44,7 @@ export function ColorStep() { ...(color ? { color: color as AgentColorName } : {}), - source: wizardData.location + source: wizardData.location! } }); goNext(); diff --git a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx index c57247d38c..95abdd12dd 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx @@ -25,7 +25,7 @@ type Props = { onSaveAndEdit: () => void; error?: string | null; }; -export function ConfirmStep(t0) { +export function ConfirmStep(t0: Props): React.ReactNode { const $ = _c(88); const { tools, @@ -37,7 +37,7 @@ export function ConfirmStep(t0) { const { goBack, wizardData - } = useWizard(); + } = useWizard(); let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = { @@ -69,6 +69,9 @@ export function ConfirmStep(t0) { } const handleKeyDown = t2; const agent = wizardData.finalAgent; + if (!agent || !wizardData.location) { + return null; + } let T0; let T1; let t10; diff --git a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx index f5a92f9812..271975c77f 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx @@ -18,10 +18,10 @@ export function DescriptionStep() { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); const [whenToUse, setWhenToUse] = useState(wizardData.whenToUse || ""); const [cursorOffset, setCursorOffset] = useState(whenToUse.length); - const [error, setError] = useState(null); + const [error, setError] = useState(null); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { diff --git a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx index 4740a6468b..fb5b303fc3 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx @@ -17,7 +17,7 @@ export function LocationStep() { goNext, updateWizardData, cancel - } = useWizard(); + } = useWizard(); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { diff --git a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx index 540bffe4ad..c886c34bce 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx @@ -22,7 +22,7 @@ export function MemoryStep() { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { @@ -77,7 +77,7 @@ export function MemoryStep() { finalAgent: wizardData.finalAgent ? { ...wizardData.finalAgent, memory, - getSystemPrompt: isAutoMemoryEnabled() && memory && agentType ? () => wizardData.systemPrompt + "\n\n" + loadAgentMemoryPrompt(agentType, memory) : () => wizardData.systemPrompt + getSystemPrompt: isAutoMemoryEnabled() && memory && agentType ? () => wizardData.systemPrompt! + "\n\n" + loadAgentMemoryPrompt(agentType, memory) : () => wizardData.systemPrompt! } : undefined }); goNext(); diff --git a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx index 25ae3ee5bb..1389636098 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx @@ -16,7 +16,7 @@ export function MethodStep() { goBack, updateWizardData, goToStep - } = useWizard(); + } = useWizard(); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = [{ diff --git a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx index ea744e973d..e747f82665 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ModelStep.tsx @@ -14,7 +14,7 @@ export function ModelStep() { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); let t0; if ($[0] !== goNext || $[1] !== updateWizardData) { t0 = model => { diff --git a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx index be6d44bb08..e80320674a 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/PromptStep.tsx @@ -17,10 +17,10 @@ export function PromptStep() { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); const [systemPrompt, setSystemPrompt] = useState(wizardData.systemPrompt || ""); const [cursorOffset, setCursorOffset] = useState(systemPrompt.length); - const [error, setError] = useState(null); + const [error, setError] = useState(null); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { diff --git a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx index 60a58c42d3..bc60cf0361 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx @@ -11,7 +11,7 @@ import type { AgentWizardData } from '../types.js'; type Props = { tools: Tools; }; -export function ToolsStep(t0) { +export function ToolsStep(t0: Props): React.ReactNode { const $ = _c(9); const { tools @@ -21,7 +21,7 @@ export function ToolsStep(t0) { goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); let t1; if ($[0] !== goNext || $[1] !== updateWizardData) { t1 = selectedTools => { diff --git a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx index 358869d647..db63d6b54a 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/TypeStep.tsx @@ -14,16 +14,16 @@ import type { AgentWizardData } from '../types.js'; type Props = { existingAgents: AgentDefinition[]; }; -export function TypeStep(_props) { +export function TypeStep(_props: Props): React.ReactNode { const $ = _c(15); const { goNext, goBack, updateWizardData, wizardData - } = useWizard(); + } = useWizard(); const [agentType, setAgentType] = useState(wizardData.agentType || ""); - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [cursorOffset, setCursorOffset] = useState(agentType.length); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { diff --git a/src/components/wizard/WizardProvider.tsx b/src/components/wizard/WizardProvider.tsx index c5767918e7..5155d83def 100644 --- a/src/components/wizard/WizardProvider.tsx +++ b/src/components/wizard/WizardProvider.tsx @@ -6,7 +6,7 @@ import type { WizardContextValue, WizardProviderProps } from './types.js'; // Use any here for the context since it will be cast properly when used // eslint-disable-next-line @typescript-eslint/no-explicit-any export const WizardContext = createContext | null>(null); -export function WizardProvider(t0) { +export function WizardProvider = Record>(t0: WizardProviderProps): React.ReactNode { const $ = _c(38); const { steps, @@ -37,7 +37,7 @@ export function WizardProvider(t0) { } else { t4 = $[2]; } - const [navigationHistory, setNavigationHistory] = useState(t4); + const [navigationHistory, setNavigationHistory] = useState(t4); useExitOnCtrlCDWithKeybindings(); let t5; let t6; @@ -108,7 +108,7 @@ export function WizardProvider(t0) { const goBack = t8; let t9; if ($[16] !== currentStepIndex || $[17] !== steps.length) { - t9 = index => { + t9 = (index: number) => { if (index >= 0 && index < steps.length) { setNavigationHistory(prev_3 => [...prev_3, currentStepIndex]); setCurrentStepIndex(index); @@ -137,7 +137,7 @@ export function WizardProvider(t0) { const cancel = t10; let t11; if ($[21] === Symbol.for("react.memo_cache_sentinel")) { - t11 = updates => { + t11 = (updates: Partial) => { setWizardData(prev_4 => ({ ...prev_4, ...updates diff --git a/src/components/wizard/types.ts b/src/components/wizard/types.ts new file mode 100644 index 0000000000..924c4a9682 --- /dev/null +++ b/src/components/wizard/types.ts @@ -0,0 +1,31 @@ +import type React from 'react' + +export type WizardStepComponent = React.ComponentType + +export type WizardProviderProps< + T extends Record = Record, +> = { + steps: WizardStepComponent[] + initialData?: T + onComplete: (data: T) => void + onCancel?: () => void + children?: React.ReactNode + title?: string + showStepCounter?: boolean +} + +export type WizardContextValue< + T extends Record = Record, +> = { + currentStepIndex: number + totalSteps: number + wizardData: T + setWizardData: React.Dispatch> + updateWizardData: (updates: Partial) => void + goNext: () => void + goBack: () => void + goToStep: (index: number) => void + cancel: () => void + title?: string + showStepCounter: boolean +} From fe33e71b51ecf20325ab45f854782d18a1d444e9 Mon Sep 17 00:00:00 2001 From: chioarub Date: Tue, 9 Jun 2026 11:57:49 +0300 Subject: [PATCH 2/2] fix(typecheck): guard agent wizard state --- .../wizard-steps/ColorStep.tsx | 12 +- .../wizard-steps/ConfirmStep.tsx | 2 +- .../wizard-steps/MemoryStep.tsx | 3 +- .../wizard-steps/wizardSteps.test.tsx | 254 ++++++++++++++++++ 4 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx diff --git a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx index 049622b633..195bcf68ea 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx @@ -31,12 +31,16 @@ export function ColorStep() { let t1; if ($[1] !== goNext || $[2] !== updateWizardData || $[3] !== wizardData.agentType || $[4] !== wizardData.location || $[5] !== wizardData.selectedModel || $[6] !== wizardData.selectedTools || $[7] !== wizardData.systemPrompt || $[8] !== wizardData.whenToUse) { t1 = color => { + const { agentType, location, systemPrompt, whenToUse } = wizardData; + if (!agentType || !whenToUse || !systemPrompt || !location) { + return; + } updateWizardData({ selectedColor: color, finalAgent: { - agentType: wizardData.agentType!, - whenToUse: wizardData.whenToUse!, - getSystemPrompt: () => wizardData.systemPrompt!, + agentType, + whenToUse, + getSystemPrompt: () => systemPrompt, tools: wizardData.selectedTools, ...(wizardData.selectedModel ? { model: wizardData.selectedModel @@ -44,7 +48,7 @@ export function ColorStep() { ...(color ? { color: color as AgentColorName } : {}), - source: wizardData.location! + source: location } }); goNext(); diff --git a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx index 95abdd12dd..642fb34ca6 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx @@ -70,7 +70,7 @@ export function ConfirmStep(t0: Props): React.ReactNode { const handleKeyDown = t2; const agent = wizardData.finalAgent; if (!agent || !wizardData.location) { - return null; + return }>Agent draft is incomplete.; } let T0; let T1; diff --git a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx index c886c34bce..923e011839 100644 --- a/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx +++ b/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx @@ -72,12 +72,13 @@ export function MemoryStep() { t2 = value => { const memory = value === "none" ? undefined : value as AgentMemoryScope; const agentType = wizardData.finalAgent?.agentType; + const systemPrompt = wizardData.systemPrompt ?? ""; updateWizardData({ selectedMemory: memory, finalAgent: wizardData.finalAgent ? { ...wizardData.finalAgent, memory, - getSystemPrompt: isAutoMemoryEnabled() && memory && agentType ? () => wizardData.systemPrompt! + "\n\n" + loadAgentMemoryPrompt(agentType, memory) : () => wizardData.systemPrompt! + getSystemPrompt: isAutoMemoryEnabled() && memory && agentType ? () => systemPrompt + "\n\n" + loadAgentMemoryPrompt(agentType, memory) : () => systemPrompt } : undefined }); goNext(); diff --git a/src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx b/src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx new file mode 100644 index 0000000000..889ea6dd6d --- /dev/null +++ b/src/components/agents/new-agent-creation/wizard-steps/wizardSteps.test.tsx @@ -0,0 +1,254 @@ +import { PassThrough } from 'node:stream' + +import { afterEach, beforeEach, expect, mock, test } from 'bun:test' +import React, { useEffect } from 'react' +import stripAnsi from 'strip-ansi' + +import { Box, createRoot, Text } from '../../../../ink.js' +import { WizardProvider, useWizard } from '../../../wizard/index.js' +import type { AgentWizardData } from '../types.js' + +type ColorPickerProps = { + onConfirm: (color: string | undefined) => void +} + +type SelectProps = { + onChange?: (value: string) => void +} + +type WizardSnapshot = { + currentStepIndex: number + wizardData: AgentWizardData +} + +const SYNC_START = '\x1B[?2026h' +const SYNC_END = '\x1B[?2026l' + +let latestColorPickerProps: ColorPickerProps | undefined +let latestSelectProps: SelectProps | undefined + +function extractLastFrame(output: string): string { + let lastFrame: string | null = null + let cursor = 0 + + while (cursor < output.length) { + const start = output.indexOf(SYNC_START, cursor) + if (start === -1) break + const contentStart = start + SYNC_START.length + const end = output.indexOf(SYNC_END, contentStart) + if (end === -1) break + const frame = output.slice(contentStart, end) + if (frame.trim().length > 0) lastFrame = frame + cursor = end + SYNC_END.length + } + + return lastFrame ?? output +} + +function createTestStreams() { + let output = '' + const stdout = new PassThrough() + const stdin = new PassThrough() as PassThrough & { + isTTY: boolean + setRawMode: () => void + ref: () => void + unref: () => void + } + stdin.isTTY = true + stdin.setRawMode = () => {} + stdin.ref = () => {} + stdin.unref = () => {} + ;(stdout as unknown as { columns: number }).columns = 120 + stdout.on('data', chunk => { + output += chunk.toString() + }) + return { stdout, stdin, getOutput: () => output } +} + +async function waitFor( + getValue: () => T | undefined, + message: string, +): Promise { + const startedAt = Date.now() + + while (Date.now() - startedAt < 2500) { + const value = getValue() + if (value !== undefined) return value + await Bun.sleep(10) + } + + throw new Error(message) +} + +function StateProbe({ + onSnapshot, +}: { + onSnapshot: (snapshot: WizardSnapshot) => void +}) { + const { currentStepIndex, wizardData } = useWizard() + + useEffect(() => { + onSnapshot({ currentStepIndex, wizardData }) + }, [currentStepIndex, onSnapshot, wizardData]) + + return null +} + +async function renderWizardChild( + child: React.ReactNode, + initialData: AgentWizardData, +) { + const { stdout, stdin, getOutput } = createTestStreams() + const root = await createRoot({ + stdout: stdout as unknown as NodeJS.WriteStream, + stdin: stdin as unknown as NodeJS.ReadStream, + patchConsole: false, + }) + let snapshot: WizardSnapshot | undefined + + root.render( + + steps={[_tempStep, _tempStep]} + initialData={initialData} + onComplete={() => {}} + > + + {child} + { + snapshot = nextSnapshot + }} + /> + + , + ) + + await waitFor(() => snapshot, 'Timed out waiting for wizard snapshot') + + return { + root, + stdin, + stdout, + getOutput, + getSnapshot: () => snapshot, + } +} + +beforeEach(() => { + latestColorPickerProps = undefined + latestSelectProps = undefined + mock.module('../../ColorPicker.js', () => ({ + ColorPicker: (props: ColorPickerProps) => { + latestColorPickerProps = props + return Color picker test double + }, + })) + mock.module('../../../CustomSelect/select.js', () => ({ + Select: (props: SelectProps) => { + latestSelectProps = props + return Memory select test double + }, + })) +}) + +afterEach(() => { + try { + mock.restore() + } finally { + latestColorPickerProps = undefined + latestSelectProps = undefined + } +}) + +test('ColorStep does not create a final agent when required wizard data is missing', async () => { + const { ColorStep } = + await importFreshStep('./ColorStep.js') + const harness = await renderWizardChild(, { + agentType: 'reviewer', + whenToUse: 'Use for reviews', + systemPrompt: 'Review carefully', + }) + + try { + const colorPickerProps = await waitFor( + () => latestColorPickerProps, + 'Timed out waiting for color picker props', + ) + + colorPickerProps.onConfirm(undefined) + await Bun.sleep(25) + + const snapshot = harness.getSnapshot() + expect(snapshot?.currentStepIndex).toBe(0) + expect(snapshot?.wizardData.finalAgent).toBeUndefined() + } finally { + harness.root.unmount() + harness.stdin.end() + harness.stdout.end() + } +}) + +test('MemoryStep falls back to an empty system prompt when wizard data is incomplete', async () => { + const { MemoryStep } = + await importFreshStep('./MemoryStep.js') + const harness = await renderWizardChild(, { + finalAgent: { + agentType: 'reviewer', + whenToUse: 'Use for reviews', + getSystemPrompt: () => 'original prompt', + source: 'userSettings', + }, + }) + + try { + const selectProps = await waitFor( + () => latestSelectProps, + 'Timed out waiting for memory select props', + ) + + selectProps.onChange?.('none') + await Bun.sleep(25) + + const finalAgent = harness.getSnapshot()?.wizardData.finalAgent + expect(finalAgent?.getSystemPrompt()).toBe('') + } finally { + harness.root.unmount() + harness.stdin.end() + harness.stdout.end() + } +}) + +test('ConfirmStep renders a fallback instead of a blank screen for incomplete wizard data', async () => { + const { ConfirmStep } = + await importFreshStep('./ConfirmStep.js') + const harness = await renderWizardChild( + {}} + onSaveAndEdit={() => {}} + />, + {}, + ) + + try { + await waitFor(() => { + const frame = stripAnsi(extractLastFrame(harness.getOutput())) + return frame.includes('Agent draft is incomplete') ? frame : undefined + }, 'Timed out waiting for incomplete agent draft fallback') + } finally { + harness.root.unmount() + harness.stdin.end() + harness.stdout.end() + } +}) + +function _tempStep() { + return null +} + +async function importFreshStep(specifier: string): Promise { + return import( + `${specifier}?wizard-steps-test=${Date.now()}-${Math.random()}` + ) as Promise +}