Welcome to the extra-usage project tutorial! This is the starting point of our journey. Before we dive into complex logic or user interfaces, we need to answer a fundamental question: How does the CLI know this command exists?
Imagine walking into a large office building. You don't just wander around looking for the person you want to meet. You go to the Reception Desk.
The receptionist has a list of employees (a directory). When you ask for someone, the receptionist checks two things:
If everything checks out, the receptionist calls the employee to come down to the lobby.
In our CLI project, Command Registration is that Reception Desk.
We want to create a command called extra-usage. When a user types this command in their terminal, the CLI needs to:
To solve this, we don't write all our code in one giant file. Instead, we create a small Registration Object that acts like a manifest.
Every command needs a name (what the user types) and a description (what shows up in the help menu).
isEnabled)
This is a rule that tells the CLI: "Only show this command if these conditions are met." If this returns false, the command is invisible to the user.
load)To make the CLI start fast, we don't load the actual code for the command immediately. We use a dynamic import. This is like the receptionist only calling the employee after you arrive, rather than having the employee stand in the lobby all day waiting.
Let's look at how we register the extra-usage command in index.ts. We break this down into small, manageable pieces.
First, we define a helper function to check if the user is even allowed to use this feature.
// From index.ts
function isExtraUsageAllowed(): boolean {
// 1. Check if an environment variable explicitly disables it
if (isEnvTruthy(process.env.DISABLE_EXTRA_USAGE_COMMAND)) {
return false
}
// 2. Check strict authentication rules
return isOverageProvisioningAllowed()
}
Explanation: This function acts as our security guard. It returns true or false. We check environment variables first, then check specific provisioning permissions.
Now we define the command object for human users (Interactive mode).
export const extraUsage = {
type: 'local-jsx', // Uses a rich UI
name: 'extra-usage',
description: 'Configure extra usage...',
// Only enable if allowed AND we are in an interactive session
isEnabled: () => isExtraUsageAllowed() && !getIsNonInteractiveSession(),
load: () => import('./extra-usage.js'),
} satisfies Command
Explanation:
type: 'local-jsx': Tells the CLI this command uses a visual interface (like a menu).isEnabled: We ensure isExtraUsageAllowed() is true AND we are NOT in a script (!getIsNonInteractiveSession()).load: This imports the heavy code from ./extra-usage.js only when the user actually runs the command.What if a script runs this command automatically (Headless mode)? We register a second definition for the same command name.
export const extraUsageNonInteractive = {
type: 'local', // Standard logic, no UI
name: 'extra-usage',
supportsNonInteractive: true,
// Only enable if allowed AND we ARE in a non-interactive session
isEnabled: () => isExtraUsageAllowed() && getIsNonInteractiveSession(),
load: () => import('./extra-usage-noninteractive.js'),
} satisfies Command
Explanation:
name: 'extra-usage': Notice the name is the same as above.isEnabled: The logic is flipped. It requires getIsNonInteractiveSession() to be true.load: It loads a completely different file (./extra-usage-noninteractive.js) optimized for scripts.When the CLI starts, it doesn't read your code files immediately. It only reads these registration objects.
Here is what happens when the CLI boots up:
By having two exports (extraUsage and extraUsageNonInteractive) with the same name but opposing isEnabled logic, we create a smart router.
isEnabled for extraUsage becomes True. extraUsageNonInteractive becomes False. The CLI loads the Interactive UI.isEnabled for extraUsage becomes False. extraUsageNonInteractive becomes True. The CLI loads the Headless script.This concept is crucial for creating a smooth experience. We will explore the differences between these two modes in depth in Interactive vs. Headless Modes.
In this chapter, we learned:
isEnabled to act as a gatekeeper, checking environment variables and permissions.load to lazy-load code, keeping the CLI fast.Now that our command is registered and knows when to run, let's look at what runs.
Next Chapter: Interactive vs. Headless Modes
Generated by Code IQ