| name | design-cli-output |
| description | Design terminal output for a CLI tool with chalk colors, Unicode glyphs, multiple verbosity levels (human, verbose, quiet, JSON), and consistent voice rules. Covers color palette selection, status indicator design, reporter function architecture, ceremony/narrative output variants, and cross-terminal compatibility. Use when building a new CLI reporter module, adding warm narrative output to an existing tool, standardizing output across multiple commands, or designing machine-readable JSON alongside human-readable text.
|
| license | MIT |
| allowed-tools | Read Write Edit Bash Grep Glob |
| metadata | {"author":"Philipp Thoss","version":"1.0","domain":"cli","complexity":"basic","language":"TypeScript","tags":["cli","terminal","ux","chalk","unicode"],"locale":"ja","source_locale":"en","source_commit":"2630b6e9","translator":"Claude + human review","translation_date":"2026-07-30"} |
Design CLI Output
ã³ãã³ãã©ã€ã³ããŒã«ã®ããã«ãäžè²«ãããã«ãã¬ãã«ã®ã¿ãŒããã«åºåãèšèšããã
䜿çšã¿ã€ãã³ã°
- CLI ããŒã«çšã®æ°ããã¬ããŒã¿ãŒã¢ãžã¥ãŒã«ãæ§ç¯ãããšã
- æšæºçãªãã©ã³ã¶ã¯ã·ã§ã³åºåã«å ããŠæž©ããã»ç©èªçãªåºåã远å ãããšã
- è€æ°ã³ãã³ãã«ããã£ãŠåºåãã©ãŒããããæšæºåãããšã
- 人éå¯èªåºåãšäžŠè¡ãã JSON ãã·ã³åºåãèšèšãããšã
- æ°ããã¿ãŒããã«ããŒã«ã®ã«ã©ãŒãã°ãªããåé·åºŠã¬ãã«ãéžã¶ãšã
å
¥å
- å¿
é : CLI ããŒã«åãšäž»ãªå¯Ÿè±¡ïŒéçºè
ãéçšè
ããšã³ããŠãŒã¶ãŒïŒ
- å¿
é : åºåãã©ãŒããããå¿
èŠãªã³ãã³ã
- ä»»æ: ãã»ã¬ã¢ããŒããŸãã¯ç©èªåºåããªã¢ã³ããæãŸããã
- ä»»æ: ãã©ã³ãã£ã³ã°å¶çŽïŒã«ã©ãŒãã¬ãããããŒã³ïŒ
æé
ã¹ããã1: ã«ã©ãŒãã¬ãããå®çŸ©ãã
chalk ã䜿ã£ãŠååä»ããã¬ãããªããžã§ã¯ããäœæããã
chalk ã no-color ãã©ãŒã«ããã¯ã®èåŸã§ããŒãããã ãã©ãŒã«ããã¯ã¯ãã¬ããã䜿ããã¹ãŠã®åŒã³åºã圢ã代æ¿ããªããã°ãªãããããã¯æååããã®ãŸãŸéãããšã ãã§ã¯è¶³ããªã:
const FACTORIES = new Set(['ansi256', 'bgAnsi256', 'bgHex', 'bgRgb', 'hex',
'rgb', 'underlineAnsi256', 'underlineHex', 'underlineRgb']);
function makeChalkStub() {
return new Proxy((text) => text, {
get(target, prop) {
if (prop === 'then') return undefined;
if (prop === 'level') return 0;
if (typeof prop === 'symbol') return Reflect.get(target, prop);
return FACTORIES.has(prop) ? () => makeChalkStub() : makeChalkStub();
},
});
}
chalk;
{ chalk = ( ()).; }
{ chalk = (); }
4 ã€ã®äžå€æ¡ä»¶ãããçãã¹ã¿ãã¯ãããããåãéãã:
- Proxy ã®ã¿ãŒã²ãããåŒã³åºãå¯èœ:
{} ã§ã¯ãªã (text) => textããã§ãŒã³ïŒchalk.bold.cyan('x')ïŒã¯ãã¹ãŠã®ããããã€ã³ããã¯ã¹å¯èœãã€åŒã³åºãå¯èœã§ããããšãèŠæ±ããã
- ãã¡ã¯ããªã¯é¢æ°ãè¿ã:
new Proxy({}, { get: () => (s) => s }) ã¯çŽæ¥ã¹ã¿ã€ã«ãæºããã€ã€ãã¡ã¯ããªãå£ããchalk.hex('#FF6B35') ã¯ãã®ãšã æåå '#FF6B35' ã§ããããããåŒã¶ãš TypeError: ... is not a function ãæããããã¬ããã¯ã¢ãžã¥ãŒã«ããŒãæã«æ§ç¯ãããããããã®ãã©ãŒã«ããã¯ã¯ã€ã³ããŒãæç¹ã§ããŒã«ãèœãšã â ãã¬ãŒã³ããã¹ããžã®å£åãããç®çã ã£ããŸãã«ãã®ç¶æ³ã§ã
then ã undefined: ãã¹ãŠã®ããããã£ã«é¢æ°ãè¿ãã¹ã¿ã㯠await chalk ãæ°žä¹
ã«ãã³ã°ããããã©ã³ã¿ã€ã ã .then ãåŒã³ã誰ãåŒã°ãªãã³ãŒã«ããã¯ãåŸ
ã€ãNode 㯠Detected unsettled top-level await ãå ±åã㊠13 ã§çµäºããã
level ãæ°å€: èœåã²ãŒã㯠chalk.level >= 1 ãèªããçå€ã®ã¹ã¿ãã¯èåŸã«ã«ã©ãŒãµããŒãããªããŸãŸãããéããŠããŸãã
ãã®ã€ã³ããŒããçãå»¶ã³ããªããžã§ã¯ããããã¬ãããæ§ç¯ããã
æšæºãã¬ããïŒãã©ã³ã¶ã¯ã·ã§ã³åºåïŒ:
const ok = chalk.green;
const fail = chalk.red;
const warn = chalk.yellow;
const info = chalk.cyan;
const dim = chalk.dim;
const bold = chalk.bold;
æž©ãããã¬ããïŒã»ã¬ã¢ããŒ/ç©èªåºåïŒ:
const C = {
flame: chalk.hex('#FF6B35'),
amber: chalk.hex('#FFB347'),
spark: chalk.hex('#FFF4E0'),
ember: chalk.hex('#8B4513'),
warm: chalk.hex('#D4A574'),
dim: chalk.dim,
fail: chalk.red,
};
ãã¬ããèšèšã«ãŒã«:
- åžžã« no-color ãã©ãŒã«ããã¯ãæäŸãããã¬ãããå®éã«äœ¿ãåŒã³åºã圢ã«å¯ŸããŠæ€æ»ãã â äžã®æž©ãããã¬ããã¯ã»ãŒãã¹ãŠãã¡ã¯ããªã§ãã
- ã«ã¹ã¿ã ãã¬ããã«ã¯ hex ã«ã©ãŒã䜿ãïŒ
chalk.hex('#FF6B35')ïŒ
- ãã¬ããããŒãã«é¢ä¿ãªã fail/error ã«ã©ãŒã¯èµ€ãä¿ã€
- ãã¬ãããšã³ããªã¯èŠãç®ã§ã¯ãªãæå³ç圹å²ã§åœåãã
- ã¹ã¿ãã¯åã€ã³ããŒãç®æã§äœãçŽãã®ã§ã¯ãªãã¢ãžã¥ãŒã«éã§ 1 ã€ãå
±æããããããªããã°åãæ¬ é¥ãã³ããŒããšã«èŠã€ããŠçŽãããšã«ãªã
æåŸ
çµæïŒ ååä»ããšã³ããªãæã€ãã¬ãããªããžã§ã¯ããšãåã«æžãããã ãã§ãªãå®è¡ããããã©ãŒã«ããã¯ã
倱ææïŒ ãã©ãŒã«ããã¯ãã¹ãçŽæ¥å®è¡ãããå£ããŠããããšããã¬ããã§çºèŠããã®ã¯ééã£ãå Žæã§ãããã¹ã¿ããã¹ã³ãŒãã«ããç¶æ
ã§:
console.assert(chalk.dim('x') === 'x');
console.assert(chalk.hex('#fff')('x') === 'x');
console.assert(chalk.bold.cyan('x') === 'x');
console.assert(chalk.level === 0);
await chalk;
NO_COLOR=1 ã¯ãããã«ããŒããªããããã¯ãšã¹ã±ãŒããåºããªãããšãéžã¶ åäœããŠãã chalk ãå®è¡ãããããã©ãŒã«ããã¯ã¯ã€ã³ããŒãã«å€±æãã chalk ãå®è¡ããã2 ã€ã®ãã¹ã¯ã³ãŒããå
±æããªããæ³šéä»ãã®æ¬çªã¹ã¿ããæ¬ é¥ã®åçŸãäžèšãã§ãã¯ã®å®è¡å¯èœçã¯æ¡åŒµäŸãåç
§ã
ã¹ããã2: ã¹ããŒã¿ã¹ã€ã³ãžã±ãŒã¿ãŒãéžã¶
ã¹ããŒã¿ã¹éä¿¡ã®ããã® Unicode ã°ãªããŸã㯠ASCII æåãéžã¶:
ASCIIïŒæå€§äºææ§ïŒ:
+ created/installed (green)
- removed/deleted (red)
= skipped/unchanged (dim)
! error/warning (red)
UnicodeïŒããè±ããUTF-8 ã¿ãŒããã«ãå¿
èŠïŒ:
⊠item/skill/practice (spark)
â active/burning state
â cooling/embers state
â cold/dormant state
â available/not installed
â failed item
â success (use sparingly â not all terminals render it well)
éžæåºæº:
- CI ãŸãã¯ãã€ãæèã§åãããŒã«ã«ã¯ ASCII
- ã€ã³ã¿ã©ã¯ãã£ããªã¿ãŒããã«ãŠãŒã¶ãŒãããããŒã«ã«ã¯ Unicode
--ascii ãã©ã°ãŸã㯠NO_COLOR æ€åºãä»ããŠäž¡æ¹ãæäŸ
- ã°ãªãããã¹ã: macOS TerminalãWindows TerminalãVS Code terminalãSSH ã»ãã·ã§ã³
æåŸ
çµæïŒ ã«ã©ãŒã ãã«é Œããã«äžç®ã§ã¹ããŒã¿ã¹ãäŒããã°ãªãã»ããã
倱ææïŒ ãã¹ãã§ã°ãªãã ? ãŸãã¯ç®±ãšããŠæç»ãããããASCII ç䟡ç©ã«çœ®ãæããã+/-/=/! ã»ããã¯ã©ãã§ãåãã
ã¹ããã3: åé·åºŠã¬ãã«ãèšèšãã
ãã¹ãŠã®ã³ãã³ã㯠4 ã€ã®åºåã¬ãã«ããµããŒããã¹ã:
| Level | Flag | Audience | Content |
|---|
| Default | (none) | Human at terminal | Formatted, colored, informative |
| Verbose | --verbose or --ceremonial | Human wanting detail | Per-item breakdown, arrival sequences |
| Quiet | --quiet | Scripts, CI | Minimal lines, status icons, no decoration |
| JSON | --json | Machine consumers | Structured, parseable, complete |
å®è£
ãã¿ãŒã³:
function output(data, options) {
if (options.json) {
console.log(JSON.stringify(data, null, 2));
return;
}
if (options.quiet) {
for (const item of data.items) {
const icon = item.ok ? '+' : '!';
console.log(`${icon} ${item.id}`);
}
return;
}
printFormatted(data, { verbose: options.verbose });
}
JSON åºåã«ãŒã«:
- åžžã«æå¹ãª JSONïŒäººéããã¹ããšæ··åšãããªãïŒ
- 人éåºåã瀺ããã¹ãŠã®ããŒã¿ + ãã·ã³æçšãã£ãŒã«ããå«ã
- ã³ãã³ãéã§äžè²«ããããŒåœåã䜿ã
- åºåã¢ãŒãã«é¢ä¿ãªããæåã¯çµäºã³ãŒã 0ããšã©ãŒã¯ 1
æåŸ
çµæïŒ ã³ãã³ãéã§äžè²«ããæåãæã€ 4 ã€ã®æç¢ºãªåºåã¬ãã«ã
倱ææïŒ verbose ã¢ãŒãããããããããªããæ®µéçåé·åºŠã¬ãã«ã§ã¯ãªããªããã€ã³ïŒ--ceremonialïŒã«ããã
ã¹ããã4: ãã€ã¹ã«ãŒã«ã確ç«ãã
ãã¹ãŠã®åºå颿°ãåŸãã¹ãããŒã³ãšã¹ã¿ã€ã«ãå®çŸ©ããããããã³ãã³ãéã®äžæŽåãé²ãã
ãã€ã¹ã«ãŒã«äŸïŒcampfire ã¬ããŒã¿ãŒããïŒ:
- çŸåšåœ¢ãèœåæ
: ãmystic arrivesãããmystic has been installedãã§ã¯ãªã
- æå笊ãªã: éããªèªä¿¡ãããŒã«ã¯å«ã°ãªãã
- ã¡ã¿ãã¡ãŒã§å°éçšèªã眮æ: ãpracticesãããdependenciesãã§ã¯ãªãïŒã»ã¬ã¢ããŒã¢ãŒãã®ã¿ïŒ
- 倱æã¯æ£çŽã«ãç Žæ»
çã«ã§ã¯ãªã: ãA spark was lostãããERROR: installation failed with exit code 1ãã§ã¯ãªã
- çµããã®è¡ãç¶æ
ãåæ : ãã¹ãŠã®æäœã¯ã¹ããŒã¿ã¹æŠèŠã§çµãã
- çµµæåãªã: Unicode ã°ãªãã¯è£
食çã§ãªãèŠèŠçéã¿ãæã€
- ãã¹ãŠã®åèªãæ
å ±ãéã¶: åèªãçè§£ãå ããªããªããåé€ãã
æšæºïŒéã»ã¬ã¢ããŒïŒåºåã®ãã€ã¹ã«ãŒã«:
- ç°¡æœãªäºå®ã®è¡
- ã¹ããŒã¿ã¹ã¢ã€ã³ã³ + ã¢ã€ãã ID + æè
- ã«ãŠã³ãä»ãæŠèŠè¡
- ãšã©ãŒã¡ãã»ãŒãžã¯æ¯æ£æªçœ®ãææ¡ãã
æåŸ
çµæïŒ åºå颿°ãåŸãã¹ã 3-7 ã®ãã€ã¹ã«ãŒã«ã®æžãèµ·ããã»ããã
倱ææïŒ ã«ãŒã«ãæ£æçã«æãããããªãããã¹ããã: åã«ãŒã«ãã/ãªãã§åãåºåãæžããã«ãŒã«ãé€ããŠãåºåå質ãå€ãããªããªãããã®ã«ãŒã«ã¯äžèŠã
ã¹ããã5: ã¬ããŒã¿ãŒé¢æ°ãå®è£
ãã
çŠç¹ãçµã£ã颿°ãæã€ã¬ããŒã¿ãŒã¢ãžã¥ãŒã«ã«åºåãæŽçãã:
export function printResults(results) { ... }
export function printItemTable(items) { ... }
export function printDetections(detections) { ... }
export function printAudit(auditResults) { ... }
export function printDryRun() { ... }
export function warn(msg) { ... }
export function error(msg) { ... }
export { chalk };
å颿°ã¯åãæ§é ã«åŸã:
- 空/null å
¥åãåªé
ã«æ±ã
- ã¬ã€ã¢ãŠããèšç®ïŒã«ã©ã å¹
ãããã£ã³ã°ïŒ
- ãã¬ããè²ã§åºå
- äžçªäžã«æŠèŠè¡
ã»ã¬ã¢ããŒåºåã«ã¯å¥ã¢ãžã¥ãŒã«ãäœã:
export function printArrival({ teamId, agents, results, ceremonial }) { ... }
export function printScatter({ teamId, agents, results }) { ... }
export function printTend(fires) { ... }
export function printCampfireList({ teams, state, reg }) { ... }
export function printFireSummary({ team, fireData, reg }) { ... }
export function printJson(data) { ... }
æåŸ
çµæïŒ ç¬ç«ããŠäœ¿ããã¬ããŒã¿ãŒé¢æ° â åã
ã¯åŒã³åºãå
ç¶æ
ã«äŸåããèªèº«ã®ãã©ãŒããããæ±ãã
倱ææïŒ 颿°ã ~50 è¡ãè¶
ããŠæé·ããããã«ããŒãæœåºãããã¬ããŒã¿ãŒé¢æ°ã¯åç¬ã§ã¬ãã¥ãŒããããããã¹ãã
ã¹ããã6: ç°å¢éã§åºåããã¹ããã
ç°ãªãæèã§åºåãæ£ããæç»ãããããšãæ€èšŒãã:
node cli/index.js list --domains
node cli/index.js list --domains | cat
NO_COLOR=1 node cli/index.js list --domains
node cli/index.js campfire --json | jq .
CI=true node cli/index.js audit
node --test 'cli/test/*.test.js'
確èªäºé
:
- ã€ã³ã¿ã©ã¯ãã£ãã¢ãŒãã§ã«ã©ãŒãæ£ãã衚瀺ããã
- ãã€ã/ãªãã€ã¬ã¯ãåºåã« ANSI ãšã¹ã±ãŒãã³ãŒããæŒããªã
- JSON ãæå¹ïŒ
jq . ã«ãã€ãããŠæ€èšŒïŒ
- ã¿ãŒã²ããã¿ãŒããã«ã§ Unicode ã°ãªããæç»ããã
- å
容å¹
ãå€åããŠãã«ã©ã æŽåãä¿ããã
- no-color ãã©ãŒã«ããã¯ããã¬ããã䜿ããã¹ãŠã®åŒã³åºã圢ã«å¿çããæäœæ¥ã§äžåºŠç€ºãã®ã§ã¯ãªãã¹ã€ãŒãã§ã¢ãµãŒããããŠãã
æåŸ
çµæïŒ 6 ã€ã®æèãã¹ãŠã§åºåãæ£ããã
倱ææïŒ ANSI ã³ãŒããæŒããã chalk ã NO_COLOR ãå°éããã確èªãããUnicode ãå£ããã ASCII ãã©ãŒã«ããã¯ã¢ãŒããæäŸãããç·ã®ã¹ã€ãŒãã¯ã«ã©ãŒã«ã€ããŠã©ã¡ãã®æå³ã§ãäœãèªããªãããšã«æ³šæ: ãã¹ãã©ã³ããŒã¯ stdout ããã€ããããã chalk.level 㯠0 ã«ãªããã«ã©ãŒåºåãšéã«ã©ãŒåºåã¯ãã€ãåäœã§åäžã«ãªã£ãŠãã«ã©ãŒãå®å
šã«å£ããŠããŠãã¢ãµãŒã·ã§ã³ã¯éããã«ã©ãŒãåäœããããšã蚌æããã«ã¯ FORCE_COLOR=3 ãšãšã¹ã±ãŒãã·ãŒã±ã³ã¹ã«å¯Ÿããã¢ãµãŒã·ã§ã³ãå¿
èŠã§ããã
ããªããŒã·ã§ã³
ããããèœãšã穎
- çŽæ¥ã¹ã¿ã€ã«ããæ±ããªã no-color ãã©ãŒã«ããã¯:
new Proxy({}, { get: () => (s) => s }) ã¯å®å
šãªãã®ã«èŠããchalk.dim ãš chalk.red ã¯å®éã«ã«ããŒãããããã®ãšããã¹ãŠã®ãã¡ã¯ããªã¯åŒã³åºãå
ãçŽåŸã«åŒãŒããšããæååãè¿ãããã¬ããã¯ã¢ãžã¥ãŒã«ããŒãæã«æ§ç¯ããããããTypeError ã¯ã€ã³ããŒãæç¹ã§çºçãã â ãã©ãŒã«ããã¯ã¯ãããååšããå¯äžã®å Žé¢ã§æãæ¿ãã倱æãããã¹ã¿ããæºããã¹ã 4 ã€ã®äžå€æ¡ä»¶ã¯ã¹ããã1ã«åæãããŠããã
- 人éããã¹ããš JSON ãæ··ãã:
--json ã¢ãŒãã§ã¯æå¹ JSON ã®ã¿ãåºåãäžè¡ã§ãæ··ããïŒãDRY RUNããªã©ïŒãš JSON ããŒãµãŒãå£ãããã³ãã³ããäž¡æ¹ã衚瀺ããªããã°ãªããªããªããæç¢ºã«åé¢ããã JSON ã¢ãŒãã§äººéããã¹ããæå¶ããã
- ããŒãã³ãŒããããã«ã©ã å¹
: å
容é·ã¯å€åããã
Math.max(...items.map(i => i.id.length)) ã䜿ã£ãŠããã£ã³ã°ãåçã«èšç®ããã
- æå³ã®ãªãã«ã©ãŒ: ã«ã©ãŒãæåãšå€±æãåºå¥ããå¯äžã®æ¹æ³ãªããè²èŠå€æ§æ§ãŠãŒã¶ãŒãšãã€ãåºåã¯æ
å ±ã倱ããåžžã«ã«ã©ãŒãããã¹ãã€ã³ãžã±ãŒã¿ãŒïŒ
+ãOKãERRïŒãšçµã¿åãããã
- 誀ã£ãæèã§ã®ã»ã¬ã¢ããŒ: æž©ããç©èªåºåã¯ã€ã³ã¿ã©ã¯ãã£ãã¿ãŒããã«ã»ãã·ã§ã³ã«é©ããŠãããCIãã¹ã¯ãªããã
--quiet ã¢ãŒãã§ã¯ãã€ãºã«ãªããã»ã¬ã¢ããŒåºåã¯æç€ºçãã©ã°ã§ã²ãŒãããã
- æŠèŠè¡ãå¿ãã: ãŠãŒã¶ãŒã¯æåŸã®è¡ãæåã«ã¹ãã£ã³ããããã¹ãŠã®æäœã¯äžè¡ã®æŠèŠïŒæå/倱æ/ã¹ãããã®ã«ãŠã³ãïŒã§çµããã¹ãã
é¢é£ã¹ãã«
scaffold-cli-command â ãã®åºåã䜿ãã³ãã³ã
test-cli-application â åºåãæåŸ
ã«äžèŽããããšã®ãã¹ã
build-cli-plugin â ãã©ã°ã€ã³ã¯ãã®åºåã·ã¹ãã ãéããŠçµæãã¬ããŒããã