用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/agents-inc/skills --skill cli-prompts-clack命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | cli-prompts-clack |
| description | Beautiful interactive CLI prompts with @clack/prompts and custom prompts with @clack/core |
Quick Guide: Use
@clack/promptsfor pre-styled interactive CLI prompts (text, select, multiselect, confirm, spinner, progress). CheckisCancel()after EVERY prompt call -- users can Ctrl+C at any point.cancel()only prints; exit after it, with a non-zero code. Usegroup()for multi-step flows with centralized cancellation. Use@clack/coreonly when building fully custom prompt UIs. ESM-only since v1.0.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST check isCancel() after EVERY prompt call -- skipping this causes silent crashes when users press Ctrl+C)
(You MUST exit the process after cancel() -- the cancel message prints but execution continues otherwise)
(You MUST exit with a NON-ZERO code on cancellation, taking the value from the CLI framework's exit-code table where one exists and using 130 where none does -- exiting 0 tells every caller the work succeeded)
(You MUST use group() with onCancel for multi-step flows -- it handles cancellation centrally so you don't check each prompt individually)
(You MUST call spinner.stop() before any other output -- overlapping spinner output with prompts or logs corrupts the terminal)
</critical_requirements>
Auto-detection: @clack/prompts, @clack/core, clack, isCancel, intro, outro, cancel, spinner, group, text prompt, select prompt, confirm prompt, multiselect, groupMultiselect, selectKey, note, log, tasks, progress, taskLog, stream, box, autocomplete, date prompt, path prompt, updateSettings
When to use:
When NOT to use:
y/n confirmation that doesn't need styling (plain readline suffices)Key patterns covered:
Clack provides beautiful, minimal CLI prompts with zero configuration. The @clack/prompts package gives you pre-styled components that look great out of the box. Every prompt returns a value or a cancel symbol -- the core discipline is always checking for cancellation.
Two packages, two purposes:
@clack/prompts -- Pre-styled, opinionated prompts. Use this for 95% of cases.@clack/core -- Unstyled primitives with a render() function. Use only when you need a completely custom prompt UI.Key design principles:
value | symbol -- the symbol indicates cancellationintro/outro) create visual grouping in the terminalgroup() composes multiple prompts with shared cancellation handlingsignal: AbortSignal for programmatic cancellationEvery clack CLI flow starts with intro() and ends with outro(). The critical pattern is checking isCancel() after every prompt call.
import * as p from "@clack/prompts";
// 128 + SIGINT(2), the value a shell reports for an interrupted command.
// Use only when no CLI framework owns an exit-code table -- see below.
const EXIT_CANCELLED = 130;
p.intro("Project setup");
const name = await p.text({ message: "Project name?" });
if (p.isCancel(name)) {
p.cancel("Setup cancelled.");
process.exit(EXIT_CANCELLED);
}
// name is now narrowed to string (not symbol)
p.outro(`Created ${name}`);
Why good: isCancel check narrows the type from string | symbol to string, cancel prints a styled message, the exit stops dangling execution, the non-zero code stops callers from reading an abandoned run as a completed one
// BAD: Missing isCancel check
const name = await p.text({ message: "Project name?" });
console.log(`Created ${name}`); // name could be a symbol -- crashes or prints "[Symbol]"
Why bad: if user presses Ctrl+C, name is a symbol, not a string -- string operations on it will crash or produce garbage output
cancel() prints and returns; it never stops the process. What you exit with is a separate decision, and it is not this library's to make:
| Situation | Exit with |
|---|---|
| The CLI framework defines an exit-code table | That table's cancellation constant. It is the authority; do not override. |
| A standalone script with no such table | 130 -- 128 + SIGINT(2), what a shell reports for an interrupted command |
| Never | 0 |
Why never 0: 0 means success, and cli && deploy, set -e, CI steps and every other caller act on exactly that. A user who pressed Ctrl+C halfway through setup did not succeed, so a 0 exit hands the next command a half-configured project and no signal that anything went wrong. Upstream examples showing process.exit(0) are illustrating that you must exit at all -- the point they make is about the missing exit, not about the value.
group() chains multiple prompts and handles cancellation in one place. Each prompt receives previous results.
import * as p from "@clack/prompts";
const project = await p.group(
{
name: () => p.text({ message: "Project name?", placeholder: "my-app" }),
framework: ({ results }) =>
p.select({
message: `Framework for ${results.name}?`,
options: [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "svelte", label: "Svelte" },
],
}),
install: () => p.confirm({ message: "Install dependencies?" }),
},
{
onCancel: () => {
p.cancel("Setup cancelled.");
process.exit(EXIT_CANCELLED);
},
},
);
// project is typed: { name: string; framework: string; install: boolean }
Why good: centralized onCancel eliminates per-prompt isCancel checks, results are typed as an object, each prompt can reference previous results via results, the single exit point means the cancellation code is decided once for the whole flow
See examples/core.md for complete group patterns with validation and conditional prompts.
Spinners show activity during async work. Always stop the spinner before printing other output.
import * as p from "@clack/prompts";
const s = p.spinner();
s.start("Installing dependencies");
await installDeps();
s.stop("Dependencies installed");
Progress bar extends spinner with incremental tracking:
const MAX_STEPS = 100;
const prog = p.progress({ max: MAX_STEPS, style: "heavy" });
prog.start("Processing files");
for (const file of files) {
await processFile(file);
prog.advance(1, `Processed ${file.name}`);
}
prog.stop("All files processed");
Why good: spinner and progress provide visual feedback, stop message replaces the spinner line cleanly
See examples/core.md for spinner error handling, cancellation with AbortSignal, and tasks runner.
All input prompts accept a validate function. Return a string to show an error, or undefined to accept.
const MIN_LENGTH = 2;
const MAX_LENGTH = 50;
const name = await p.text({
message: "Package name?",
validate: (value) => {
if (!value || value.length < MIN_LENGTH)
return `Name must be at least ${MIN_LENGTH} characters`;
if (value.length > MAX_LENGTH)
return `Name must be at most ${MAX_LENGTH} characters`;
if (!/^[a-z0-9-]+$/.test(value))
return "Name must be lowercase alphanumeric with hyphens";
},
});
Why good: validation runs inline before the prompt resolves, user sees the error immediately and can retry, named constants for limits
See examples/core.md for validation patterns on different prompt types.
Clack provides styled output functions that match the prompt theme.
import * as p from "@clack/prompts";
// Logging with state symbols
p.log.info("Checking configuration...");
p.log.success("Configuration valid");
p.log.warn("Missing optional field: description");
p.log.error("Invalid config file");
p.log.step("Step 1 complete");
// Boxed note for important information
p.note("Run `npm start` to begin development", "Next steps");
// Styled box
p.box("v1.0.0 released!", "Announcement", {
contentAlign: "center",
rounded: true,
});
Why good: themed output matches prompt styling, note/box draw attention to important information
Sequential tasks with automatic success/failure messaging.
import * as p from "@clack/prompts";
await p.tasks([
{
title: "Downloading template",
task: async () => {
await downloadTemplate();
return "Template downloaded";
},
},
{
title: "Installing dependencies",
task: async (message) => {
message("Resolving packages...");
await installDeps();
return "Dependencies installed";
},
},
]);
Why good: tasks display spinner per item, return value becomes the completion message, message() callback updates spinner text mid-task
See examples/core.md for error handling in tasks and taskLog for detailed output.
Detailed Resources:
<decision_framework>
Need user input?
|
+-> Single value?
| +-> Free text -> text() or password()
| +-> One of N choices -> select() (list) or selectKey() (keyboard shortcut)
| +-> Yes/No -> confirm()
| +-> Date -> date()
| +-> File path -> path()
|
+-> Multiple values?
| +-> Flat list -> multiselect()
| +-> Grouped categories -> groupMultiselect()
| +-> Searchable -> autocomplete() or autocompleteMultiselect()
|
+-> Multiple prompts in sequence?
+-> group() with onCancel for centralized handling
Need to show progress?
|
+-> Indeterminate wait -> spinner()
+-> Known total steps -> progress()
+-> Sequential tasks -> tasks()
+-> Detailed logs per task -> taskLog()
Need styled output?
|
+-> Status message -> log.info/warn/error/success/step()
+-> Important notice -> note() or box()
+-> Streaming content -> stream.info/warn/error/success()
</decision_framework>
<red_flags>
High Priority Issues:
isCancel() check after a prompt -- the return value is value | symbol, and using the symbol as a string crashes or produces garbage. Always check before using the value.process.exit() after cancel() -- cancel() only prints a message, it does not stop execution. The process continues running.0 after cancel() -- a cancelled run reports success to every caller, so cli && next-step runs the next step against a half-finished state. Exit non-zero: the framework's cancellation constant, or 130 when there is no framework table.spinner.stop() first.require() with @clack/prompts v1.0+ -- the package is ESM-only since v1.0. Use import syntax.Medium Priority Issues:
group() for multi-step flows -- checking isCancel() after every single prompt is verbose and error-prone. group() with onCancel centralizes this.validate option -- prompts accept invalid input by default. Add validation for any input that has constraints.multiselect without required: false when zero selections should be valid -- by default, at least one item must be selected.Gotchas & Edge Cases:
isCancel() returns true for the cancel symbol but also narrows the TypeScript type -- always use it as a type guard before accessing the valuespinner() returns an object, not a promise -- call .start() separatelygroup() prompt functions receive { results } with all previously collected values, but TypeScript types each value as possibly undefined since earlier prompts might not have run yetconfirm() returns boolean | symbol, not just boolean -- still needs isCancel() check when used outside group()select() generic type parameter controls the return type -- select<"react" | "vue">({...}) narrows the resultlog.warn has an alias log.warning -- both work identicallyprogress.advance() with no arguments advances by 1 -- the step parameter is optionalnote() and box() are synchronous (not prompts) -- they return void, not promisessignal: AbortSignal for programmatic cancellation (e.g., timeouts)updateSettings() applies globally -- call it once at startup, not per promptpicocolors with Node.js built-in styleText -- requires Node.js 20.12+</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST check isCancel() after EVERY prompt call -- skipping this causes silent crashes when users press Ctrl+C)
(You MUST exit the process after cancel() -- the cancel message prints but execution continues otherwise)
(You MUST exit with a NON-ZERO code on cancellation, taking the value from the CLI framework's exit-code table where one exists and using 130 where none does -- exiting 0 tells every caller the work succeeded)
(You MUST use group() with onCancel for multi-step flows -- it handles cancellation centrally so you don't check each prompt individually)
(You MUST call spinner.stop() before any other output -- overlapping spinner output with prompts or logs corrupts the terminal)
Failure to follow these rules will cause silent process hangs, corrupted terminal output, and runtime crashes on user cancellation.
</critical_reminders>