Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ bun.lock

.agentreview
ProtocolTesting/

# Git worktrees
.worktrees/
27 changes: 25 additions & 2 deletions src/cli/tui/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { getWorkingDirectory } from '../../lib';
import { createProgram } from '../cli';
import { LayoutProvider } from './context';
import { CLI_ONLY_EXAMPLES } from './copy';
import { MissingProjectMessage, WrongDirectoryMessage, getProjectRootMismatch, projectExists } from './guards';
import { AddFlow } from './screens/add/AddFlow';
import { CliOnlyScreen } from './screens/cli-only';
import { CreateScreen } from './screens/create';
import { DeployScreen } from './screens/deploy/DeployScreen';
import { DevScreen } from './screens/dev/DevScreen';
Expand Down Expand Up @@ -42,7 +44,8 @@ type Route =
| { name: 'fetch-access' }
| { name: 'validate' }
| { name: 'package' }
| { name: 'update' };
| { name: 'update' }
| { name: 'cli-only'; commandId: string };

// Commands that don't require being at the project root
const PROJECT_ROOT_EXEMPT_COMMANDS = new Set(['create', 'update']);
Expand Down Expand Up @@ -74,6 +77,13 @@ function AppContent() {
return;
}

// CLI-only commands → show usage info screen
const cliOnlyExamples = CLI_ONLY_EXAMPLES[id];
if (cliOnlyExamples) {
setRoute({ name: 'cli-only', commandId: id });
return;
}

if (id === 'dev') {
setRoute({ name: 'dev' });
} else if (id === 'deploy') {
Expand Down Expand Up @@ -247,7 +257,20 @@ function AppContent() {
return <UpdateScreen isInteractive={true} onExit={() => setRoute({ name: 'help' })} />;
}

// All visible commands are handled above; this is unreachable.
if (route.name === 'cli-only') {
const info = CLI_ONLY_EXAMPLES[route.commandId];
if (info) {
return (
<CliOnlyScreen
title={route.commandId}
description={info.description}
examples={info.examples}
onExit={() => setRoute({ name: 'help' })}
/>
);
}
}

return null;
}

Expand Down
34 changes: 34 additions & 0 deletions src/cli/tui/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
export const HINTS = {
HOME: 'Type to search, Tab commands, Esc quit',
COMMANDS: 'Type to filter, ↑↓ navigate, Enter select, Esc back',
COMMANDS_SHOW_ALL: 'Type to filter · ↑↓ Enter select · / show all · Esc back',
COMMANDS_HIDE_CLI: 'Type to filter · ↑↓ Enter select · / hide cli · Esc back',
} as const;

/**
Expand Down Expand Up @@ -48,3 +50,35 @@ export const COMMAND_DESCRIPTIONS = {
update: 'Check for and install CLI updates',
validate: 'Validate agentcore/ config files.',
} as const;

/**
* CLI-only command examples and usage information.
* These commands must run in the terminal, not in the TUI.
*/
export const CLI_ONLY_EXAMPLES: Record<string, { description: string; examples: string[] }> = {
logs: {
description: 'Stream or search agent runtime logs. This command runs in the terminal.',
examples: [
'agentcore logs',
'agentcore logs --since 30m --level error',
'agentcore logs --agent MyAgent --query "timeout"',
'agentcore logs evals --since 1h',
],
},
traces: {
description: 'View and download agent traces. This command runs in the terminal.',
examples: [
'agentcore traces list',
'agentcore traces list --since 1h --limit 10',
'agentcore traces get <traceId>',
],
},
pause: {
description: 'Pause a deployed online eval config. This command runs in the terminal.',
examples: ['agentcore pause online-eval <name>', 'agentcore pause online-eval --arn <arn>'],
},
resume: {
description: 'Resume a paused online eval config. This command runs in the terminal.',
examples: ['agentcore resume online-eval <name>', 'agentcore resume online-eval --arn <arn>'],
},
};
5 changes: 5 additions & 0 deletions src/cli/tui/hooks/useTextInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export interface UseTextInputOptions {
onDownArrow?: () => void;
/** Whether input is active (default: true) */
isActive?: boolean;
/** Characters to ignore (not added to input) */
excludeChars?: string[];
}

export interface UseTextInputResult {
Expand Down Expand Up @@ -63,6 +65,7 @@ export function useTextInput({
onUpArrow,
onDownArrow,
isActive = true,
excludeChars,
}: UseTextInputOptions = {}): UseTextInputResult {
const [state, setState] = useState({ text: initialValue, cursor: initialValue.length });

Expand Down Expand Up @@ -196,6 +199,8 @@ export function useTextInput({

// Regular character input
if (input && !key.ctrl && !key.meta) {
// Skip excluded characters
if (excludeChars?.includes(input)) return;
// Filter out control characters (DEL, backspace, carriage return)
// eslint-disable-next-line no-control-regex
const filtered = input.replace(/[\x7f\x08\r]/g, '');
Expand Down
Loading
Loading