litclaude-ai 0.2.2

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.
Files changed (156) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/LICENSE +21 -0
  3. package/README.md +369 -0
  4. package/README_ko-KR.md +374 -0
  5. package/RELEASE_CHECKLIST.md +165 -0
  6. package/bin/litclaude-ai.js +643 -0
  7. package/cover.png +0 -0
  8. package/docs/agents.md +67 -0
  9. package/docs/hooks.md +134 -0
  10. package/docs/lsp.md +40 -0
  11. package/docs/migration.md +209 -0
  12. package/docs/workflow-compatibility-audit.md +119 -0
  13. package/generate_cover.py +123 -0
  14. package/package.json +48 -0
  15. package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
  16. package/plugins/litclaude/.lsp.json +13 -0
  17. package/plugins/litclaude/.mcp.json +9 -0
  18. package/plugins/litclaude/agents/boulder-executor.md +12 -0
  19. package/plugins/litclaude/agents/librarian-researcher.md +15 -0
  20. package/plugins/litclaude/agents/oracle-verifier.md +16 -0
  21. package/plugins/litclaude/agents/prometheus-planner.md +13 -0
  22. package/plugins/litclaude/agents/qa-runner.md +16 -0
  23. package/plugins/litclaude/agents/quality-reviewer.md +17 -0
  24. package/plugins/litclaude/bin/litclaude-hook.js +110 -0
  25. package/plugins/litclaude/bin/litclaude-hud.js +271 -0
  26. package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
  27. package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
  28. package/plugins/litclaude/commands/deep-interview.md +21 -0
  29. package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
  30. package/plugins/litclaude/commands/lit-loop.md +40 -0
  31. package/plugins/litclaude/commands/lit-plan.md +35 -0
  32. package/plugins/litclaude/commands/litgoal.md +30 -0
  33. package/plugins/litclaude/commands/review-work.md +35 -0
  34. package/plugins/litclaude/commands/start-work.md +36 -0
  35. package/plugins/litclaude/hooks/hooks.json +54 -0
  36. package/plugins/litclaude/lib/context-pressure.mjs +25 -0
  37. package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
  38. package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
  39. package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
  40. package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
  41. package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
  42. package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
  43. package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
  44. package/plugins/litclaude/lib/workflow-check.mjs +83 -0
  45. package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
  46. package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
  47. package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
  48. package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
  49. package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
  50. package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
  51. package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
  52. package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
  53. package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
  54. package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
  55. package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
  56. package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
  57. package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
  58. package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
  59. package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
  60. package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
  61. package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
  62. package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
  63. package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
  64. package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
  65. package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
  66. package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
  67. package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
  68. package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
  69. package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
  70. package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
  71. package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
  72. package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
  73. package/plugins/litclaude/skills/programming/SKILL.md +106 -0
  74. package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
  75. package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
  76. package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
  77. package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
  78. package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
  79. package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
  80. package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
  81. package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
  82. package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
  83. package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
  84. package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
  85. package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
  86. package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
  87. package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
  88. package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
  89. package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
  90. package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
  91. package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
  92. package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
  93. package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
  94. package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
  95. package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
  96. package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
  97. package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
  98. package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
  99. package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
  100. package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
  101. package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
  102. package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
  103. package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
  104. package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
  105. package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
  106. package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
  107. package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
  108. package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
  109. package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
  110. package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
  111. package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
  112. package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
  113. package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
  114. package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
  115. package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
  116. package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
  117. package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
  118. package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
  119. package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
  120. package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
  121. package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
  122. package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
  123. package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
  124. package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
  125. package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
  126. package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
  127. package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
  128. package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
  129. package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
  130. package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
  131. package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
  132. package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
  133. package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
  134. package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
  135. package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
  136. package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
  137. package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
  138. package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
  139. package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
  140. package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
  141. package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
  142. package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
  143. package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
  144. package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
  145. package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
  146. package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
  147. package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
  148. package/plugins/litclaude/skills/rules/SKILL.md +66 -0
  149. package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
  150. package/scripts/audit-plan-checkboxes.mjs +37 -0
  151. package/scripts/doctor.mjs +41 -0
  152. package/scripts/inspect-agent-tools.mjs +27 -0
  153. package/scripts/postinstall.mjs +50 -0
  154. package/scripts/qa-claude-plugin-smoke.sh +60 -0
  155. package/scripts/qa-portable-install.sh +136 -0
  156. package/scripts/validate-plugin.mjs +72 -0
