@zigrivers/scaffold 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -12
- package/content/knowledge/backend/backend-api-design.md +103 -0
- package/content/knowledge/backend/backend-architecture.md +100 -0
- package/content/knowledge/backend/backend-async-patterns.md +101 -0
- package/content/knowledge/backend/backend-auth-patterns.md +100 -0
- package/content/knowledge/backend/backend-conventions.md +105 -0
- package/content/knowledge/backend/backend-data-modeling.md +102 -0
- package/content/knowledge/backend/backend-deployment.md +100 -0
- package/content/knowledge/backend/backend-dev-environment.md +102 -0
- package/content/knowledge/backend/backend-observability.md +102 -0
- package/content/knowledge/backend/backend-project-structure.md +100 -0
- package/content/knowledge/backend/backend-requirements.md +103 -0
- package/content/knowledge/backend/backend-security.md +104 -0
- package/content/knowledge/backend/backend-testing.md +101 -0
- package/content/knowledge/backend/backend-worker-patterns.md +100 -0
- package/content/knowledge/cli/cli-architecture.md +101 -0
- package/content/knowledge/cli/cli-conventions.md +117 -0
- package/content/knowledge/cli/cli-dev-environment.md +121 -0
- package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
- package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
- package/content/knowledge/cli/cli-output-patterns.md +107 -0
- package/content/knowledge/cli/cli-project-structure.md +124 -0
- package/content/knowledge/cli/cli-requirements.md +101 -0
- package/content/knowledge/cli/cli-shell-integration.md +130 -0
- package/content/knowledge/cli/cli-testing.md +134 -0
- package/content/knowledge/library/library-api-design.md +306 -0
- package/content/knowledge/library/library-architecture.md +247 -0
- package/content/knowledge/library/library-bundling.md +244 -0
- package/content/knowledge/library/library-conventions.md +229 -0
- package/content/knowledge/library/library-dev-environment.md +220 -0
- package/content/knowledge/library/library-documentation.md +300 -0
- package/content/knowledge/library/library-project-structure.md +237 -0
- package/content/knowledge/library/library-requirements.md +173 -0
- package/content/knowledge/library/library-security.md +257 -0
- package/content/knowledge/library/library-testing.md +319 -0
- package/content/knowledge/library/library-type-definitions.md +284 -0
- package/content/knowledge/library/library-versioning.md +300 -0
- package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
- package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
- package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
- package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
- package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
- package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
- package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
- package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
- package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
- package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
- package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
- package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
- package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
- package/content/knowledge/web-app/web-app-architecture.md +116 -0
- package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
- package/content/knowledge/web-app/web-app-conventions.md +121 -0
- package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
- package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
- package/content/knowledge/web-app/web-app-deployment.md +134 -0
- package/content/knowledge/web-app/web-app-design-system.md +158 -0
- package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
- package/content/knowledge/web-app/web-app-observability.md +221 -0
- package/content/knowledge/web-app/web-app-project-structure.md +160 -0
- package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
- package/content/knowledge/web-app/web-app-requirements.md +112 -0
- package/content/knowledge/web-app/web-app-security.md +193 -0
- package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
- package/content/knowledge/web-app/web-app-testing.md +249 -0
- package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
- package/content/methodology/backend-overlay.yml +73 -0
- package/content/methodology/cli-overlay.yml +69 -0
- package/content/methodology/library-overlay.yml +67 -0
- package/content/methodology/mobile-app-overlay.yml +71 -0
- package/content/methodology/web-app-overlay.yml +79 -0
- package/dist/cli/commands/init.d.ts +21 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +261 -13
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +206 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/config/schema.d.ts +1392 -64
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +82 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +302 -1
- package/dist/config/schema.test.js.map +1 -1
- package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
- package/dist/core/assembly/overlay-loader.js +2 -1
- package/dist/core/assembly/overlay-loader.js.map +1 -1
- package/dist/core/assembly/overlay-loader.test.js +56 -0
- package/dist/core/assembly/overlay-loader.test.js.map +1 -1
- package/dist/e2e/game-pipeline.test.js +1 -0
- package/dist/e2e/game-pipeline.test.js.map +1 -1
- package/dist/e2e/project-type-overlays.test.d.ts +16 -0
- package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
- package/dist/e2e/project-type-overlays.test.js +834 -0
- package/dist/e2e/project-type-overlays.test.js.map +1 -0
- package/dist/types/config.d.ts +19 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/wizard/questions.d.ts +27 -1
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +142 -3
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +206 -8
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +21 -0
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +27 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
- package/dist/types/wizard.d.ts +0 -14
- package/dist/types/wizard.d.ts.map +0 -1
- package/dist/types/wizard.js +0 -2
- package/dist/types/wizard.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-dev-environment
|
|
3
|
+
description: Local development setup for CLIs including npm link, cargo install, debug flags, manual testing workflow, and hot reload
|
|
4
|
+
topics: [cli, dev-environment, npm-link, cargo, debug, hot-reload, testing-workflow]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
CLI development has a tighter feedback loop requirement than library development: you need to run the actual binary, observe its output, and verify behavior against real filesystem and network state. Setting up a fast local development workflow is not optional — a slow iteration cycle compounds across hundreds of test invocations.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
CLI development requires a tight feedback loop: install locally via `npm link` (Node), `cargo install --path .` (Rust), or `pip install -e .` (Python), run the actual binary, and verify behavior. Support debug output via environment variables and `--verbose` flags, with debug output always on stderr to protect pipe chains.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### Installing for Local Development
|
|
16
|
+
|
|
17
|
+
**Node.js (npm link)**
|
|
18
|
+
|
|
19
|
+
`npm link` creates a global symlink to the local package, making the CLI available as if installed globally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd my-cli
|
|
23
|
+
npm install
|
|
24
|
+
npm link # Creates symlink in global node_modules
|
|
25
|
+
my-cli --version # Now resolves to local source
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
To unlink: `npm unlink my-cli` (run from the package directory).
|
|
29
|
+
|
|
30
|
+
When using workspaces or monorepos, link from the workspace root and use the scoped package name.
|
|
31
|
+
|
|
32
|
+
**Rust (cargo install --path)**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cargo install --path . # Compiles and installs to ~/.cargo/bin/
|
|
36
|
+
my-cli --version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
During active development, prefer `cargo run -- <args>` to avoid reinstall overhead on each change.
|
|
40
|
+
|
|
41
|
+
**Python (pip install -e)**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install -e . # Editable install; changes reflect immediately
|
|
45
|
+
my-cli --version
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Editable installs are the Python equivalent of `npm link` — source changes are picked up without reinstall.
|
|
49
|
+
|
|
50
|
+
**Go**
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
go install . # Installs to $GOPATH/bin
|
|
54
|
+
my-cli --version
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or run directly: `go run . <args>`.
|
|
58
|
+
|
|
59
|
+
### Manual Testing Workflow
|
|
60
|
+
|
|
61
|
+
Structure manual testing around scenarios, not individual flags:
|
|
62
|
+
|
|
63
|
+
1. Define the happy path scenario as a shell script in `tests/manual/`
|
|
64
|
+
2. Define common error scenarios (missing required arg, bad input format, network failure)
|
|
65
|
+
3. Run the scenario script after each meaningful change
|
|
66
|
+
|
|
67
|
+
Keep a `Makefile` target or `scripts/dev-test.sh` that runs the most important manual scenarios. This catches integration issues that unit tests miss and runs in seconds.
|
|
68
|
+
|
|
69
|
+
### Debug Flags
|
|
70
|
+
|
|
71
|
+
Support a debug output mode that is invisible in normal operation but invaluable during development:
|
|
72
|
+
|
|
73
|
+
- **Node.js**: Respect `DEBUG=my-cli:*` using the `debug` package. Namespaced debug output appears only when the env var is set
|
|
74
|
+
- **Rust**: Use `RUST_LOG=debug` with the `env_logger` or `tracing` crate
|
|
75
|
+
- **Python**: `LOGLEVEL=DEBUG` with the `logging` module
|
|
76
|
+
|
|
77
|
+
Add an explicit `--verbose` or `--debug` flag that mirrors the env var. This lets users report bugs with detailed output without requiring knowledge of the env var.
|
|
78
|
+
|
|
79
|
+
Internal debug output always goes to stderr, never stdout. Stdout is for the tool's actual output; mixing diagnostic messages corrupts pipe chains.
|
|
80
|
+
|
|
81
|
+
### Hot Reload for Node.js
|
|
82
|
+
|
|
83
|
+
For TypeScript CLIs, use `ts-node` or `tsx` to avoid a compile step during development:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Run directly with tsx (fast TypeScript runner)
|
|
87
|
+
tsx src/index.ts <args>
|
|
88
|
+
|
|
89
|
+
# Or use nodemon for file-watch rerun
|
|
90
|
+
npx nodemon --ext ts --exec "tsx src/index.ts" -- <args>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Add a `dev` script to `package.json`:
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"scripts": {
|
|
97
|
+
"dev": "tsx src/index.ts",
|
|
98
|
+
"build": "tsc",
|
|
99
|
+
"start": "node dist/index.js"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### REPL Development
|
|
105
|
+
|
|
106
|
+
When building interactive prompts or REPL-like features, test incrementally:
|
|
107
|
+
|
|
108
|
+
- Mock `process.stdin` and `process.stdout` in unit tests to verify prompt sequences
|
|
109
|
+
- Use `script` (Unix) or `Expect` to record and replay terminal interactions in integration tests
|
|
110
|
+
- Test TTY vs non-TTY behavior explicitly — many prompt libraries silently skip prompts in non-TTY mode
|
|
111
|
+
|
|
112
|
+
### Environment Isolation
|
|
113
|
+
|
|
114
|
+
Use `.env.development` and `.env.test` files for environment-specific overrides. Never commit credentials. Load with `dotenv` (Node), `dotenvy` (Rust), or `python-dotenv`. Validate required env vars at startup with a clear error if missing.
|
|
115
|
+
|
|
116
|
+
Isolate test runs from the user's real config by setting `XDG_CONFIG_HOME` and `HOME` to temporary directories in tests:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export XDG_CONFIG_HOME=$(mktemp -d)
|
|
120
|
+
my-cli init # reads/writes to temp dir, never touches ~/.config
|
|
121
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-distribution-patterns
|
|
3
|
+
description: npm, pip, and cargo publishing, Homebrew formulae, standalone binaries, Docker images, and GitHub Releases with checksums
|
|
4
|
+
topics: [cli, distribution, npm, homebrew, cargo, pip, standalone-binaries, github-releases, checksums]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Distribution is where many CLI projects fail: the tool works perfectly in development but is painful to install, update, or run in different environments. A well-distributed CLI reaches users through multiple channels (package manager, direct download, container) and handles updates gracefully.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
A well-distributed CLI reaches users through multiple channels: package registries (npm, cargo, pip), Homebrew formulae, standalone binaries, and GitHub Releases with SHA256 checksums. Automate publishing via CI with trusted publishing (OIDC) rather than storing long-lived tokens. Notify users of updates asynchronously without blocking command execution.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### Package Registry Publishing
|
|
16
|
+
|
|
17
|
+
**npm (Node.js)**
|
|
18
|
+
|
|
19
|
+
Declare `bin` in `package.json` and publish to npm:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"name": "@myorg/my-cli",
|
|
24
|
+
"version": "1.0.0",
|
|
25
|
+
"bin": { "my-cli": "./bin/my-cli" },
|
|
26
|
+
"files": ["bin/", "dist/"]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Users install with `npm install -g @myorg/my-cli`. Use `npm publish --access public` for scoped packages. Set up `publishConfig` for automated CI publishing via npm trusted publishing (GitHub OIDC) rather than storing long-lived tokens.
|
|
31
|
+
|
|
32
|
+
**cargo (Rust)**
|
|
33
|
+
|
|
34
|
+
Publish to crates.io with `cargo publish`. Users install with `cargo install my-cli`. Binary name is declared in `Cargo.toml` under `[[bin]]`. Provide pre-built binaries via GitHub Releases for users who do not have Rust installed.
|
|
35
|
+
|
|
36
|
+
**pip (Python)**
|
|
37
|
+
|
|
38
|
+
Use `pyproject.toml` with `[project.scripts]` to declare the CLI entry point. Publish to PyPI with `python -m build && twine upload`. Users install with `pip install my-cli` or `pipx install my-cli`. Prefer `pipx` for CLI tools — it installs in an isolated virtualenv, preventing dependency conflicts.
|
|
39
|
+
|
|
40
|
+
### Homebrew Formulae
|
|
41
|
+
|
|
42
|
+
Homebrew is the preferred installation channel for macOS users. Two options:
|
|
43
|
+
|
|
44
|
+
**Core formula (homebrew-core)**: For widely-used tools. Submit a PR to `homebrew/homebrew-core`. Requires the tool to meet popularity and quality standards.
|
|
45
|
+
|
|
46
|
+
**Custom tap**: For any tool, immediately available:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
# Formula/my-cli.rb
|
|
50
|
+
class MyCli < Formula
|
|
51
|
+
desc "One-line description of what this does"
|
|
52
|
+
homepage "https://github.com/myorg/my-cli"
|
|
53
|
+
url "https://github.com/myorg/my-cli/archive/v1.0.0.tar.gz"
|
|
54
|
+
sha256 "abc123..."
|
|
55
|
+
license "MIT"
|
|
56
|
+
|
|
57
|
+
def install
|
|
58
|
+
bin.install "bin/my-cli"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
test do
|
|
62
|
+
assert_match "1.0.0", shell_output("#{bin}/my-cli --version")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Automate formula updates with `brew bump-formula-pr` or tools like `release-please`. The SHA256 in the formula must match the release tarball exactly.
|
|
68
|
+
|
|
69
|
+
### Standalone Binaries
|
|
70
|
+
|
|
71
|
+
Ship zero-dependency binaries for users who do not have the language runtime installed:
|
|
72
|
+
|
|
73
|
+
- **Node.js**: `pkg` (Vercel) or `nexe` bundle Node + app into a single executable. `bun build --compile` produces a single binary. Provide binaries for `linux-x64`, `linux-arm64`, `darwin-x64`, `darwin-arm64`, `win-x64`
|
|
74
|
+
- **Rust**: Cross-compile with `cross` (Docker-based cross-compiler). `cargo-dist` automates release binary creation and GitHub Release uploads
|
|
75
|
+
- **Python**: `PyInstaller` bundles the interpreter and dependencies. `Nuitka` compiles to C for smaller binaries
|
|
76
|
+
- **Go**: `GOOS=linux GOARCH=amd64 go build` — Go cross-compilation is built in
|
|
77
|
+
|
|
78
|
+
### GitHub Releases with Checksums
|
|
79
|
+
|
|
80
|
+
Every release should publish binaries with SHA256 checksums:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Generate checksums
|
|
84
|
+
sha256sum my-cli-linux-x64 my-cli-darwin-arm64 > checksums.txt
|
|
85
|
+
|
|
86
|
+
# Upload to release
|
|
87
|
+
gh release create v1.0.0 \
|
|
88
|
+
my-cli-linux-x64 \
|
|
89
|
+
my-cli-darwin-arm64 \
|
|
90
|
+
checksums.txt \
|
|
91
|
+
--title "v1.0.0" \
|
|
92
|
+
--notes-file CHANGELOG.md
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Users verify: `sha256sum -c checksums.txt`. Homebrew formulae require a SHA256 of the source tarball. Provide a `checksums.txt` in a consistent, parseable format.
|
|
96
|
+
|
|
97
|
+
### Auto-Update Strategy
|
|
98
|
+
|
|
99
|
+
CLIs should notify users of available updates without blocking command execution:
|
|
100
|
+
|
|
101
|
+
1. Check for updates asynchronously in a background process after command completes
|
|
102
|
+
2. Cache the result for 24 hours to avoid hitting the registry on every run
|
|
103
|
+
3. Print update notification to stderr so it does not corrupt stdout output
|
|
104
|
+
4. Respect `--no-update-check` flag and `MYCLI_NO_UPDATE_CHECK` env var for CI environments
|
|
105
|
+
|
|
106
|
+
Implement with a non-blocking background check: spawn a subprocess that writes the latest version to a cache file, then read that cache on the next invocation.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-interactivity-patterns
|
|
3
|
+
description: Prompt libraries, spinners, progress bars, color output, TTY detection, and graceful degradation for piped output
|
|
4
|
+
topics: [cli, interactivity, prompts, spinners, progress-bars, tty-detection, color, chalk]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Interactive CLI features — prompts, spinners, progress bars, colored output — exist to help humans. They must never obstruct machines. The guiding principle is graceful degradation: detect the execution context and silently disable interactive features when they would break automation.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Interactive CLI features — prompts, spinners, progress bars, colored output — must be gated on TTY detection and degrade gracefully in pipes, redirected output, and CI environments. Always provide `--yes` / `--no-interactive` flags. Spinners and progress output go to stderr; data goes to stdout. Follow the `NO_COLOR` standard for color defeat.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### TTY Detection
|
|
16
|
+
|
|
17
|
+
Every interactive feature must be gated on TTY detection:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
// Node.js
|
|
21
|
+
const isInteractive = process.stdout.isTTY && process.stdin.isTTY;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```rust
|
|
25
|
+
// Rust
|
|
26
|
+
use std::io::IsTerminal;
|
|
27
|
+
let is_interactive = std::io::stdout().is_terminal();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# Python
|
|
32
|
+
import sys
|
|
33
|
+
is_interactive = sys.stdout.isatty()
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Non-TTY contexts include: piped output (`my-cli | grep`), redirected output (`my-cli > out.txt`), CI environments (GitHub Actions, Jenkins), and SSH sessions without a pseudo-TTY. In these contexts: suppress spinners, suppress color, suppress prompts (or fail with a clear error if input is required).
|
|
37
|
+
|
|
38
|
+
### Prompt Libraries
|
|
39
|
+
|
|
40
|
+
For wizard-style input collection:
|
|
41
|
+
|
|
42
|
+
- **Node.js**: `@inquirer/prompts` (modern, ESM, individual prompt imports), `prompts` (lightweight, no class hierarchy), `inquirer` (classic, comprehensive)
|
|
43
|
+
- **Rust**: `dialoguer` (composable prompts: text, select, confirm, multi-select), `requestty`
|
|
44
|
+
- **Python**: `questionary` (rich prompts), `click.prompt()` / `click.confirm()` (built into click)
|
|
45
|
+
|
|
46
|
+
Prompt patterns:
|
|
47
|
+
- **Text input**: Free-form string with optional validation and default
|
|
48
|
+
- **Select**: Choose one from a list (arrow keys)
|
|
49
|
+
- **Multi-select**: Choose multiple from a list (space to toggle)
|
|
50
|
+
- **Confirm**: Yes/no boolean
|
|
51
|
+
- **Password**: Hidden input (no echo)
|
|
52
|
+
|
|
53
|
+
Always provide `--yes` / `--no-interactive` flags that skip prompts with defaults. In CI, any tool requiring interactive prompts without this escape hatch is broken.
|
|
54
|
+
|
|
55
|
+
### Spinners
|
|
56
|
+
|
|
57
|
+
Spinners indicate ongoing background work without a known duration:
|
|
58
|
+
|
|
59
|
+
- **Node.js**: `ora` (most popular, customizable), `nanospinner` (tiny), `cli-spinners` (spinner frames only)
|
|
60
|
+
- **Rust**: `indicatif` (spinners + progress bars)
|
|
61
|
+
- **Python**: `halo`, `yaspin`
|
|
62
|
+
|
|
63
|
+
Rules:
|
|
64
|
+
- Only render spinners when `stdout.isTTY` is true
|
|
65
|
+
- Spinners output to stderr, not stdout, so pipe chains are not polluted
|
|
66
|
+
- Clear the spinner line before printing final output — users should not see spinner characters mixed with results
|
|
67
|
+
- On completion, replace spinner with a final status line: `✓ Built in 2.4s`
|
|
68
|
+
|
|
69
|
+
### Progress Bars
|
|
70
|
+
|
|
71
|
+
Progress bars are appropriate when the total number of steps is known:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Building assets ████████████████░░░░ 80% (240/300 files)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- Show percentage, current/total count, and elapsed or estimated remaining time
|
|
78
|
+
- Update at most 10–20 times per second — more frequent updates cause flicker without adding information
|
|
79
|
+
- Collapse to a single completion line when done
|
|
80
|
+
- For network transfers, show bytes downloaded and transfer rate
|
|
81
|
+
|
|
82
|
+
### Color Output
|
|
83
|
+
|
|
84
|
+
- **Node.js**: `chalk` (most popular), `picocolors` (tiny, fast), `kleur`
|
|
85
|
+
- **Rust**: `colored`, `owo-colors`, `nu-ansi-term`
|
|
86
|
+
- **Python**: `rich` (full formatting library), `colorama` (Windows ANSI compatibility), `termcolor`
|
|
87
|
+
|
|
88
|
+
Color conventions:
|
|
89
|
+
- **Green**: Success, completion
|
|
90
|
+
- **Yellow/Amber**: Warning, deprecation
|
|
91
|
+
- **Red**: Error, failure
|
|
92
|
+
- **Cyan/Blue**: Informational, prompts
|
|
93
|
+
- **Dim/Gray**: Secondary information, timestamps
|
|
94
|
+
|
|
95
|
+
### Graceful Degradation Checklist
|
|
96
|
+
|
|
97
|
+
Before shipping any interactive feature, verify it degrades correctly:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Test pipe context
|
|
101
|
+
my-cli build | cat
|
|
102
|
+
|
|
103
|
+
# Test redirected output
|
|
104
|
+
my-cli build > output.txt
|
|
105
|
+
|
|
106
|
+
# Test CI simulation
|
|
107
|
+
CI=true my-cli build
|
|
108
|
+
|
|
109
|
+
# Test NO_COLOR
|
|
110
|
+
NO_COLOR=1 my-cli build
|
|
111
|
+
|
|
112
|
+
# Test non-interactive (should not hang waiting for input)
|
|
113
|
+
echo "" | my-cli init
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If any of these scenarios hangs, errors with formatting artifacts, or corrupts stdout with escape codes, the interactive feature is not correctly gated.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-output-patterns
|
|
3
|
+
description: --json/--format flags, table formatting, machine-readable output, piping conventions, and --quiet flag for CLI tools
|
|
4
|
+
topics: [cli, output, json, formatting, tables, piping, stdout, stderr, machine-readable]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
CLI output is a two-audience problem: humans reading in a terminal, and machines reading in a pipeline. Tools that ignore the machine audience force users to write fragile regex parsers against human-formatted output. Tools that ignore the human audience are inscrutable to debug. The solution is structured output modes with a clear default.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
CLI output serves two audiences: humans in terminals and machines in pipelines. stdout carries data; stderr carries status, progress, warnings, and errors. Provide `--json` for machine consumption, `--format` for multiple output modes, and `--quiet` to suppress non-error output. Use consistent snake_case field names in JSON output across all subcommands.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### stdout vs stderr Convention
|
|
16
|
+
|
|
17
|
+
This distinction is non-negotiable for tools expected to participate in pipelines:
|
|
18
|
+
|
|
19
|
+
- **stdout**: The tool's result — data, transformed content, generated output. This is what `>` captures and `|` pipes.
|
|
20
|
+
- **stderr**: Status messages, progress, warnings, errors — anything that is for the human operator, not the downstream process.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Correct: status to stderr, data to stdout
|
|
24
|
+
my-cli process input.json > output.json 2>errors.log
|
|
25
|
+
|
|
26
|
+
# If status messages are on stdout, this breaks
|
|
27
|
+
my-cli process input.json | jq '.result' # jq sees status messages, fails to parse
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Log every informational line, progress update, warning, and error to stderr. Reserve stdout for the tool's actual data output.
|
|
31
|
+
|
|
32
|
+
### --json Flag
|
|
33
|
+
|
|
34
|
+
Provide `--json` for any command that produces output a script might consume:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
my-cli status --json
|
|
38
|
+
# Output:
|
|
39
|
+
{
|
|
40
|
+
"status": "running",
|
|
41
|
+
"pid": 12345,
|
|
42
|
+
"uptime": 3600,
|
|
43
|
+
"version": "2.4.1"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Rules for `--json` output:
|
|
48
|
+
- Always valid JSON — never mix human-readable text with JSON
|
|
49
|
+
- Always exit 0 and include an error field for operational errors, rather than outputting JSON to stdout and error text to stdout
|
|
50
|
+
- For errors: `{"error": "message", "code": "ERROR_CODE"}` to stderr with non-zero exit
|
|
51
|
+
- Arrays for lists, never newline-separated JSON objects (unless using JSON Lines)
|
|
52
|
+
|
|
53
|
+
**JSON Lines** (`--json-lines` or `--format=jsonl`): For streaming or large list output, emit one JSON object per line. Each line is independently parseable. This is preferred over wrapping everything in a single JSON array when the output can be large.
|
|
54
|
+
|
|
55
|
+
### --format Flag
|
|
56
|
+
|
|
57
|
+
For tools that support multiple output formats:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
my-cli list --format=table # Human-readable table (default)
|
|
61
|
+
my-cli list --format=json # JSON array
|
|
62
|
+
my-cli list --format=csv # CSV with header row
|
|
63
|
+
my-cli list --format=tsv # Tab-separated, no header (script-friendly)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When `--format` is provided without a value, default to `json`. When piping to a non-TTY, consider defaulting to `json` automatically.
|
|
67
|
+
|
|
68
|
+
### Table Formatting
|
|
69
|
+
|
|
70
|
+
For human-readable tabular output:
|
|
71
|
+
|
|
72
|
+
- **Node.js**: `columnify`, `cli-table3`, `tabled`
|
|
73
|
+
- **Rust**: `comfy-table`, `prettytable-rs`, `tabled`
|
|
74
|
+
- **Python**: `tabulate`, `rich.table`
|
|
75
|
+
|
|
76
|
+
Table rules:
|
|
77
|
+
- Align columns consistently; numbers right-aligned, strings left-aligned
|
|
78
|
+
- Truncate long values with `...` suffix to preserve column alignment
|
|
79
|
+
- Print column headers by default; suppress with `--no-header`
|
|
80
|
+
- Respect terminal width (`process.stdout.columns`) — do not print lines that wrap unexpectedly
|
|
81
|
+
|
|
82
|
+
### --quiet Flag
|
|
83
|
+
|
|
84
|
+
`--quiet` / `-q` suppresses all non-error output. The tool runs silently and only prints to stderr if something goes wrong:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
my-cli build --quiet && echo "Build OK" # Only "Build OK" appears on success
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Quiet mode is essential for cron jobs, shell scripts that run in the background, and CI steps where unrelated output creates noise.
|
|
91
|
+
|
|
92
|
+
Quiet + --json interaction: `--json` takes precedence. A user who explicitly requests JSON output expects it even with `--quiet`.
|
|
93
|
+
|
|
94
|
+
### Output Format Decision Matrix
|
|
95
|
+
|
|
96
|
+
| Scenario | Recommended Format |
|
|
97
|
+
|---|---|
|
|
98
|
+
| Interactive terminal, human operator | Table or formatted text |
|
|
99
|
+
| Piped to another CLI tool | JSON or TSV |
|
|
100
|
+
| CI/CD logging | Plain text (no ANSI), or JSON Lines |
|
|
101
|
+
| Consumed by a script | JSON with `--json` |
|
|
102
|
+
| Large dataset (streaming) | JSON Lines |
|
|
103
|
+
| Copy-paste into a document | Plain text or CSV |
|
|
104
|
+
|
|
105
|
+
### Consistent Field Names
|
|
106
|
+
|
|
107
|
+
When outputting JSON, use consistent snake_case field names across all subcommands. Define a schema. Breaking changes to JSON output are breaking changes to any script that consumes the tool — treat them with the same care as a library API change.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-project-structure
|
|
3
|
+
description: Directory layout, entry points, config file resolution, and plugin directory structure for CLI projects
|
|
4
|
+
topics: [cli, project-structure, directory-layout, config-resolution, bin, plugins]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
A well-structured CLI project makes it easy to add subcommands, locate business logic, and onboard contributors. The structure should reflect the mental model of the tool: commands are the public API, utilities are shared infrastructure, and configuration drives runtime behavior.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
A CLI project structure separates concerns into `bin/` (entry point), `src/commands/` (one file per subcommand), `src/utils/` (shared business logic), and `tests/`. Command handlers are thin dispatchers; business logic belongs in utilities. Config files are discovered in precedence order from CLI flags through project-local files to XDG Base Directory user config.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### Canonical Directory Layout
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
my-cli/
|
|
19
|
+
├── bin/
|
|
20
|
+
│ └── my-cli # Entry point (Node shebang or compiled binary symlink)
|
|
21
|
+
├── src/
|
|
22
|
+
│ ├── commands/ # One file per subcommand
|
|
23
|
+
│ │ ├── init.ts
|
|
24
|
+
│ │ ├── build.ts
|
|
25
|
+
│ │ └── deploy.ts
|
|
26
|
+
│ ├── utils/ # Shared helpers (not command-specific)
|
|
27
|
+
│ │ ├── logger.ts
|
|
28
|
+
│ │ ├── config.ts
|
|
29
|
+
│ │ └── fs.ts
|
|
30
|
+
│ ├── types/ # Shared type definitions
|
|
31
|
+
│ └── index.ts # CLI entry point (parses argv, routes to commands)
|
|
32
|
+
├── tests/
|
|
33
|
+
│ ├── commands/ # Integration tests per command
|
|
34
|
+
│ └── utils/ # Unit tests for utilities
|
|
35
|
+
├── package.json # (Node) or Cargo.toml (Rust) or pyproject.toml (Python)
|
|
36
|
+
└── README.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Command File Convention
|
|
40
|
+
|
|
41
|
+
Each command file owns one subcommand and should export:
|
|
42
|
+
- A `command` descriptor (name, aliases, description, flags schema)
|
|
43
|
+
- A `handler` function that receives parsed arguments and executes the operation
|
|
44
|
+
|
|
45
|
+
Keep handler functions thin: validate inputs, call service functions from `utils/`, handle errors, format output. Business logic belongs in `utils/`, not in command handlers.
|
|
46
|
+
|
|
47
|
+
### bin/ Entry Point
|
|
48
|
+
|
|
49
|
+
The `bin/` entry point is what users run. It should:
|
|
50
|
+
1. Set up the Node/Python/Rust runtime minimum (e.g., `#!/usr/bin/env node`)
|
|
51
|
+
2. Import and invoke the CLI router from `src/index.ts`
|
|
52
|
+
3. Contain no business logic
|
|
53
|
+
|
|
54
|
+
For Node.js, declare the bin in `package.json`:
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"bin": {
|
|
58
|
+
"my-cli": "./bin/my-cli"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For compiled languages (Rust, Go), `bin/` may contain the compiled binary or a wrapper script. The real entry point is `src/main.rs` or `cmd/root.go`.
|
|
64
|
+
|
|
65
|
+
### Config File Resolution
|
|
66
|
+
|
|
67
|
+
Config files should be discovered in order from most-specific to most-general:
|
|
68
|
+
|
|
69
|
+
1. Path from `--config` flag (explicit override)
|
|
70
|
+
2. `MYCLI_CONFIG` environment variable
|
|
71
|
+
3. `./mycli.config.json` (project-local, in current working directory)
|
|
72
|
+
4. `.myclirc` (project-local dotfile)
|
|
73
|
+
5. `~/.config/mycli/config.json` (XDG Base Directory standard)
|
|
74
|
+
6. `~/.myclirc` (legacy home dotfile)
|
|
75
|
+
7. Built-in defaults
|
|
76
|
+
|
|
77
|
+
Use XDG Base Directory (`~/.config/<name>/`) as the preferred user config location on Linux/macOS. On Windows, use `%APPDATA%\<name>\config.json`. Libraries like `env-paths` (Node) or the `dirs` crate (Rust) handle cross-platform config paths correctly.
|
|
78
|
+
|
|
79
|
+
Walk the directory tree upward from CWD when looking for project-local config (same pattern as `.gitignore`). Stop at the home directory or filesystem root.
|
|
80
|
+
|
|
81
|
+
### Plugin Directory Structure
|
|
82
|
+
|
|
83
|
+
If the tool supports plugins:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
~/.config/mycli/
|
|
87
|
+
├── config.json # User config
|
|
88
|
+
└── plugins/
|
|
89
|
+
├── my-plugin/
|
|
90
|
+
│ ├── package.json
|
|
91
|
+
│ └── index.js
|
|
92
|
+
└── another-plugin/
|
|
93
|
+
└── ...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Plugin discovery scans `~/.config/mycli/plugins/` at startup. Each plugin directory must have a manifest (`package.json` or `plugin.json`) declaring:
|
|
97
|
+
- `name`: Plugin identifier
|
|
98
|
+
- `version`: Semver
|
|
99
|
+
- `main`: Entry point relative to plugin directory
|
|
100
|
+
- `commands`: Array of subcommand names the plugin registers
|
|
101
|
+
|
|
102
|
+
### Monorepo CLI Structure
|
|
103
|
+
|
|
104
|
+
When a CLI is part of a larger monorepo:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
packages/
|
|
108
|
+
├── cli/ # The CLI package
|
|
109
|
+
│ ├── src/commands/
|
|
110
|
+
│ └── package.json
|
|
111
|
+
├── core/ # Shared business logic (used by CLI + SDK + server)
|
|
112
|
+
└── sdk/ # Programmatic API (no CLI dependency)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Keep the CLI package thin. Business logic in `core/` can be tested without CLI overhead and reused by other consumers. The CLI is a delivery mechanism, not the application.
|
|
116
|
+
|
|
117
|
+
### State File Convention
|
|
118
|
+
|
|
119
|
+
Runtime state (auth tokens, cached data, last-run timestamps) should be stored separately from config:
|
|
120
|
+
|
|
121
|
+
- Config: user-editable settings (`~/.config/mycli/config.json`)
|
|
122
|
+
- State: machine-managed runtime data (`~/.local/share/mycli/state.json` per XDG, or `~/.mycli/state.json`)
|
|
123
|
+
|
|
124
|
+
Never overwrite user config with machine-managed state. Mixing them causes painful merge conflicts and makes it hard to commit config to a dotfiles repo.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-requirements
|
|
3
|
+
description: CLI UX principles, POSIX conventions, exit codes, signal handling, and progressive disclosure for command-line tools
|
|
4
|
+
topics: [cli, ux, posix, exit-codes, signal-handling, progressive-disclosure]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
CLI tools occupy a unique design space: they are used by humans typing commands, embedded in shell scripts, and piped together in pipelines. A tool that conflates these contexts fails at all of them. The guiding principle is: do one thing well, fail loudly with actionable errors, and never surprise the pipeline.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
CLI tools serve humans, shell scripts, and pipelines simultaneously. Follow POSIX flag conventions, use standard exit codes (0 success, 1 error, 2 usage), handle signals cleanly (SIGINT, SIGTERM, SIGPIPE), and implement progressive disclosure in help output. Define the tool's single responsibility before writing code.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### Do One Thing Well
|
|
16
|
+
|
|
17
|
+
Unix philosophy is not aspirational — it is a load-bearing constraint. A CLI that tries to be a GUI, an interactive REPL, and a batch processor will be worse than a dedicated tool at each. Define the single responsibility before writing a line of code: what is the one transformation or action this tool performs?
|
|
18
|
+
|
|
19
|
+
Corollaries:
|
|
20
|
+
- Subcommands are fine; each subcommand should still do one thing
|
|
21
|
+
- Options should modify behavior, not switch into a different tool
|
|
22
|
+
- If two features feel unrelated, they probably belong in two tools connected by a pipe
|
|
23
|
+
|
|
24
|
+
### POSIX Conventions
|
|
25
|
+
|
|
26
|
+
Following POSIX conventions is not optional for tools that expect to live in shell scripts. Deviating breaks the mental model of every experienced shell user:
|
|
27
|
+
|
|
28
|
+
- Short flags use a single dash and one letter: `-v`, `-q`, `-n`
|
|
29
|
+
- Long flags use double dash: `--verbose`, `--quiet`, `--dry-run`
|
|
30
|
+
- Short flags can be combined: `-vq` is equivalent to `-v -q`
|
|
31
|
+
- Flags precede positional arguments; `--` terminates flag parsing
|
|
32
|
+
- Options that take values: `-o output.txt` or `--output=output.txt`
|
|
33
|
+
- Mutually exclusive flags should be documented, not silently overridden
|
|
34
|
+
|
|
35
|
+
### Exit Codes
|
|
36
|
+
|
|
37
|
+
Exit codes are the API surface of a CLI. Scripts depend on them; violating the convention silently breaks downstream automation:
|
|
38
|
+
|
|
39
|
+
- **0** — success, operation completed as expected
|
|
40
|
+
- **1** — general error (operation failed for a known reason)
|
|
41
|
+
- **2** — usage error (bad arguments, missing required flag, unknown subcommand)
|
|
42
|
+
- **126** — command found but not executable
|
|
43
|
+
- **127** — command not found
|
|
44
|
+
- **130** — terminated by Ctrl-C (128 + SIGINT signal 2)
|
|
45
|
+
|
|
46
|
+
Always exit with 1 for domain errors, 2 for argument errors. Never exit 0 when something went wrong. Scripts using `set -e` will silently swallow partial failures if a tool exits 0 on error.
|
|
47
|
+
|
|
48
|
+
### Signal Handling
|
|
49
|
+
|
|
50
|
+
Respect signals — scripts set `trap` handlers and expect clean shutdown:
|
|
51
|
+
|
|
52
|
+
- **SIGINT** (Ctrl-C): Interrupt. Clean up temp files, restore terminal state (if using raw mode), then exit 130
|
|
53
|
+
- **SIGTERM**: Graceful shutdown request. Same cleanup as SIGINT; exit 143 (128 + 15)
|
|
54
|
+
- **SIGPIPE**: Write to a closed pipe (e.g., `mytool | head -5` closes stdin after 5 lines). Handle by exiting cleanly rather than printing a broken pipe error
|
|
55
|
+
|
|
56
|
+
Register signal handlers at startup. In Node.js: `process.on('SIGINT', cleanup)`. In Rust: the `ctrlc` crate. In Python: `signal.signal(signal.SIGINT, handler)`.
|
|
57
|
+
|
|
58
|
+
### Progressive Disclosure
|
|
59
|
+
|
|
60
|
+
Help output should scale with user familiarity:
|
|
61
|
+
|
|
62
|
+
- `--help` shows the most common options and a one-line description of each
|
|
63
|
+
- `--help --verbose` or `help <subcommand>` shows full option documentation
|
|
64
|
+
- Man pages contain full reference material including examples and edge cases
|
|
65
|
+
- Error messages should name the bad input and suggest the fix: `Unknown flag --colour. Did you mean --color?`
|
|
66
|
+
|
|
67
|
+
Never dump 200 lines of option documentation in response to a bad argument. The user who mistyped a flag wants a correction, not a manual.
|
|
68
|
+
|
|
69
|
+
### Input Validation Order
|
|
70
|
+
|
|
71
|
+
Validate in this order to give the most useful errors earliest:
|
|
72
|
+
|
|
73
|
+
1. Flag syntax (unknown flags, missing required values)
|
|
74
|
+
2. Required argument presence
|
|
75
|
+
3. Type/format validation (is this a valid integer? a valid path?)
|
|
76
|
+
4. Semantic validation (does the file exist? does the user have permission?)
|
|
77
|
+
5. External resource validation (can we reach the API? is the DB up?)
|
|
78
|
+
|
|
79
|
+
Exit 2 for steps 1–3 (usage errors). Exit 1 for steps 4–5 (runtime errors).
|
|
80
|
+
|
|
81
|
+
### Stdin / Stdout Pipeline Contract
|
|
82
|
+
|
|
83
|
+
A tool that participates in pipelines must honor these contracts:
|
|
84
|
+
|
|
85
|
+
- **Accept stdin**: If the tool reads input, accept it from stdin when no file argument is provided. `my-cli process < input.txt` and `cat input.txt | my-cli process` must both work.
|
|
86
|
+
- **Write to stdout**: All output data goes to stdout. All status, progress, and error messages go to stderr. Violating this breaks every pipe chain the tool participates in.
|
|
87
|
+
- **Exit on broken pipe**: When the downstream process closes its stdin (e.g., `my-cli list | head -5`), the tool receives SIGPIPE. Handle it cleanly — do not print a "broken pipe" error.
|
|
88
|
+
- **No color in pipes**: When stdout is not a TTY, disable ANSI color codes automatically. Colored output in a pipe corrupts the downstream process's input.
|
|
89
|
+
- **No interactive prompts in pipes**: When stdin is not a TTY, never prompt for user input. Either use defaults or fail with a clear error requesting the `--yes` flag.
|
|
90
|
+
|
|
91
|
+
### Error Message Quality
|
|
92
|
+
|
|
93
|
+
Error messages are the primary user support channel for CLI tools. Every error should answer three questions:
|
|
94
|
+
|
|
95
|
+
1. **What happened?** — "Could not connect to the API at https://api.example.com"
|
|
96
|
+
2. **Why did it happen?** — "Connection timed out after 10 seconds"
|
|
97
|
+
3. **What should the user do?** — "Check your network connection, or set --timeout to increase the timeout"
|
|
98
|
+
|
|
99
|
+
Never print a raw stack trace to the user. Stack traces go to debug output or log files. The user-facing message should be actionable. For unknown errors, provide a way to capture debug output: "Run with --verbose and file an issue at https://github.com/..."
|
|
100
|
+
|
|
101
|
+
Suggest corrections for common mistakes: `Unknown command 'biuld'. Did you mean 'build'?` Use Levenshtein distance or a fuzzy matching library to find close matches among known commands and flags.
|