@@ -26,145 +26,21 @@ import { registerTraces } from './commands/traces';
2626import { registerUpdate } from './commands/update' ;
2727import { registerValidate } from './commands/validate' ;
2828import { PACKAGE_VERSION } from './constants' ;
29+ import { printPostCommandNotices , printTelemetryNotice } from './notices' ;
2930import { ALL_PRIMITIVES } from './primitives' ;
3031import { TelemetryClientAccessor } from './telemetry' ;
31- import { App , type InitialRoute } from './tui/App ' ;
32+ import { renderTUI , setupAltScreenCleanup } from './tui' ;
3233import { LayoutProvider } from './tui/context' ;
3334import { COMMAND_DESCRIPTIONS } from './tui/copy' ;
34- import { clearExitAction , getExitAction } from './tui/exit-action' ;
3535import { clearExitMessage , getExitMessage } from './tui/exit-message' ;
3636import { requireTTY } from './tui/guards' ;
3737import { CommandListScreen } from './tui/screens/home' ;
3838import { getCommandsForUI } from './tui/utils' ;
39- import { type UpdateCheckResult , checkForUpdate , printUpdateNotification } from './update-notifier' ;
39+ import { checkForUpdate } from './update-notifier' ;
4040import { Command } from '@commander-js/extra-typings' ;
4141import { render } from 'ink' ;
4242import React from 'react' ;
4343
44- // ANSI escape sequences
45- const ENTER_ALT_SCREEN = '\x1B[?1049h\x1B[H' ;
46- const EXIT_ALT_SCREEN = '\x1B[?1049l' ;
47- const SHOW_CURSOR = '\x1B[?25h' ;
48-
49- // Track if we're in alternate screen mode
50- let inAltScreen = false ;
51-
52- /**
53- * Global terminal cleanup - ensures cursor is always restored on exit.
54- * Registered once at startup, catches all exit scenarios.
55- */
56- function setupGlobalCleanup ( ) {
57- const cleanup = ( ) => {
58- if ( inAltScreen ) {
59- process . stdout . write ( EXIT_ALT_SCREEN ) ;
60- }
61- process . stdout . write ( SHOW_CURSOR ) ;
62- } ;
63-
64- process . on ( 'exit' , cleanup ) ;
65- process . on ( 'SIGINT' , ( ) => {
66- cleanup ( ) ;
67- process . exit ( 0 ) ;
68- } ) ;
69- process . on ( 'SIGTERM' , ( ) => {
70- cleanup ( ) ;
71- process . exit ( 0 ) ;
72- } ) ;
73- }
74-
75- function printTelemetryNotice ( ) : void {
76- const yellow = '\x1b[33m' ;
77- const reset = '\x1b[0m' ;
78- process . stderr . write (
79- [
80- '' ,
81- `${ yellow } The AgentCore CLI will soon begin collecting aggregated, anonymous usage` ,
82- 'analytics to help improve the tool.' ,
83- 'To opt out: agentcore telemetry disable' ,
84- `To learn more: agentcore telemetry --help${ reset } ` ,
85- '' ,
86- '' ,
87- ] . join ( '\n' )
88- ) ;
89- }
90-
91- function printPostCommandNotices ( isFirstRun : boolean , updateCheck : Promise < UpdateCheckResult | null > ) : Promise < void > {
92- if ( isFirstRun ) {
93- printTelemetryNotice ( ) ;
94- }
95- return updateCheck . then ( result => {
96- if ( result ?. updateAvailable ) {
97- printUpdateNotification ( result ) ;
98- }
99- } ) ;
100- }
101-
102- export interface RenderTUIOptions {
103- /** Route to navigate to on launch. If omitted, shows the default home/help screen. */
104- initialRoute ?: InitialRoute ;
105- /** Promise that resolves with update check result. Used to print update notifications on exit. Default: Promise.resolve(null) */
106- updateCheck ?: Promise < UpdateCheckResult | null > ;
107- /** Whether this is the first time the CLI has been run. Shows telemetry notice on exit. Default: false */
108- isFirstRun ?: boolean ;
109- /** Control whether TUI is rendered inline or in alternate screen. Default: true */
110- enterAltScreen ?: boolean ;
111- /** Behavior when pressing escape/back. 'help' navigates to the help screen, 'exit' exits the app. Default: 'help' */
112- actionOnBack ?: 'help' | 'exit' ;
113- /** Whether the TUI is running in full interactive mode. When false, screens auto-exit after success. Default: true */
114- isInteractive ?: boolean ;
115- }
116-
117- /**
118- * Render the TUI in alternate screen buffer mode.
119- * This is the entrypoint for TUI operations
120- */
121- export async function renderTUI ( options : RenderTUIOptions = { } ) {
122- const {
123- initialRoute,
124- updateCheck = Promise . resolve ( null ) ,
125- isFirstRun = false ,
126- enterAltScreen = true ,
127- actionOnBack = 'help' ,
128- isInteractive = true ,
129- } = options ;
130- await TelemetryClientAccessor . init ( initialRoute ?. name ?? 'tui' , 'tui' ) ;
131- if ( enterAltScreen ) {
132- inAltScreen = true ;
133- process . stdout . write ( ENTER_ALT_SCREEN ) ;
134- }
135-
136- const { waitUntilExit } = render ( React . createElement ( App , { initialRoute, actionOnBack, isInteractive } ) ) ;
137-
138- await waitUntilExit ( ) ;
139-
140- if ( inAltScreen ) {
141- inAltScreen = false ;
142- process . stdout . write ( EXIT_ALT_SCREEN ) ;
143- process . stdout . write ( SHOW_CURSOR ) ;
144- }
145-
146- await TelemetryClientAccessor . shutdown ( ) ;
147-
148- // Check if the TUI requested a post-exit action (e.g., launch browser dev mode)
149- const action = getExitAction ( ) ;
150- clearExitAction ( ) ;
151-
152- if ( action ?. type === 'dev' ) {
153- const { launchBrowserDev } = await import ( './commands/dev/browser-mode' ) ;
154- await launchBrowserDev ( ) ;
155- return ;
156- }
157-
158- // Print any exit message set by screens (e.g., after successful project creation)
159- const exitMessage = getExitMessage ( ) ;
160- if ( exitMessage ) {
161- console . log ( exitMessage ) ;
162- clearExitMessage ( ) ;
163- }
164-
165- await printPostCommandNotices ( isFirstRun , updateCheck ) ;
166- }
167-
16844function renderHelp ( program : Command ) : void {
16945 const commands = getCommandsForUI ( program ) ;
17046 render ( React . createElement ( LayoutProvider , null , React . createElement ( CommandListScreen , { commands } ) ) ) ;
@@ -245,7 +121,7 @@ export function registerCommands(program: Command) {
245121
246122export const main = async ( argv : string [ ] ) => {
247123 // Register global cleanup handlers once at startup
248- setupGlobalCleanup ( ) ;
124+ setupAltScreenCleanup ( ) ;
249125
250126 // Generate installationId on first run and show telemetry notice
251127 const { created : isFirstRun } = await getOrCreateInstallationId ( ) ;
0 commit comments