@@ -0,0 +1,468 @@
1
+ # CLI Stack — cobra + slog + caarlos0/env + signal handling
2
+
3
+ The canonical Go CLI skeleton. `cobra` is the de facto framework — Kubernetes, Docker CLI, Helm, GitHub CLI, gh, Hugo all use it. Use it.
4
+
5
+ ---
6
+
7
+ ## Toolchain
8
+
9
+ ```bash
10
+ go install github.com/spf13/cobra-cli@latest
11
+ cobra-cli init mytool
12
+ cobra-cli add server
13
+ cobra-cli add migrate
14
+ ```
15
+
16
+ `cobra-cli` scaffolds the `cmd/` package. Edit the result; do not regenerate.
17
+
18
+ ---
19
+
20
+ ## Layout
21
+
22
+ ```
23
+ mytool/
24
+ ├── go.mod
25
+ ├── main.go # ≤ 30 LOC, calls cmd.Execute
26
+ ├── cmd/
27
+ │ ├── root.go # rootCmd, persistent flags, slog setup
28
+ │ ├── server.go # `mytool server` subcommand
29
+ │ ├── migrate.go # `mytool migrate` subcommand
30
+ │ └── version.go # `mytool version` — auto-injected version
31
+ ├── internal/
32
+ │ ├── config/
33
+ │ └── server/
34
+ └── Taskfile.yml
35
+ ```
36
+
37
+ ---
38
+
39
+ ## `main.go`
40
+
41
+ ```go
42
+ package main
43
+
44
+ import (
45
+ "context"
46
+ "log/slog"
47
+ "os"
48
+ "os/signal"
49
+ "syscall"
50
+
51
+ "github.com/your-org/mytool/cmd"
52
+ )
53
+
54
+ func main() {
55
+ ctx, stop := signal.NotifyContext(context.Background(),
56
+ syscall.SIGINT, syscall.SIGTERM)
57
+ defer stop()
58
+
59
+ if err := cmd.Execute(ctx); err != nil {
60
+ slog.Error("fatal", slog.Any("err", err))
61
+ os.Exit(1)
62
+ }
63
+ }
64
+ ```
65
+
66
+ `signal.NotifyContext` (Go 1.16+) gives every subcommand a ctx that cancels on Ctrl-C. Subcommands plumb the ctx into their workers.
67
+
68
+ ---
69
+
70
+ ## `cmd/root.go`
71
+
72
+ ```go
73
+ package cmd
74
+
75
+ import (
76
+ "context"
77
+ "log/slog"
78
+ "os"
79
+
80
+ "github.com/spf13/cobra"
81
+ )
82
+
83
+ var (
84
+ verbose bool
85
+ logFormat string
86
+ configPath string
87
+ )
88
+
89
+ var rootCmd = &cobra.Command{
90
+ Use: "mytool",
91
+ Short: "Short description of mytool",
92
+ Long: `Long description, prose; cobra wraps it for --help.`,
93
+ PersistentPreRunE: func(c *cobra.Command, args []string) error {
94
+ return setupLogger()
95
+ },
96
+ SilenceUsage: true, // don't print --help on every error
97
+ SilenceErrors: true, // we log them ourselves in Execute
98
+ }
99
+
100
+ func init() {
101
+ rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false,
102
+ "enable debug logging")
103
+ rootCmd.PersistentFlags().StringVar(&logFormat, "log-format", "text",
104
+ "log format: text or json")
105
+ rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "",
106
+ "path to config file (optional)")
107
+ }
108
+
109
+ func Execute(ctx context.Context) error {
110
+ return rootCmd.ExecuteContext(ctx)
111
+ }
112
+
113
+ func setupLogger() error {
114
+ level := slog.LevelInfo
115
+ if verbose { level = slog.LevelDebug }
116
+ opts := &slog.HandlerOptions{Level: level}
117
+
118
+ var h slog.Handler
119
+ switch logFormat {
120
+ case "json":
121
+ h = slog.NewJSONHandler(os.Stderr, opts)
122
+ case "text":
123
+ h = slog.NewTextHandler(os.Stderr, opts)
124
+ default:
125
+ return fmt.Errorf("invalid log-format %q", logFormat)
126
+ }
127
+ slog.SetDefault(slog.New(h))
128
+ return nil
129
+ }
130
+ ```
131
+
132
+ Notes:
133
+
134
+ - `RunE` / `PersistentPreRunE` (the `E` variants) return errors. Use these; never use `Run` (no error return, encourages `log.Fatal`).
135
+ - `SilenceUsage: true` + `SilenceErrors: true` together: cobra stops printing the full `--help` on every command failure (the default behavior is rude in production scripts).
136
+ - `ExecuteContext` (cobra 1.8+) plumbs the ctx into every subcommand's `cmd.Context()`.
137
+
138
+ ---
139
+
140
+ ## `cmd/server.go`
141
+
142
+ ```go
143
+ package cmd
144
+
145
+ import (
146
+ "log/slog"
147
+
148
+ "github.com/spf13/cobra"
149
+ "github.com/your-org/mytool/internal/server"
150
+ )
151
+
152
+ var (
153
+ serverAddr string
154
+ )
155
+
156
+ var serverCmd = &cobra.Command{
157
+ Use: "server",
158
+ Short: "Run the HTTP server",
159
+ RunE: func(c *cobra.Command, args []string) error {
160
+ ctx := c.Context()
161
+ slog.InfoContext(ctx, "starting", slog.String("addr", serverAddr))
162
+ return server.Run(ctx, serverAddr)
163
+ },
164
+ }
165
+
166
+ func init() {
167
+ serverCmd.Flags().StringVar(&serverAddr, "addr", ":8080",
168
+ "listen address")
169
+ rootCmd.AddCommand(serverCmd)
170
+ }
171
+ ```
172
+
173
+ The subcommand is a thin shim — flags + log line + delegate to `internal/server`. Anything bigger violates the 250-LOC ceiling and belongs in `internal/`.
174
+
175
+ ---
176
+
177
+ ## Subcommands with arguments
178
+
179
+ ```go
180
+ var migrateUpCmd = &cobra.Command{
181
+ Use: "up [N]",
182
+ Short: "Apply N migrations (default: all)",
183
+ Args: cobra.MaximumNArgs(1),
184
+ RunE: func(c *cobra.Command, args []string) error {
185
+ n := -1 // all
186
+ if len(args) == 1 {
187
+ var err error
188
+ n, err = strconv.Atoi(args[0])
189
+ if err != nil {
190
+ return fmt.Errorf("invalid N: %w", err)
191
+ }
192
+ }
193
+ return migrate.Up(c.Context(), n)
194
+ },
195
+ }
196
+ ```
197
+
198
+ Use cobra's argument validators (`cobra.ExactArgs`, `cobra.MaximumNArgs`, `cobra.OnlyValidArgs`). They produce clean help text.
199
+
200
+ ---
201
+
202
+ ## Flag types — typed, not strings
203
+
204
+ ```go
205
+ // GOOD
206
+ serverCmd.Flags().DurationVar(&timeout, "timeout", 30*time.Second, "request timeout")
207
+ serverCmd.Flags().IntVar(&port, "port", 8080, "port")
208
+ serverCmd.Flags().StringSliceVar(&hosts, "host", nil, "allowed hosts (repeatable)")
209
+
210
+ // BAD — manual parsing
211
+ serverCmd.Flags().StringVar(&timeoutStr, "timeout", "30s", "")
212
+ // ...then later: time.ParseDuration(timeoutStr)
213
+ ```
214
+
215
+ `pflag` (cobra's flag lib) has typed variants for every common type. Use them; the parsing and error messages are free.
216
+
217
+ ---
218
+
219
+ ## Bind flags to env vars
220
+
221
+ cobra + viper is overkill for env binding. Use `caarlos0/env/v11`:
222
+
223
+ ```go
224
+ type ServerOpts struct {
225
+ Addr string `env:"ADDR" envDefault:":8080"`
226
+ Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
227
+ }
228
+
229
+ var opts ServerOpts
230
+
231
+ var serverCmd = &cobra.Command{
232
+ Use: "server",
233
+ PersistentPreRunE: func(c *cobra.Command, args []string) error {
234
+ // 1. Parse env first.
235
+ if err := env.Parse(&opts); err != nil { return err }
236
+ // 2. Flags override env if explicitly set.
237
+ if c.Flags().Changed("addr") {
238
+ opts.Addr, _ = c.Flags().GetString("addr")
239
+ }
240
+ return nil
241
+ },
242
+ RunE: func(c *cobra.Command, args []string) error {
243
+ return server.Run(c.Context(), opts)
244
+ },
245
+ }
246
+
247
+ func init() {
248
+ serverCmd.Flags().String("addr", "", "listen address (env: ADDR)")
249
+ serverCmd.Flags().Duration("timeout", 0, "request timeout (env: TIMEOUT)")
250
+ rootCmd.AddCommand(serverCmd)
251
+ }
252
+ ```
253
+
254
+ Precedence: **flag (if set) > env > default**. Document the env var in the flag usage string.
255
+
256
+ ---
257
+
258
+ ## Version subcommand — build-injected
259
+
260
+ ```go
261
+ // cmd/version.go
262
+ package cmd
263
+
264
+ import (
265
+ "fmt"
266
+ "runtime/debug"
267
+
268
+ "github.com/spf13/cobra"
269
+ )
270
+
271
+ // Set by -ldflags at build time, falls back to debug.BuildInfo.
272
+ var (
273
+ version = ""
274
+ commit = ""
275
+ date = ""
276
+ )
277
+
278
+ var versionCmd = &cobra.Command{
279
+ Use: "version",
280
+ Short: "Print version",
281
+ Run: func(c *cobra.Command, args []string) {
282
+ v, c2, d := resolveVersion()
283
+ fmt.Printf("mytool %s (commit %s, built %s)\n", v, c2, d)
284
+ },
285
+ }
286
+
287
+ func resolveVersion() (string, string, string) {
288
+ if version != "" { return version, commit, date }
289
+ info, ok := debug.ReadBuildInfo()
290
+ if !ok { return "dev", "unknown", "unknown" }
291
+
292
+ var vcs, hash, time string
293
+ for _, s := range info.Settings {
294
+ switch s.Key {
295
+ case "vcs.revision": hash = s.Value
296
+ case "vcs.time": time = s.Value
297
+ case "vcs": vcs = s.Value
298
+ }
299
+ }
300
+ return info.Main.Version, hash, time + " (" + vcs + ")"
301
+ }
302
+
303
+ func init() { rootCmd.AddCommand(versionCmd) }
304
+ ```
305
+
306
+ Build with version injection:
307
+
308
+ ```bash
309
+ go build \
310
+ -ldflags="-X 'github.com/your-org/mytool/cmd.version=v1.2.3' -X 'github.com/your-org/mytool/cmd.commit=$(git rev-parse --short HEAD)' -X 'github.com/your-org/mytool/cmd.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)'" \
311
+ -o bin/mytool ./
312
+ ```
313
+
314
+ The `debug.BuildInfo` fallback means a `go install`'d binary also has version info — no manual `-ldflags` needed.
315
+
316
+ ---
317
+
318
+ ## Shell completions
319
+
320
+ ```go
321
+ var completionCmd = &cobra.Command{
322
+ Use: "completion [bash|zsh|fish|powershell]",
323
+ Short: "Generate shell completion",
324
+ Args: cobra.ExactValidArgs(1),
325
+ ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
326
+ DisableFlagsInUseLine: true,
327
+ RunE: func(c *cobra.Command, args []string) error {
328
+ switch args[0] {
329
+ case "bash": return rootCmd.GenBashCompletionV2(os.Stdout, true)
330
+ case "zsh": return rootCmd.GenZshCompletion(os.Stdout)
331
+ case "fish": return rootCmd.GenFishCompletion(os.Stdout, true)
332
+ case "powershell": return rootCmd.GenPowerShellCompletion(os.Stdout)
333
+ }
334
+ return nil
335
+ },
336
+ }
337
+
338
+ func init() { rootCmd.AddCommand(completionCmd) }
339
+ ```
340
+
341
+ User:
342
+
343
+ ```bash
344
+ mytool completion zsh > "${fpath[1]}/_mytool"
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Interactive prompts — `huh` from charm
350
+
351
+ For prompts/forms (`Are you sure?`, "Pick an environment", multi-field forms):
352
+
353
+ ```go
354
+ import "github.com/charmbracelet/huh"
355
+
356
+ var confirm bool
357
+ err := huh.NewConfirm().
358
+ Title("Apply migrations to PRODUCTION?").
359
+ Affirmative("Yes, do it").
360
+ Negative("Abort").
361
+ Value(&confirm).
362
+ Run()
363
+ ```
364
+
365
+ `huh` replaces `survey` (which is no longer maintained). It composes with `lipgloss` for styling.
366
+
367
+ ---
368
+
369
+ ## Progress / spinners
370
+
371
+ ```go
372
+ import "github.com/charmbracelet/huh/spinner"
373
+
374
+ err := spinner.New().Title("Fetching...").Action(func() {
375
+ // long-running work
376
+ }).Run()
377
+ ```
378
+
379
+ For determinate progress (downloads, batch processing), use `vbauerster/mpb/v8`:
380
+
381
+ ```go
382
+ import "github.com/vbauerster/mpb/v8"
383
+
384
+ p := mpb.New(mpb.WithWidth(60))
385
+ bar := p.AddBar(int64(total), /* decorators */)
386
+ for i := 0; i < total; i++ {
387
+ work()
388
+ bar.Increment()
389
+ }
390
+ p.Wait()
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Output — JSON vs text
396
+
397
+ Honor `--output json` for any CLI that scripts will parse:
398
+
399
+ ```go
400
+ var outputFmt string
401
+
402
+ rootCmd.PersistentFlags().StringVar(&outputFmt, "output", "text",
403
+ "output format: text or json")
404
+
405
+ func render(v any) error {
406
+ switch outputFmt {
407
+ case "json":
408
+ enc := json.NewEncoder(os.Stdout)
409
+ enc.SetIndent("", " ")
410
+ return enc.Encode(v)
411
+ case "text":
412
+ return renderText(v)
413
+ default:
414
+ return fmt.Errorf("invalid --output %q", outputFmt)
415
+ }
416
+ }
417
+ ```
418
+
419
+ The `text` format uses `lipgloss` tables or `aquasecurity/table` for nicely-aligned columns. The `json` format is for `jq`-style piping.
420
+
421
+ ---
422
+
423
+ ## Error semantics
424
+
425
+ - Return errors from `RunE`. Cobra catches them and the `Execute` wrapper logs + exits non-zero.
426
+ - `os.Exit(1)` should appear **only in `main.go`**. Anywhere else means a subcommand cannot be tested.
427
+ - For graceful early termination ("user cancelled"), return a sentinel and check it in `Execute`:
428
+ ```go
429
+ var ErrCancelled = errors.New("cancelled by user")
430
+ // ... return ErrCancelled
431
+ // in main:
432
+ if errors.Is(err, cmd.ErrCancelled) { os.Exit(130) } // 128 + SIGINT
433
+ ```
434
+
435
+ ---
436
+
437
+ ## Testing CLI commands
438
+
439
+ ```go
440
+ func TestServerCmd_runs_with_default_addr(t *testing.T) {
441
+ // Given
442
+ buf := &bytes.Buffer{}
443
+ rootCmd.SetOut(buf)
444
+ rootCmd.SetErr(buf)
445
+ rootCmd.SetArgs([]string{"server", "--addr", ":0"})
446
+
447
+ // When
448
+ ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
449
+ defer cancel()
450
+ err := rootCmd.ExecuteContext(ctx)
451
+
452
+ // Then
453
+ require.NoError(t, err)
454
+ require.Contains(t, buf.String(), "starting")
455
+ }
456
+ ```
457
+
458
+ `SetArgs` + `ExecuteContext` is the canonical pattern. Bind a ctx with a short deadline for tests that would otherwise block.
459
+
460
+ ---
461
+
462
+ ## Sources
463
+
464
+ - cobra docs: https://github.com/spf13/cobra/blob/main/site/content/user_guide.md
465
+ - pflag: https://github.com/spf13/pflag
466
+ - huh: https://github.com/charmbracelet/huh
467
+ - caarlos0/env: https://github.com/caarlos0/env
468
+ - signal.NotifyContext: https://pkg.go.dev/os/signal#NotifyContext