Created
May 14, 2026 20:46
-
-
Save alexcarpenter/def00176061721dea279627e56fc7dc3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| Clerk CLI vs Notion CLI Design: Key Differences | |
| Things you already do well (matching or exceeding Notion's guidelines) | |
| - Noun-verb structure -- you're already there (clerk users list, clerk apps create) | |
| - stdout vs stderr separation -- your log.data() / log.info() split is clean and enforced by lint rules | |
| - Non-TTY / agent mode -- your mode detection is actually more sophisticated than Notion's (explicit --mode flag, CLERK_MODE env var, auto-detection, structured JSON error | |
| output for agents) | |
| - Confirmation defaults -- destructive prompts default to false, --yes skips them. Matches exactly. | |
| - Shell completions -- dynamic completions with description annotations. Already there. | |
| - Config precedence -- flag > env var > linked project > config file. Matches the layered model. | |
| - --dry-run on mutating commands -- Notion doesn't even mention this; you're ahead here. | |
| - --input-json for full command options as JSON -- a nice touch Notion lacks. | |
| --- | |
| Gaps worth considering | |
| 1. No --plain output mode | |
| Notion offers three tiers: human (aligned columns), --plain (tab-separated, no headers for cut/awk), and --json. You have human and --json but no --plain. This matters for shell | |
| scripts that don't want to pull in jq just to extract a column. | |
| Effort: Low. Add a --plain flag that outputs TSV without headers. | |
| 2. NO_COLOR is not respected in your own output | |
| Your color.ts emits raw ANSI escapes unconditionally. Inquirer respects NO_COLOR via node:util, but your log.success(), log.warn(), log.error(), and table output do not. This | |
| violates the NO_COLOR spec and can produce garbled output in terminals/tools that don't support ANSI. | |
| Effort: Low. Wrap your color helpers to check process.env.NO_COLOR or !process.stderr.isTTY before emitting escape codes. | |
| 3. No UNIX-style verb aliases (ls, rm) | |
| Notion aliases list → ls, delete → rm, etc. Your aliases are limited to auth synonyms (signup, signin). Power users expect clerk users ls to work. | |
| Effort: Low. Add .alias('ls') to every list subcommand, .alias('rm') to delete subcommands. | |
| 4. Error hints are URLs, not inline actionable text | |
| Notion pairs errors with inline hint: lines that tell you exactly what to run: | |
| error: No workspace selected. | |
| hint: Run `ntn login` first, or set NOTION_WORKSPACE_ID. | |
| Your errors attach a docsUrl but don't consistently print an inline "run this command" hint. The user has to click through to docs to figure out the fix. | |
| Effort: Medium. Add an optional hint: string field to CliError and print it inline below the error message. You already do this in some API error formatting (formatApiBody), but | |
| it's not systematic. | |
| 5. No short help vs long help distinction | |
| Notion uses -h for short help (just args + one-liner) and --help for the full version with env var tables and usage notes. You have a single --help level. For a growing CLI, | |
| short help keeps the common case fast and the full docs discoverable. | |
| Effort: Medium. Commander supports .helpOption() customization, but splitting into two tiers requires a custom help formatter change (you already have one in help.ts). | |
| 6. No --verbose for error diagnostics | |
| Notion shows the causal chain (caused by:) only with --verbose. Your errors either show the user-facing message or the full structured JSON (agent mode). There's no middle | |
| ground for a human who wants to debug a network failure without switching to agent mode. | |
| Effort: Low. Add a global --verbose flag. In the error handler, print the error's .cause chain when set. | |
| 7. Exit codes are coarse | |
| Notion uses differentiated exit codes: 0 success, 1 internal, 2 usage, 4 auth, 5 network/API. You use 0, 1, 2, 130. Auth failures and API errors both exit 1, which makes it | |
| harder for scripts to branch on failure mode. | |
| Effort: Low. You already have AuthError and ApiError as distinct types. Map them to distinct exit codes in the global handler. | |
| 8. Emoji in output | |
| Notion says "no emoji in output." Your CLI uses checkmarks (✔) and other symbols in success/status messages. This is a stylistic choice -- your usage is restrained and | |
| functional, not "cute." But it's a philosophical difference worth being intentional about. Emoji can cause rendering issues in some terminals and log viewers. | |
| Consideration: Keep or drop, but decide deliberately. If you keep them, make sure they're suppressed in agent mode and NO_COLOR contexts. | |
| --- | |
| Summary: Priority recommendations | |
| ┌──────────┬──────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐ | |
| │ Priority │ Change │ Why │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ High │ Respect NO_COLOR in color.ts │ Spec compliance, broken output in some tools │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ High │ Inline hint: text on errors │ Biggest UX win -- users shouldn't need to click a URL to know what to run next │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ Medium │ Add --verbose for error chains │ Debugging without agent mode │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ Medium │ Differentiate exit codes (auth=4, API=5) │ Script/CI branching │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ Low │ Add --plain output mode │ Shell script ergonomics │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ Low │ Add ls/rm verb aliases │ Power user speed │ | |
| ├──────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ | |
| │ Low │ Short vs long help │ Nice-to-have as command count grows │ | |
| └──────────┴──────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘ | |
| The biggest takeaway: your CLI is already well-architected. The agent mode system is more mature than what Notion describes. The main gaps are around the "polish for power users | |
| and scripts" layer -- NO_COLOR, inline hints, --verbose, richer exit codes, and --plain output. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment