Welcome to the first chapter of our tutorial! We are going to build a vim command for a CLI (Command Line Interface) tool.
Imagine you have written a cool piece of code that changes how text is edited. But right now, it's just a file sitting on your computer. If a user types vim in the terminal, the computer says "Command not found."
The Problem: How do we tell the main application that our new command exists, what it is called, and where to find the code to run it?
The Use Case: We want to create a command named vim. When a user runs it, it should toggle the editor mode (like switching between a standard notepad and the Vim editor).
The best way to understand Command Definition & Registration is to think of a restaurant.
index.ts): When you sit down, you look at a menu. It lists the names of dishes (e.g., "Spaghetti") and a description ("Pasta with tomato sauce"). It does not contain the chef or the stove. It just tells you what is available.vim.ts): This is where the actual cooking happens. This file contains the logic and ingredients.In our project, we separate these two concerns. We create a "manifest" file (the menu) that points to the "implementation" file (the kitchen).
Let's look at the "Menu" file first. In this project, every command has an index.ts file that acts as its registration card.
First, we define the basic identity of our command.
// File: index.ts
import type { Command } from '../../commands.js'
const command = {
name: 'vim',
description: 'Toggle between Vim and Normal editing modes',
supportsNonInteractive: false,
type: 'local',
// ... more code coming
}
Explanation:
name: This is what the user types in the terminal.description: This is what appears when the user asks for help (e.g., help vim).type: Defines where this runs (locally on your machine).Now, we need to connect the menu to the kitchen. We don't want to load the heavy cooking logic until the user actually orders the dish.
// File: index.ts (continued)
const command = {
// ... previous properties
// This function is only called when the user runs the command!
load: () => import('./vim.js'),
} satisfies Command
export default command
Explanation:
load: This is a function that dynamically imports the logic file.import('./vim.js'): This points to the file where the code actually lives.
What actually happens when you type vim and hit Enter?
index.ts files).name: 'vim'.load() function we defined above.vim.ts file is loaded into memory.call function inside vim.ts.vim.ts)
Now let's peek inside the implementation file to see how it handles the request. This file exports a call function.
First, the command needs to know the current state.
// File: vim.ts
import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'
export const call: LocalCommandCall = async () => {
// Get current settings
const config = getGlobalConfig()
let currentMode = config.editorMode || 'normal'
// ... logic continues
Explanation:
call: This is the standard function name the CLI looks for.getGlobalConfig: We fetch the current user settings. To learn more about how settings are stored, see Global Configuration Management.Next, we calculate the new mode (toggle between 'vim' and 'normal') and save it.
// File: vim.ts (Logic)
// Logic to swap modes
const newMode = currentMode === 'normal' ? 'vim' : 'normal'
// Save the new preference
saveGlobalConfig(current => ({
...current,
editorMode: newMode,
}))
Explanation:
normal to vim and vice versa.Finally, we log that the user did something and return a message.
// File: vim.ts (Ending)
// Log usage data
logEvent('tengu_editor_mode_changed', {
mode: newMode,
source: 'command',
})
return {
type: 'text',
value: `Editor mode set to ${newMode}.`
}
}
Explanation:
logEvent: We track that this feature was used. This is covered in Event Analytics & Telemetry.return: We send a text response back to the user.
In this chapter, you learned how to register a command. We created a "Menu" (index.ts) that describes the command and tells the CLI where to find the "Kitchen" (vim.ts) where the code actually runs.
This separation keeps our application fast and organized. But how does the application find all these index.ts files automatically without us manually listing them in a central file?
To find out, proceed to the next chapter: Dynamic Command Loading.
Generated by Code IQ