Skip to content

Commit 7a1b407

Browse files
committed
fix(import): wire deploy next-step navigation and show dotfiles in file picker
Two TUI fixes for the import flow: 1. ImportFlow now accepts onNavigate prop so selecting "Deploy" from next steps navigates to the deploy screen instead of going back. 2. PathInput gains a showHidden prop; YamlPathScreen uses it so .bedrock_agentcore.yaml is visible in the file picker.
1 parent 4934747 commit 7a1b407

5 files changed

Lines changed: 56 additions & 25 deletions

File tree

src/cli/tui/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { LayoutProvider } from './context';
44
import { CLI_ONLY_EXAMPLES } from './copy';
55
import { MissingProjectMessage, WrongDirectoryMessage, getProjectRootMismatch, projectExists } from './guards';
66
import { AddFlow } from './screens/add/AddFlow';
7-
import { ImportFlow } from './screens/import';
87
import { CliOnlyScreen } from './screens/cli-only';
98
import { CreateScreen } from './screens/create';
109
import { DeployScreen } from './screens/deploy/DeployScreen';
1110
import { DevScreen } from './screens/dev/DevScreen';
1211
import { EvalHubScreen, EvalScreen } from './screens/eval';
1312
import { FetchAccessScreen } from './screens/fetch-access';
1413
import { HelpScreen, HomeScreen } from './screens/home';
14+
import { ImportFlow } from './screens/import';
1515
import { InvokeScreen } from './screens/invoke';
1616
import { OnlineEvalDashboard } from './screens/online-eval';
1717
import { PackageScreen } from './screens/package';
@@ -262,7 +262,12 @@ function AppContent() {
262262
}
263263

264264
if (route.name === 'import') {
265-
return <ImportFlow onBack={() => setRoute({ name: 'help' })} />;
265+
return (
266+
<ImportFlow
267+
onBack={() => setRoute({ name: 'help' })}
268+
onNavigate={command => setRoute({ name: command } as Route)}
269+
/>
270+
);
266271
}
267272

