Skip to main content

stacks-cli

Use when building CLI commands or tools with Stacks - the @stacksjs/cli package for creating commands with argument parsing, option handling, colored output, tables, progress indicators, prompts, or integrating with the buddy command system. Covers @stacksjs/cli and app/Commands/.

Quellinformationen

Repository
stacksjs/stacks
Letzte Quellaktivität
2. November 2026 um 19:16
Erkannte Sprache von SKILL.md
Englisch
Sterne
637
Forks
20

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
stacks-cli
description
Use when building CLI commands or tools with Stacks - the @stacksjs/cli package for creating commands with argument parsing, option handling, colored output, tables, progress indicators, prompts, or integrating with the buddy command system. Covers @stacksjs/cli and app/Commands/.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks CLI Framework The `@stacksjs/cli` package provides the foundation for building CLI commands, used internally by the buddy CLI. ## Key Paths - Core package: `storage/framework/core/cli/src/` - CLI configuration: `config/cli.ts` - Application commands: `app/Commands/` - Optional registry: `app/Commands.ts` (not needed - see below) ## Creating Commands Every `.ts` file in `app/Commands/` is a command. There is no registration step and no generated registry file: drop the file in and it is live, nested directories included (`app/Commands/Archive/Run.ts`). ### Declarative form (preferred) `defineCommand()` infers the handler's `options` from the flags declared above it, so there is no hand-written options interface to drift out of step. ```typescript // app/Commands/Greet.ts import { defineCommand, log } from '@stacksjs/cli' export default defineCommand({ name: 'greet <who>', description: 'Greet a user', aliases: ['g'], options: { '--loud, -l': { description: 'Shout the greeting', default: false }, '--title <title>': 'Prefix the name', '--times <n>': { description: 'How often', default: 1, type: [Number] }, }, handle(options, who) { // options.loud -> boolean // options.title -> string | undefined // options.times -> number const greeting = `Hello, ${options.title ?? ''}${who}!` for (let i = 0; i < options.times; i++) log.info(options.loud ? greeting.toUpperCase() : greeting) }, }) ``` Flag to property: `--dry-run` -> `dryRun`, `--two, -t` -> `two`, `--no-cache` -> `cache`. Value type: `<x>` -> `string`, `[x]` -> `string | true`, `<x...>` -> `string[]`, no argument -> `boolean`. A `default` makes the property always present; a `type: [Number]` makes it a `number`. ### Imperative form For a file that registers several commands or needs `cli.on()`: ```typescript import { defineCommand, log } from '@stacksjs/cli' export default defineCommand((cli) => { cli.command('inspire', 'Inspire yourself').alias('insp').action(() => {}) cli.command('inspire:two', 'Two quotes').action(() => {}) cli.on('inspire:*', () => log.error('Invalid command')) }) ``` ### Per-file configuration Named exports beside the default one, so a command owns its own configuration: ```typescript export const aliases = ['emails', 'mail'] // extra aliases export const enabled = false // keep the file, hide the command ``` ### The optional registry (`app/Commands.ts`) Only worth keeping when you want to control the order commands are listed in, or to alias/disable a command without editing its file. A command file the registry never mentions still loads. ```typescript import { defineCommands } from '@stacksjs/cli' export default defineCommands({ 'send-emails <type>': { file: 'SendEmails', aliases: ['emails'] }, 'legacy': { file: 'Legacy', enabled: false }, }) ``` ### Registration Methods 1. **Drop a file in `app/Commands/`** - auto-discovered, nothing else to do 2. **Event listeners** - CLI events in `app/Listeners/Console.ts` 3. **`app/Commands.ts`** - optional, for ordering / aliases / disabling ## CLI Event Listeners ```typescript // app/Listeners/Console.ts export default function(cli: CLI) { cli.on('custom:command', () => { log.info('Custom command executed!') }) cli.on('my:*', () => { // Wildcard - matches my:anything }) cli.on('unknown:!', () => { // Default/fallback handler }) } ``` ## Plain command output Import `print` and `printError` from `@stacksjs/cli/output` for command results, test tallies and diagnostics that must be visible even when logging is filtered. They write synchronously to stdout and stderr, preserve Node formatting and inspection, append a newline, and complete before an immediate `process.exit()`. The narrow import needs no application configuration. Use `log` for application logging with levels, context and transports. For large results or piped output, use `await printAsync(...args)` and `await printErrorAsync(...args)` from the same entrypoint. Their signatures are `(...args: unknown[]) => Promise<void>`. They append a newline, preserve Node formatting, wait for stream writes under backpressure, and reject write errors such as `EPIPE`. Await them before `process.exit()`. The synchronous helpers remain for small, bounded immediate-exit messages and may throw `EAGAIN` when a nonblocking pipe is full. These helpers are also exported by `@stacksjs/cli`. ## Output Formatting Via `@stacksjs/utils` color functions (available in CLI context): ```typescript import { bold, green, red, yellow, dim, underline } from '@stacksjs/utils' print(green('✓ Success')) printError(red('✗ Error')) console.log(yellow('⚠ Warning')) console.log(bold('Important')) console.log(dim('Subtle info')) ``` ## config/cli.ts (BinaryConfig) ```typescript { name: 'My Custom CLI', command: 'mycli', description: 'My custom CLI tool', deploy: true } ``` ## CLI Commands - `buddy make:command [name]` - scaffold a new command ## Compiled Binaries ```bash buddy build:cli # build CLI binary # Compiles for: linux-x64, linux-arm64, windows-x64, darwin-x64, darwin-arm64 ``` ## Gotchas - Application commands go in `app/Commands/`, framework commands in `storage/framework/core/buddy/src/commands/` - Commands are auto-discovered from `app/Commands/`; `app/Commands.ts` is optional and additive - CLI events support wildcards (`*`) and default handlers (`!`) - The buddy CLI lazy-loads commands - not all load at startup - Output formatting uses ANSI colors from `@stacksjs/utils` - Custom CLIs can be compiled to platform-specific binaries - The CLI framework is separate from buddy - buddy uses it internally ## Native terminal primitives The public `@stacksjs/cli` entry re-exports table, progress, tasks, note, getTerminalSize and isInteractive from `core/cli/src/terminal.ts`. Import dump/dd/echo from `@stacksjs/logging`, where those debug helpers live. Read their options/types before building a new terminal formatter or adding another CLI dependency. Commands can also use the native prompt, spinner, exec and signal helpers from the same package. A command file is discovered recursively; its handler's argument order comes from defineCommand, not a copied CAC action shape. Source evidence: `storage/framework/core/cli/src/index.ts`, `commands.ts` and `terminal.ts`; command discovery is owned by buddy's runtime registry. `buddy list` and `buddy <command> --help` are the authoritative command/flag inventory, including package-contributed commands. ## Native command ownership Import `spawnOwnedProcess` from `@stacksjs/cli/owned-process` for a command whose normally inherited descendants belong to the same lifetime. This subpath uses only native Bun/Node primitives, with no CLI or application bootstrap. A controlled owner establishes a POSIX process group or native Windows Job Object before admitting the command. `started` resolves its actual command PID; `pid` identifies the stable lifecycle owner. Consumer IPC is forwarded with the verified command PID. `exited` preserves command status and settles after tree disposal. `stop()` is idempotent, and signaling the owner also initiates cleanup. Compiled callers supply their installed Bun path as `ownerRuntime`. This is lifecycle ownership, not a sandbox: deliberate POSIX breakaway is outside the group contract. Native admission or disposal failures reject rather than falling back to direct-child cleanup. `runBoundedProcess` in `cli/process` retains its separate output-byte/deadline contract; it is not this ownership API.
Auf GitHub ansehen