scaffold-cli-command
정보
이 스킬은 구조화된 옵션, 액션 핸들러, 그리고 세 가지 출력 모드(가독성 높은 모드, 정숙 모드, JSON 모드)를 갖춘 새로운 Commander.js CLI 명령어를 구성합니다. 이는 개발자가 기존 CLI에 명령어를 추가하거나, 새로운 도구를 처음부터 설계하거나, 다중 명령어 CLI 전반에 걸쳐 구조를 표준화하는 데 도움을 줍니다. 생성된 코드에는 공유 컨텍스트 패턴, 오류 처리, 그리고 통합 테스트 가이드라인이 포함됩니다.
빠른 설치
Claude Code
추천npx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/scaffold-cli-commandClaude Code에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요
문서
Scaffold a CLI Command
Add a new command to a Commander.js CLI with consistent option handling, three output modes, and integration tests.
When to Use
- Adding a new command to an existing Commander.js CLI
- Designing a multi-command CLI tool from scratch
- Standardizing command structure so all commands follow the same patterns
- Adding a "ceremony" variant that replaces machine output with warm narrative
Inputs
- Required: Command name and verb (e.g.,
gather,audit,sync) - Required: What the command does (one sentence)
- Required: Path to CLI entry point (e.g.,
cli/index.js) - Optional: Whether the command needs a ceremony variant
- Optional: Custom options beyond the standard set
- Optional: Subcommand arguments (positional args like
<name>or[names...])
Procedure
Step 1: Choose the Command Name and Category
Select a verb that communicates the command's action. Group commands into categories:
| Category | Verbs | Pattern |
|---|---|---|
| CRUD | install, uninstall, list, search | Operates on content |
| Lifecycle | init, sync, audit | Manages project state |
| Ceremony | gather, scatter, tend, campfire | Warm narrative output |
Naming conventions:
- Single verb (not
install-skill— let options specify what) - Lowercase, no hyphens in the command name itself
- Positional args:
<required>or[optional]or[variadic...]
program
.command('gather <name>')
.description('Gather a team around the campfire')
Got: A command name, description, and positional args defined.
If fail: If the verb overlaps an existing command, either compose them (add an option to the existing command) or differentiate clearly in the description.
Step 2: Define Options
Every command should support a standard set of shared options plus command-specific ones.
Standard options (include as needed):
.option('-n, --dry-run', 'Preview without making changes')
.option('-q, --quiet', 'Suppress human-readable output')
.option('--json', 'Output as JSON')
.option('-f, --framework <id>', 'Target specific framework')
.option('-g, --global', 'Use global scope')
.option('--scope <scope>', 'Scope: project, workspace, global', 'project')
.option('--source <path>', 'Path to tool root directory')
Command-specific options — add only what the command needs:
.option('--ceremonial', 'Show each item arriving individually')
.option('--only <items>', 'Comma-separated subset to include')
.option('-y, --yes', 'Skip confirmation prompts')
Design rules:
- Short flags (
-n) for frequently used options - Long flags (
--dry-run) for clarity - Default values as third argument where appropriate
- Boolean flags (no argument) for toggles
Got: A complete option chain with both standard and custom options.
If fail: If too many options accumulate (>8), consider splitting into subcommands or grouping related options.
Step 3: Implement the Action Handler
The action handler follows a consistent pattern:
.action(async (name, options) => {
// 1. Get shared context (registries, adapters, paths)
const ctx = getContext(options);
// 2. Resolve what to operate on
const items = resolveItems(ctx, name, options);
if (!items || items.length === 0) {
reporter.error('Nothing found.');
process.exit(1);
}
// 3. Preview if dry-run
if (options.dryRun) reporter.printDryRun();
// 4. Execute the operation
const results = await executeOperation(items, ctx, options);
// 5. Output results (3 modes)
if (options.json) {
console.log(JSON.stringify(results, null, 2));
} else if (options.quiet) {
reporter.printResults(results);
} else {
printHumanOutput(results, options);
}
})
The getContext() shared helper centralizes:
- Root directory detection
- Registry loading
- Framework detection or explicit selection
- Scope resolution
Got: An action handler that follows the 5-step pattern: context → resolve → preview → execute → output.
If fail: If the command doesn't fit the resolve-then-execute pattern (e.g., purely informational like detect), simplify to: context → compute → output.
Step 4: Add the Three Output Modes
Every command should support three output modes:
Default (human-readable):
Installing 3 item(s) to Claude Code...
+ create-skill [claude-code] .claude/skills/create-skill
+ write-tests [claude-code] .claude/skills/write-tests
= commit-changes [claude-code] (skipped)
2 installed, 1 skipped
Quiet (--quiet):
Standard reporter output — concise lines with status icons (+, -, =, !), no ceremony, no decoration.
JSON (--json):
{
"command": "install",
"items": 3,
"installed": 2,
"skipped": 1,
"failed": 0
}
Implementation pattern:
if (options.json) {
console.log(JSON.stringify(data, null, 2));
return;
}
if (options.quiet) {
reporter.printResults(results);
return;
}
// Default: human-readable output
printHumanReadable(results, options);
Got: All three modes produce useful output. JSON is parseable. Quiet is concise. Default is informative.
If fail: If the command has no meaningful JSON representation (e.g., detect), skip the JSON mode and document why.
Step 5: Add Ceremony Variant (Optional)
For commands that benefit from warm, narrative output instead of transactional reporting:
if (options.json) {
ceremonyReporter.printJson(data);
} else if (options.quiet) {
reporter.printResults(results);
} else {
ceremonyReporter.printArrival({
teamId: name,
agents,
results: { installed, skipped, failed },
ceremonial: options.ceremonial || false,
});
}
Ceremony output follows voice rules:
- Present tense, active voice ("mystic arrives", not "mystic was installed")
- No exclamation marks
- Metaphor replaces jargon ("practices" not "dependencies")
- Failures are honest, not catastrophic ("a spark was lost")
- Closing line reflects state ("The fire burns.")
- No emoji — use Unicode glyphs (✦ ◉ ◎ ○ ✗)
- Every word must carry information
See design-cli-output for detailed terminal output patterns.
Got: Ceremony output that follows all voice rules and produces warm, informative narratives.
If fail: If ceremony output feels forced or adds no information beyond the standard output, skip it. Not every command needs a ceremony variant.
Step 6: Handle Errors and Edge Cases
// Unknown item
if (!item) {
reporter.error(`Unknown: ${name}. Use 'tool list' to browse.`);
process.exit(1);
}
// Confirmation for destructive actions
if (!options.yes && !options.quiet && !options.dryRun) {
const answer = await askYesNo('Proceed?');
if (!answer) {
console.log(' Cancelled.');
return;
}
}
// State validation
if (!state.fires[name]) {
reporter.error(`Not active. Nothing to remove.`);
process.exit(1);
}
Error design principles:
- Error messages suggest the corrective action
process.exit(1)for unrecoverable errors- Confirmation prompts for destructive operations (bypass with
--yes) - Dry-run always succeeds (never blocks on confirmation)
Got: All error paths produce helpful messages. Destructive operations require confirmation.
If fail: If confirmation prompts interfere with scripting, ensure --yes and --quiet both bypass them.
Step 7: Write Integration Tests
import { describe, it, after } from 'node:test';
import assert from 'node:assert/strict';
import { execSync } from 'child_process';
const CLI = 'node cli/index.js';
function run(args) {
return execSync(`${CLI} ${args}`, { encoding: 'utf8', timeout: 10000 });
}
describe('new-command', () => {
after(() => { /* cleanup created files/state */ });
it('dry-run shows preview', () => {
const out = run('new-command arg --dry-run');
assert.match(out, /DRY RUN/);
});
it('--json outputs valid JSON', () => {
const out = run('new-command arg --json');
const start = out.indexOf('{');
const data = JSON.parse(out.slice(start));
assert.equal(data.command, 'new-command');
});
it('rejects unknown input', () => {
assert.throws(() => run('new-command nonexistent'), /Unknown/);
});
});
See test-cli-application for comprehensive CLI testing patterns.
Got: At least 3 tests: dry-run, JSON output, error case. More for complex commands.
If fail: If execSync times out, increase the timeout or check for interactive prompts blocking the command.
Validation
- Command registered in CLI entry point and appears in
--help - Standard options (
--dry-run,--quiet,--json) work correctly - Default output is human-readable and informative
- JSON output is valid and parseable
- Error messages suggest corrective actions
- Destructive operations require confirmation (bypassed by
--yes) - At least 3 integration tests pass
- Command follows the getContext → resolve → execute → output pattern
Pitfalls
- Forgetting the JSON mode: Machine consumers (scripts, CI) depend on structured output. Always implement
--jsoneven if the command seems interactive-only. - Confirmation prompts blocking scripts: Any command that prompts for input will hang in non-interactive contexts. Always provide
--yesfor destructive commands and ensure--quietsuppresses prompts. - Inconsistent error exit codes: Use
process.exit(1)for all errors. Tools that parse CLI output check exit codes first. - Options without defaults: Options like
--scopeshould have sensible defaults so users don't need to specify them every time. - Leaking ceremony into quiet mode: The
--quietflag means "minimal output for machines." If ceremony text leaks into quiet mode, scripts will break on unexpected output.
Related Skills
build-cli-plugin— build the adapter/plugin that commands operate ontest-cli-application— comprehensive CLI testing patterns beyond the basics in Step 7design-cli-output— terminal output design for all verbosity levelsinstall-almanac-content— example of a well-structured CLI command skill
GitHub 저장소
연관 스킬
evaluating-llms-harness
테스팅이 Claude Skill은 MMLU, GSM8K를 포함한 60개 이상의 표준화된 학술 과제에서 LLM 성능을 벤치마크하기 위해 lm-evaluation-harness를 실행합니다. 개발자들이 모델 품질을 비교하고, 학습 진행 상황을 추적하거나 학술 결과를 보고할 수 있도록 설계되었습니다. 이 도구는 HuggingFace와 vLLM 모델을 포함한 다양한 백엔드를 지원합니다.
cloudflare-cron-triggers
테스팅이 스킬은 cron 표현식을 사용하여 Worker를 스케줄링하기 위한 Cloudflare Cron Triggers 구현에 관한 포괄적인 지식을 제공합니다. 주기적 작업, 유지보수 작업, 자동화된 워크플로우 설정 방법을 다루며, 잘못된 cron 표현식이나 시간대 문제 같은 일반적인 이슈들을 해결하는 방법을 포함합니다. 개발자들은 이를 통해 스케줄된 핸들러 구성, cron 트리거 테스트, Workflows 및 Green Compute와의 연동 작업을 수행할 수 있습니다.
webapp-testing
테스팅이 Claude Skill은 Python 스크립트를 통해 로컬 웹 애플리케이션을 테스트하기 위한 Playwright 기반 툴킷을 제공합니다. 프론트엔드 검증, UI 디버깅, 스크린샷 캡처, 로그 확인 기능을 지원하며 서버 라이프사이클을 관리합니다. 브라우저 자동화 작업에 사용하되 컨텍스트 오염을 방지하기 위해 소스 코드를 읽지 않고 스크립트를 직접 실행하세요.
finishing-a-development-branch
테스팅이 스킬은 테스트 통과를 확인한 후 체계적인 통합 옵션을 제시하여 개발자가 완성된 작업을 마무리하도록 돕습니다. 구현이 완료된 후 머지, PR 생성, 브랜치 정리와 같은 워크플로우를 안내합니다. 코드가 준비되고 테스트가 완료되었을 때 개발 프로세스를 체계적으로 마무리하기 위해 사용하세요.
