Welcome back! In Chapter 1: Command Configuration, we created the "menu item" for our command. We told the system, "If the user types exit, load this file."
Now, we need to write the actual code that runs when that file is loaded.
In traditional command-line tools, commands usually do one thing: they run a task and print text to the screen.
But what if exiting is dangerous? What if you have unsaved work open?
If you try to close a word processor with unsaved changes, it pops up a window: "Do you want to save before quitting?" We want that same level of interactivity in our terminal. We don't just want to print text; we want to render an interface.
The Solution: The Local JSX Command Handler. Instead of just running a function, our command acts like a React component controller. It decides whether to perform an action immediately or render a purely interactive UI (like a dialog box) inside your terminal.
To understand this handler, we need to understand two main ideas:
call Function: Every command exports a function named call. This is the entry point.ReactNode): This function doesn't just return text. It returns a React Component (UI) or null.Imagine a doorman at a building exit:
null -> Action happens immediately).<Component /> -> UI appears).
Let's look at how we implement this logic in our exit.tsx file. We want to check if the user is busy before letting them leave.
First, we define the structure. The system passes us a tool called onDone, which we use to tell the system "We are finished."
import * as React from 'react';
// We import types to keep our code safe
import type { LocalJSXCommandOnDone } from '../../types/command.js';
// This is the main function the system calls
export async function call(onDone: LocalJSXCommandOnDone) {
// Logic goes here...
}
Explanation: This is the standard skeleton for any Local JSX command.
We need to see if the user has an active "Worktree Session" (our term for open work). We will cover exactly how this state works in Chapter 4: Worktree Session State.
import { getCurrentWorktreeSession } from '../../utils/worktree.js';
// ... inside the call function
const showWorktree = getCurrentWorktreeSession() !== null;
Explanation: We ask a utility helper: "Is there anything currently open?" The result is true or false.
If showWorktree is true, we shouldn't exit yet. We should show a component.
import { ExitFlow } from '../../components/ExitFlow.js';
if (showWorktree) {
// Return a React Component!
return <ExitFlow
onDone={onDone}
onCancel={() => onDone()}
/>;
}
Explanation: This is the "JSX" part. We return an <ExitFlow /> tag. The CLI will render this as a visual interface (buttons, text) in the terminal. The command technically keeps running until the user interacts with that UI.
If there is no work open, we just leave.
import { gracefulShutdown } from '../../utils/gracefulShutdown.js';
// If no worktree, say goodbye and quit
onDone('Goodbye!');
await gracefulShutdown(0, 'prompt_input_exit');
return null;
Explanation: We return null because we don't need to show any UI. We trigger the shutdown process immediately. (We will learn more about this in Chapter 5: Graceful Shutdown).
How does the system handle these different return types? Let's visualize the decision-making process.
The real power here is that call is async. This means it can perform asynchronous checks (like checking a database or file system) before deciding what to show.
Here is a simplified view of the actual exit.tsx file combining the parts we discussed:
export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNode> {
// 1. Check if we have open work
const showWorktree = getCurrentWorktreeSession() !== null;
// 2. If yes, render the UI Component
if (showWorktree) {
return <ExitFlow showWorktree={showWorktree} onDone={onDone} />;
}
// 3. If no, just exit
onDone('Goodbye!');
await gracefulShutdown(0, 'prompt_input_exit');
// 4. Return null tells React "render nothing"
return null;
}
Note: The actual source code includes an extra check at the beginning for
BG_SESSIONS(background sessions like tmux). If running in the background, we detach instead of quit. We will explore how that persistence works in Chapter 3: Background Session Persistence.
In this chapter, we learned that a command isn't just a script; it's a UI Controller.
call function is the brain.null to perform invisible actions immediately.Now that we know how to handle the immediate "exit" action, what happens if we don't want to exit fully? What if we want the program to keep running in the background?
Next Chapter: Background Session Persistence
Generated by Code IQ