AI-native workflow
Most CLIs written this year and next will be authored by an AI assistant. Caret was designed with that in mind — every component has a spec, every token is named, every error is structured. The caret.md file ties it together: a single instruction document that tells Claude, Cursor, Codex, or Copilot exactly how to use Caret on the first try.
How it works
When you scaffold with caret init, Caret writes a caret.md at your repo root. It's a short, opinionated rule book — not documentation. AI assistants read it before generating CLI code and produce output that uses Caret components instead of chalk, ora, console.log, or whatever else is in their training data.
An example
Without caret.md, asking an LLM to "add a deploy command with a spinner and an error message" typically produces something like:
import chalk from 'chalk'
import ora from 'ora'
const spinner = ora('Deploying').start()
try {
await deploy()
spinner.succeed(chalk.green('Done'))
} catch (e) {
spinner.fail(chalk.red('Error: ' + e.message))
}With caret.md in the repo:
import { spinner, error } from './caret'
await spinner('Deploying', async () => {
await deploy()
}, { onSuccess: 'Deployed' })The second one is a Caret CLI. It respects NO_COLOR, falls back on dumb terminals, and prints structured errors with hint: and see: URLs — without you asking for any of it.
Rules in caret.md
The hard rules caret.md enforces:
- Don't import
chalk,kleur,picocolors,ora,cli-spinners,enquirer,prompts,clack,inquirer,cli-table. Caret owns those layers. - Use
error()notthrowat the CLI boundary. Errors are display, not control flow. - Wrap long-running async work in
spinner('label', fn). - stdout is for data, stderr is for messages. Caret components route automatically; never
console.loga status message. - Never customize Caret symbols.
^ ▸ ● ○ ✓ ✗ ⚠ — │are the brand. - Never set the terminal background.
- Never use the terminal bell. Use
caret.notifyfor system notifications.
Component catalog as a contract
caret.md includes a cheat sheet for every component — exact signature, common props, idiomatic call site. AI assistants treat it as the ground truth and don't have to invent prop names.
// Excerpt from the cheat sheet:
await spinner('Building', async () => {
await build()
}, { onSuccess: 'Built', onFailure: 'Build failed' })
error('Deploy failed', {
body: 'Vercel returned 401.',
hint: 'Run my-cli login to refresh your token.',
see: 'https://my-cli.dev/docs/auth',
})
const region = await prompt.select({
label: 'Region',
options: [
{ value: 'us-east-1', label: 'US East' },
{ value: 'eu-west-1', label: 'Europe' },
],
})Adding rules to caret.md
Project-specific conventions belong in the same file. Append your own rules — error code prefixes, custom theme colors, a domain glossary. AI assistants will read them on every interaction.
Continue with Principles for the philosophy the rules in caret.md follow, or browse the component catalog to see the cheat sheet applied at scale.