From 37e75dfd68a05bddb6cc551b74fbd2fe260740eb Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Fri, 4 Sep 2026 16:22:05 -0400 Subject: [PATCH 1/2] fix: show inputs on ENTER and add selected state styling --- src/components/HarnessWizard.tsx | 3 +- .../harness/create/create.screen.test.tsx | 2 + .../harness/update/update.screen.test.tsx | 30 +++++++++++++ .../project/create/create.screen.test.tsx | 35 +++++++++++++-- src/handlers/project/create/screen.tsx | 44 ++++++++++--------- 5 files changed, 88 insertions(+), 26 deletions(-) diff --git a/src/components/HarnessWizard.tsx b/src/components/HarnessWizard.tsx index df1cc549e..13efb026a 100644 --- a/src/components/HarnessWizard.tsx +++ b/src/components/HarnessWizard.tsx @@ -867,8 +867,9 @@ function MemoryStep({ helpText="how should the harness remember conversations?" options={MEMORY_OPTIONS} focusedIndex={index} + selectedIndex={editing ? index : undefined} /> - {value.kind === "byo" && ( + {editing && value.kind === "byo" && ( { await waitForText(r.lastFrame, "how should the harness remember conversations?"); await r.press("down"); // bring your own await waitForText(r.lastFrame, "● bring your own"); + expect(r.lastFrame()).not.toContain("Memory ARN"); await r.press("return"); // focus the memory arn field + await waitForText(r.lastFrame, "Memory ARN"); await r.press("return"); // empty → error await waitForText(r.lastFrame, "enter the ARN of an existing AgentCore Memory"); await r.write("arn:aws:bedrock-agentcore:us-east-1:123:memory/m-1"); diff --git a/src/handlers/harness/update/update.screen.test.tsx b/src/handlers/harness/update/update.screen.test.tsx index b8e793d51..40c851774 100644 --- a/src/handlers/harness/update/update.screen.test.tsx +++ b/src/handlers/harness/update/update.screen.test.tsx @@ -131,6 +131,36 @@ describe("harness update wizard", () => { r.unmount(); }); + test("reveals the current bring-your-own memory ARN only after enter", async () => { + const core = coreForUpdate(); + const current = currentHarness(); + const memoryArn = "arn:aws:bedrock-agentcore:us-east-1:123:memory/existing"; + current.harness!.memory = { + agentCoreMemoryConfiguration: { arn: memoryArn }, + }; + core.harness.setGetResponse(current); + const r = renderScreen("/agentcore/harness/update/MyHarness-abc123", { core }); + + await waitForText(r.lastFrame, "● keep current"); + await r.press("return"); + + await waitForText(r.lastFrame, "● bring your own"); + expect(r.lastFrame()).not.toContain("Memory ARN"); + expect(r.lastFrame()).not.toContain(memoryArn); + + await r.press("return"); + await waitForText(r.lastFrame, memoryArn); + + await r.press("escape"); + await waitFor(() => !(r.lastFrame() ?? "").includes(memoryArn)); + expect(r.lastFrame()).not.toContain("Memory ARN"); + expect(r.lastFrame()).toContain("● bring your own"); + + await r.press("return"); + await waitForText(r.lastFrame, memoryArn); + r.unmount(); + }); + test("changing the model submits a request with just that field", async () => { const core = coreForUpdate(); const r = renderScreen("/agentcore/harness/update/MyHarness-abc123", { core }); diff --git a/src/handlers/project/create/create.screen.test.tsx b/src/handlers/project/create/create.screen.test.tsx index 01de58af9..4dbb19094 100644 --- a/src/handlers/project/create/create.screen.test.tsx +++ b/src/handlers/project/create/create.screen.test.tsx @@ -81,15 +81,16 @@ describe("project create wizard", () => { await r.press("down"); // harness await r.press("return"); - // Model step: providers and the selected provider's fields share one page. + // Model step: fields stay hidden until the provider is confirmed. await waitForText(r.lastFrame, "choose a model"); expect(r.lastFrame()).toContain("● bedrock"); expect(r.lastFrame()).not.toContain("bedrock (recommended)"); expect(r.lastFrame()).toContain("○ openai"); expect(r.lastFrame()).toContain("○ gemini"); expect(r.lastFrame()).toContain("○ litellm"); - expect(r.lastFrame()).toContain(DEFAULT_MODEL_ID); + expect(r.lastFrame()).not.toContain(DEFAULT_MODEL_ID); await r.press("return"); // focus model id + await waitForText(r.lastFrame, DEFAULT_MODEL_ID); await r.press("return"); // accept model id // Review: the summary names the project, type, model, and directory. @@ -247,6 +248,32 @@ describe("project create wizard", () => { r.unmount(); }); + test("reveals model fields only after enter and hides them again on escape", async () => { + const r = renderScreen("/agentcore/project/create"); + + await waitForText(r.lastFrame, "name your project"); + await r.write("ModelApp"); + await r.press("return"); + await r.press("down"); // harness + await r.press("return"); + await waitForText(r.lastFrame, "choose a model"); + + await r.press("down"); // openai + await waitForText(r.lastFrame, "● openai"); + expect(r.lastFrame()).not.toContain("model ID"); + expect(r.lastFrame()).not.toContain("API key ARN"); + + await r.press("return"); + await waitForText(r.lastFrame, "model ID"); + expect(r.lastFrame()).toContain("API key ARN"); + + await r.press("escape"); + await waitFor(() => !(r.lastFrame() ?? "").includes("model ID")); + expect(r.lastFrame()).not.toContain("API key ARN"); + expect(r.lastFrame()).toContain("● openai"); + r.unmount(); + }); + test("the model picker remains readable in an 80x24 terminal", async () => { const r = renderScreen("/agentcore/project/create"); await r.resize(80, 24); @@ -268,8 +295,8 @@ describe("project create wizard", () => { expect(frame).toContain("○ openai"); expect(frame).toContain("○ gemini"); expect(frame).toContain("○ litellm"); - expect(frame).toContain("model ID"); - expect(frame).toContain(DEFAULT_MODEL_ID); + expect(frame).not.toContain("model ID"); + expect(frame).not.toContain(DEFAULT_MODEL_ID); expect(frame).toContain("[enter] continue"); expect(frame).toContain("[esc] back"); r.unmount(); diff --git a/src/handlers/project/create/screen.tsx b/src/handlers/project/create/screen.tsx index 3fd5370f6..1ba2469e4 100644 --- a/src/handlers/project/create/screen.tsx +++ b/src/handlers/project/create/screen.tsx @@ -672,28 +672,30 @@ function ModelStep({ helpText="the provider and model that will power the harness" options={options} focusedIndex={providerIndex} + selectedIndex={focusedField !== null ? providerIndex : undefined} /> - {fields.map((field, fieldIndex) => ( - { - onChange({ - ...value, - configs: { - ...value.configs, - [value.provider]: { ...config, [field.key]: next }, - }, - }); - setError(null); - }} - focused={focusedField === fieldIndex} - /> - ))} + {focusedField !== null && + fields.map((field, fieldIndex) => ( + { + onChange({ + ...value, + configs: { + ...value.configs, + [value.provider]: { ...config, [field.key]: next }, + }, + }); + setError(null); + }} + focused={focusedField === fieldIndex} + /> + ))} {error && ( {error} From b71bcc9056b7f605e52d9aba7b27b65046b9cad4 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Fri, 4 Sep 2026 16:40:55 -0400 Subject: [PATCH 2/2] chore: align FormTextArea styling with FormTextInput for consistency --- src/components/FormTextArea.tsx | 41 +++++++++---------- src/components/HarnessWizard.tsx | 10 +---- .../harness/create/create.screen.test.tsx | 6 +++ 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/components/FormTextArea.tsx b/src/components/FormTextArea.tsx index 14a816081..a7d55df1b 100644 --- a/src/components/FormTextArea.tsx +++ b/src/components/FormTextArea.tsx @@ -54,31 +54,30 @@ export function FormTextArea({ return ( - + {name} {helpText} - {hidden > 0 && … (+{hidden} earlier lines)} - {visible.length === 0 ? ( - - {placeholder} - - - ) : ( - visible.map((line, i) => ( - - {line} - {i === visible.length - 1 ? : null} + + {hidden > 0 && … (+{hidden} earlier lines)} + {visible.length === 0 ? ( + + {placeholder} + - )) - )} + ) : ( + visible.map((line, i) => ( + + {line} + {i === visible.length - 1 ? : null} + + )) + )} + ); } diff --git a/src/components/HarnessWizard.tsx b/src/components/HarnessWizard.tsx index 13efb026a..1026d8dca 100644 --- a/src/components/HarnessWizard.tsx +++ b/src/components/HarnessWizard.tsx @@ -1087,15 +1087,7 @@ function PromptStep({ onChange={onChange} /> {value !== "" && ( - - {`${value.length} chars · ctrl+d continues`} - + {`${value.length} chars · ctrl+d continues`} )} ); diff --git a/src/handlers/harness/create/create.screen.test.tsx b/src/handlers/harness/create/create.screen.test.tsx index ed1a15a05..8d73c8ce8 100644 --- a/src/handlers/harness/create/create.screen.test.tsx +++ b/src/handlers/harness/create/create.screen.test.tsx @@ -75,6 +75,12 @@ describe("harness create wizard", () => { // Step: prompt — type a prompt with a newline, then ctrl+d. await waitForText(r.lastFrame, "type or paste the agent's instructions"); + const promptFrame = r.lastFrame()!; + const promptHelpLine = promptFrame + .split("\n") + .findIndex((line) => line.includes("type or paste the agent's instructions")); + expect(promptFrame.split("\n")[promptHelpLine + 1]).toContain("╭"); + expect(promptFrame).toContain("╰"); await r.write("You are helpful."); await r.press("return"); // newline await r.write("Be brief.");