Skill v1.0.0
currentTrusted Publisher100/100version: "1.0.0" name: fluentui-cli description: 'Guides working with @fluentui/cli — the internal Fluent UI command-line tool. Use when asked to add a new CLI command, modify an existing command, understand CLI architecture, debug CLI issues, or work with the CLI build/test workflow. Covers: architecture overview, yargs conventions, lazy-loading pattern, testing, and available Nx generators.'
@fluentui/cli
The @fluentui/cli package (tools/cli/) is the internal Fluent UI command-line tool built with yargs. It uses a modular, lazy-loading architecture where each command lives in its own directory and is only loaded at runtime when invoked.
Architecture
File Structure
tools/cli/├── bin/fluentui-cli.js # Node entry point (requires compiled output)├── src/│ ├── cli.ts # Main yargs setup, registers all commands│ ├── index.ts # Public API re-exports│ ├── utils/│ │ ├── index.ts # Barrel exports│ │ └── types.ts # Shared CommandHandler type│ └── commands/│ ├── metadata/ # API metadata command│ │ ├── index.ts # CommandModule definition│ │ ├── handler.ts # Lazy-loaded handler│ │ └── impl/ # Parsing and formatting│ └── report/ # Reporting command group│ ├── index.ts # Registers report subcommands│ ├── commands/ # info and usage definitions│ └── impl/ # Lazy-loaded report implementations
Lazy Loading Pattern
Command definitions (name, description, options builder) are eagerly imported — they are lightweight. The actual handler logic is lazy-loaded via dynamic `import()` only when the command is invoked:
// index.ts — always loaded (lightweight)handler: async argv => {// handler.ts only loaded when this specific command runsconst { handler } = await import('./handler');return handler(argv);},
This keeps command implementations out of the startup path until the selected command runs.
CommandHandler Type
All handlers use the shared CommandHandler<T> type from src/utils/types.ts:
import type { ArgumentsCamelCase } from 'yargs';export type CommandHandler<T = {}> = (argv: ArgumentsCamelCase<T>) => Promise<void>;
Command Registration in cli.ts
Each command is imported and registered in tools/cli/src/cli.ts:
import yargs from 'yargs';import reportCommand from './commands/report';import metadataCommand from './commands/metadata';export async function main(argv: string[]): Promise<void> {await yargs(argv).scriptName('fluentui-cli').usage('$0 <command> [options]').command(reportCommand).command(metadataCommand).demandCommand(1, 'You need to specify a command to run.').help().strict().parse();}
Build & Test
# Build the CLIyarn nx run cli:build# Run testsyarn nx run cli:test# Test --help outputnode tools/cli/bin/fluentui-cli.js --helpnode tools/cli/bin/fluentui-cli.js <command-name> --help
Conventions
- Always use the Nx generator to scaffold new commands — do not create command files manually. See the adding commands reference.
- Place shared utilities in
tools/cli/src/utils/and export through the barrel file. - Every command must support
--help(handled by yargs.help()in the builder). - Handler files must export a named
handlerconstant typed withCommandHandler<T>. - Tests live adjacent to handlers as
handler.spec.ts.
Common Patterns
Subcommands (nested commands)
If a command needs subcommands, use yargs nested command pattern in the builder:
builder: yargs =>yargs.command('summary', 'Generate a summary', subBuilder => subBuilder, summaryHandler).command('details', 'Generate detailed output', subBuilder => subBuilder, detailsHandler).demandCommand(1).help(),