Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/cli/tui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DatasetFlow } from './screens/dataset-hub';
import { DeployScreen } from './screens/deploy/DeployScreen';
import { EvalHubScreen, EvalScreen } from './screens/eval';
import { ExportHarnessFlow } from './screens/export';
import { FeedbackScreen } from './screens/feedback';
import { FetchAccessScreen } from './screens/fetch-access';
import { HelpScreen, HomeScreen } from './screens/home';
import { ImportFlow } from './screens/import';
Expand Down Expand Up @@ -80,6 +81,7 @@ type Route =
| { name: 'dataset' }
| { name: 'import' }
| { name: 'export-harness' }
| { name: 'feedback' }
| { name: 'cli-only'; commandId: string };

// Commands that don't require being at the project root
Expand Down Expand Up @@ -199,6 +201,8 @@ function AppContent({
return;
}
setRoute({ name: 'export-harness' });
} else if (id === 'feedback') {
setRoute({ name: 'feedback' });
}
};

Expand Down Expand Up @@ -493,6 +497,10 @@ function AppContent({
);
}

if (route.name === 'feedback') {
return <FeedbackScreen onExit={handleBack} />;
}

if (route.name === 'cli-only') {
const info = CLI_ONLY_EXAMPLES[route.commandId];
if (info) {
Expand Down
16 changes: 16 additions & 0 deletions src/cli/tui/__tests__/app-command-coverage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const ROUTED_COMMANDS = new Set([
'config-bundle',
'dataset',
'batch-evaluations',
'feedback',
]);

describe('TUI home screen command coverage', () => {
Expand All @@ -52,4 +53,19 @@ describe('TUI home screen command coverage', () => {

expect(unhandled).toEqual([]);
});

it('the cliOnly flag and CLI_ONLY_EXAMPLES agree for every visible command', () => {
const program = createProgram();
const commands = getCommandsForUI(program, { inProject: true });

// A command can never be in the main interactive list while routing to the
// cli-only dead-end: cliOnly is true iff selecting it hits CliOnlyScreen.
for (const cmd of commands) {
const hasCliOnlyExamples = cmd.id in CLI_ONLY_EXAMPLES;
expect(cmd.cliOnly, `${cmd.id}: cliOnly flag must match CLI_ONLY_EXAMPLES membership`).toBe(hasCliOnlyExamples);
}

// feedback is interactive (real FeedbackScreen), not a cli-only dead-end.
expect('feedback' in CLI_ONLY_EXAMPLES).toBe(false);
});
});
8 changes: 0 additions & 8 deletions src/cli/tui/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,8 @@ export const CLI_ONLY_EXAMPLES: Record<string, { description: string; examples:
'agentcore run insights -r MyAgent -i FailureAnalysis --wait',
],
},
feedback: {
description: 'Send feedback about the AgentCore CLI to the team.',
examples: ['agentcore feedback', 'agentcore feedback --screenshot'],
},
config: {
description: 'Adjust global configuration settings such as telemetry opt-out status.',
examples: ['agentcore config'],
},
insights: {
description: '[preview] View insights analysis jobs and results.',
examples: ['agentcore insights history', 'agentcore insights results --id <id>'],
},
};
11 changes: 8 additions & 3 deletions src/cli/tui/utils/commands.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { CLI_ONLY_EXAMPLES } from '../copy';
import type { Command } from '@commander-js/extra-typings';

export interface CommandMeta {
Expand All @@ -15,9 +16,13 @@ export interface CommandMeta {
const HIDDEN_FROM_TUI = ['help', 'telemetry', 'promote'] as const;

/**
* Commands that are CLI-only (shown but marked as requiring CLI invocation).
* Single source of truth for "this top-level command cannot run interactively in
* the TUI". Derived from CLI_ONLY_EXAMPLES so the `cliOnly` flag (list placement)
* can never diverge from App.tsx's dead-end routing: a command is flagged cliOnly
* iff selecting it routes to the CliOnlyScreen. Multi-word keys in CLI_ONLY_EXAMPLES
* (e.g. 'run eval') are subcommand paths, not top-level commands, so they never match.
*/
const CLI_ONLY_COMMANDS = ['traces', 'pause', 'resume', 'stop', 'archive'] as const;
const CLI_ONLY_COMMANDS = new Set(Object.keys(CLI_ONLY_EXAMPLES));

/**
* Commands hidden from TUI when inside an existing project.
Expand Down Expand Up @@ -53,6 +58,6 @@ export function getCommandsForUI(program: Command, options: GetCommandsOptions =
.filter(sub => !HIDDEN_SUBCOMMANDS.includes(sub.name() as (typeof HIDDEN_SUBCOMMANDS)[number]))
.map(sub => sub.name()),
disabled: false,
cliOnly: CLI_ONLY_COMMANDS.includes(cmd.name() as (typeof CLI_ONLY_COMMANDS)[number]),
cliOnly: CLI_ONLY_COMMANDS.has(cmd.name()),
}));
}
Loading