---
description: User-messaging, spinner, error, and exit-code conventions for adding or modifying rp CLI commands
alwaysApply: true
---

When adding a new `rp` command or changing an existing one, follow the conventions in `CLAUDE.md` at the repo root. The non-negotiables:

- Register commands via `.action(actionRunner(fn))` in `src/index.ts` — never bypass the central error handling/usage logging.
- Wrap async/network steps in `runWithSpinner('Doing X...', fn)` (`src/helpers/spinner.ts`); spinner text is present-progressive ending in `...`.
- Prefix status lines with `symbols.*` from `src/helpers/symbols.ts` — never hardcode emoji (ASCII fallback for CI/Windows depends on it).
- Chalk semantics: green = success, blue = identifiers/versions, yellow = warnings + echoed commands/filenames, red = central error handler only, gray = secondary detail.
- Failures must exit non-zero: throw `CLIError(message, ExitCodes.GENERAL_ERROR)` — never `process.exit(0)` after printing an error. API errors surface as `PlatformError`; add `Tip:` mappings in `PlatformError.getSuggestion` for actionable cases.
- Destructive commands take `-f, --force` (skips prompts only, never validation), warn in yellow before the `ask.yesNo` prompt ending `(y/n)? `, and on "no" print `Aborting <verb>. When you are ready, run the "rp <cmd>" command again.` and return without throwing.
- Pre-check "nothing to do / user error" states before the prompt and throw a `CLIError` naming the next command in backticks (pattern: `src/actions/publish.ts`).
- Success lines: green, past tense, single-quoted identifiers, `\n`-prefixed.
- Every command change ships a `src/actions/__tests__/<cmd>.test.ts` (mocha/sinon, stub `readAuthAndConfig` + helpers, `expect` from `test-utils`) asserting the messaging contract: error class, exit code, message content, and stubs `notCalled` on abort paths.

See `CLAUDE.md` for the full rules with examples.