268273
if (route.name === 'update') {

src/cli/tui/components/PathInput.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ interface PathInputProps {
1717
maxVisibleItems?: number;
1818
/** Allow the final path segment to not exist (for create workflows). Parent directory must still exist. */
1919
allowCreate?: boolean;
20+
/** Show hidden files (dotfiles) in completions (default: false) */
21+
showHidden?: boolean;
2022
}
2123

2224
interface CompletionItem {
@@ -37,15 +39,15 @@ function parsePath(input: string, basePath: string): { dir: string; prefix: stri
3739
return { dir, prefix };
3840
}
3941

40-
function getCompletions(input: string, basePath: string, pathType: PathType): CompletionItem[] {
42+
function getCompletions(input: string, basePath: string, pathType: PathType, showHidden = false): CompletionItem[] {
4143
try {
4244
const { dir, prefix } = parsePath(input, basePath);
4345
const entries = readdirSync(dir, { withFileTypes: true });
4446

4547
const items = entries
4648
.filter(entry => {
4749
if (!entry.name.toLowerCase().startsWith(prefix.toLowerCase())) return false;
48-
if (entry.name.startsWith('.')) return false;
50+
if (!showHidden && entry.name.startsWith('.')) return false;
4951
if (pathType === 'directory' && !entry.isDirectory()) return false;
5052
return true;
5153
})
@@ -130,14 +132,15 @@ export function PathInput({
130132
pathType = 'file',
131133
maxVisibleItems = 8,
132134
allowCreate = false,
135+
showHidden = false,
133136
}: PathInputProps) {
134137
const [value, setValue] = useState(initialValue);
135138
const [cursor, setCursor] = useState(initialValue.length);
136139
const [selectedIndex, setSelectedIndex] = useState(0);
137140
const [error, setError] = useState<string | null>(null);
138141

139142
// Get live completions based on current value
140-
const matches = getCompletions(value, basePath, pathType);
143+
const matches = getCompletions(value, basePath, pathType, showHidden);
141144

142145
// Calculate viewport for scrolling
143146
const totalItems = matches.length;

src/cli/tui/components/__tests__/PathInput.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,23 @@ describe('PathInput', () => {
189189
expect(frame).not.toContain('.gitignore');
190190
});
191191

192+
it('shows dotfiles when showHidden is true', () => {
193+
mockReaddirSync.mockReturnValue([
194+
makeDirent('.hidden', true),
195+
makeDirent('.bedrock_agentcore.yaml', false),
196+
makeDirent('visible', true),
197+
]);
198+
199+
const { lastFrame } = render(
200+
<PathInput onSubmit={vi.fn()} onCancel={vi.fn()} basePath="/base" showHidden={true} />
201+
);
202+
203+
const frame = lastFrame()!;
204+
expect(frame).toContain('visible/');
205+
expect(frame).toContain('.hidden');
206+
expect(frame).toContain('.bedrock_agentcore.yaml');
207+
});
208+
192209
it('navigates dropdown with arrow keys', async () => {
193210
mockReaddirSync.mockReturnValue([makeDirent('alpha', true), makeDirent('beta', true), makeDirent('gamma', true)]);
194211

src/cli/tui/screens/import/ImportFlow.tsx

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type { ImportResult, ImportResourceResult } from '../../../commands/import/types';
1+
import type { ImportResourceResult, ImportResult } from '../../../commands/import/types';
2+
import { type NextStep, NextSteps } from '../../components/NextSteps';
3+
import { Panel } from '../../components/Panel';
24
import { ErrorPrompt } from '../../components/PromptScreen';
3-
import { NextSteps, type NextStep } from '../../components/NextSteps';
45
import { Screen } from '../../components/Screen';
5-
import { Panel } from '../../components/Panel';
66
import { HELP_TEXT } from '../../constants';
77
import { ArnInputScreen } from './ArnInputScreen';
88
import { CodePathScreen } from './CodePathScreen';
@@ -38,15 +38,16 @@ const IMPORT_NEXT_STEPS: NextStep[] = [
3838

3939
interface ImportFlowProps {
4040
onBack: () => void;
41+
onNavigate?: (command: string) => void;
4142
}
4243

43-
export function ImportFlow({ onBack }: ImportFlowProps) {
44+
export function ImportFlow({ onBack, onNavigate }: ImportFlowProps) {
4445
const [flow, setFlow] = useState<ImportFlowState>({ name: 'select-type' });
4546

4647
if (flow.name === 'select-type') {
4748
return (
4849
<ImportSelectScreen
49-
onSelect={(type) => {
50+
onSelect={type => {
5051
if (type === 'runtime' || type === 'memory') {
5152
setFlow({ name: 'arn-input', resourceType: type });
5253
} else {
@@ -62,7 +63,7 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
6263
return (
6364
<ArnInputScreen
6465
resourceType={flow.resourceType}
65-
onSubmit={(arn) => {
66+
onSubmit={arn => {
6667
if (flow.resourceType === 'runtime') {
6768
setFlow({ name: 'code-path', resourceType: 'runtime', arn });
6869
} else {
@@ -81,7 +82,7 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
8182
if (flow.name === 'code-path') {
8283
return (
8384
<CodePathScreen
84-
onSubmit={(codePath) => {
85+
onSubmit={codePath => {
8586
setFlow({
8687
name: 'importing',
8788
importType: 'runtime',
@@ -97,7 +98,7 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
9798
if (flow.name === 'yaml-path') {
9899
return (
99100
<YamlPathScreen
100-
onSubmit={(yamlPath) => {
101+
onSubmit={yamlPath => {
101102
setFlow({
102103
name: 'importing',
103104
importType: 'starter-toolkit',
@@ -116,10 +117,10 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
116117
arn={flow.arn}
117118
code={flow.code}
118119
yamlPath={flow.yamlPath}
119-
onSuccess={(result) => {
120+
onSuccess={result => {
120121
setFlow({ name: 'success', importType: flow.importType, result });
121122
}}
122-
onError={(message) => {
123+
onError={message => {
123124
setFlow({ name: 'error', message });
124125
}}
125126
onExit={onBack}
@@ -129,40 +130,39 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
129130

130131
if (flow.name === 'success') {
131132
const result = flow.result;
132-
const isResource = 'resourceType' in result;
133133

134134
return (
135135
<Screen title="Import Complete" onExit={onBack} helpText={HELP_TEXT.BACK}>
136136
<Panel>
137137
<Box flexDirection="column">
138138
<Text color="green">Import successful!</Text>
139-
{isResource && (
139+
{'resourceType' in result && (
140140
<Box flexDirection="column" marginTop={1}>
141141
<Text>
142142
<Text dimColor>Type: </Text>
143-
<Text>{(result as ImportResourceResult).resourceType}</Text>
143+
<Text>{result.resourceType}</Text>
144144
</Text>
145145
<Text>
146146
<Text dimColor>Name: </Text>
147-
<Text>{(result as ImportResourceResult).resourceName}</Text>
147+
<Text>{result.resourceName}</Text>
148148
</Text>
149-
{(result as ImportResourceResult).resourceId && (
149+
{result.resourceId && (
150150
<Text>
151151
<Text dimColor>ID: </Text>
152-
<Text>{(result as ImportResourceResult).resourceId}</Text>
152+
<Text>{result.resourceId}</Text>
153153
</Text>
154154
)}
155155
</Box>
156156
)}
157-
{!isResource && (
157+
{'importedAgents' in result && (
158158
<Box flexDirection="column" marginTop={1}>
159-
{(result as ImportResult).importedAgents?.map((agent) => (
159+
{result.importedAgents?.map(agent => (
160160
<Text key={agent}>
161161
<Text dimColor>Agent: </Text>
162162
<Text>{agent}</Text>
163163
</Text>
164164
))}
165-
{(result as ImportResult).importedMemories?.map((mem) => (
165+
{result.importedMemories?.map(mem => (
166166
<Text key={mem}>
167167
<Text dimColor>Memory: </Text>
168168
<Text>{mem}</Text>
@@ -172,7 +172,12 @@ export function ImportFlow({ onBack }: ImportFlowProps) {
172172
)}
173173
</Box>
174174
</Panel>
175-
<NextSteps steps={IMPORT_NEXT_STEPS} isInteractive={true} onSelect={() => onBack()} onBack={onBack} />
175+
<NextSteps
176+
steps={IMPORT_NEXT_STEPS}
177+
isInteractive={true}
178+
onSelect={step => (onNavigate ? onNavigate(step.command) : onBack())}
179+
onBack={onBack}
180+
/>
176181
</Screen>
177182
);
178183
}

src/cli/tui/screens/import/YamlPathScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function YamlPathScreen({ onSubmit, onExit }: YamlPathScreenProps) {
1616
<PathInput
1717
pathType="file"
1818
placeholder=".bedrock_agentcore.yaml"
19+
showHidden={true}
1920
onSubmit={onSubmit}
2021
onCancel={onExit}
2122
/>

0 commit comments

Comments
 (0)