Add a CLI Command
Add a new command to the maltty CLI end-to-end: handler, registration, and verification.
Prerequisites
- Familiarity with the CLI concepts and architecture
- The project builds successfully (
pnpm typecheck)
Steps
1. Create the command file
Create a new file in the commands directory. The filename becomes the command name (e.g., check.ts registers as the check command).
With Zod args:
Without args:
Hidden or deprecated:
Commands can be hidden from --help output or marked as deprecated. Both hidden and deprecated accept a static value or a function (Resolvable<T>), resolved at registration time.
Individual flags also support hidden, deprecated, and group:
With subcommands:
Create a directory with an index.ts for the parent command and individual files for each subcommand:
With render mode (.tsx):
Commands that need React/Ink UI use a render function instead of handler. The file must use the .tsx extension.
The render function receives RenderProps (with args, config, meta, store, colors) and owns the full Ink lifecycle. Place command-private components in a _components/ directory next to the command file. See the Components standard for full conventions.
2. Register the command
Commands are auto-registered via the autoloader when placed in the commands directory. The autoloader discovers files that:
- Have a
.ts,.tsx, or.jsextension (not.d.ts) - Do not start with
_or. - Export a default
Commandobject (created by thecommand()factory)
No manual registration is needed.
3. Add lib functions if needed
If the command needs new shared logic, add it to packages/maltty/src/lib/. Follow existing patterns:
- Return
Resulttuples for operations that can fail - Use Zod for runtime validation at boundaries
- Keep functions pure where possible
4. Write tests
Create *.test.ts files in the test/ directory following the existing structure. Test the handler directly by constructing a mock context:
- Test the success path with valid args
- Test each failure path with expected errors
- Test Zod validation rejects invalid inputs
5. Verify
Run the full CI check suite:
Verification
After completing all steps:
- Run
pnpm typecheckand confirm no errors - Run
pnpm testand confirm all tests pass - Run
pnpm maltty <name> --helpand confirm the command appears - Run the command and verify the expected behavior
Troubleshooting
Command not appearing in help
Issue: The new command does not show up in maltty --help.
Fix: Ensure the file is in the commands directory, has a .ts or .js extension, does not start with _ or ., and exports a default Command created by the command() factory.
Zod validation fails at runtime
Issue: The handler receives a validation error for a valid-looking input.
Fix: Verify the Zod schema matches the expected args shape. Args are validated against the schema before the handler runs. Check that optional fields use .optional() and defaults use .default().
Handler not receiving expected context
Issue: Properties on ctx are missing or mistyped.
Fix: Verify the command uses command() from maltty (not a custom wrapper). Check that module augmentation interfaces (MalttyArgs, CliConfig, MalttyStore) are correctly declared if using typed store keys or global args.