@rashidee/co2 1.2.5
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/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# Command Patterns — Commander.js Architecture
|
|
2
|
+
|
|
3
|
+
This reference describes the CLI command architecture patterns. Include relevant content
|
|
4
|
+
in Sections 4 and the per-command SPEC.md files.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Architecture Overview
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
cli.ts (Program root)
|
|
12
|
+
└── registerXxxCommand(program) ← Each command in its own file
|
|
13
|
+
├── program.command('xxx')
|
|
14
|
+
│ .argument(...)
|
|
15
|
+
│ .option(...)
|
|
16
|
+
│ .action(handleError(async (arg, opts) => {
|
|
17
|
+
│ // 1. Setup (verbose/json modes)
|
|
18
|
+
│ // 2. Collect missing inputs (prompts if not --yes)
|
|
19
|
+
│ // 3. Validate inputs
|
|
20
|
+
│ // 4. Call service
|
|
21
|
+
│ // 5. Format and print result
|
|
22
|
+
│ }))
|
|
23
|
+
└── (sub-commands registered via .addCommand())
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Dependency direction rule:**
|
|
27
|
+
- `cli.ts` → imports command registrations
|
|
28
|
+
- command files → import services and UI utilities
|
|
29
|
+
- services → import config and HTTP client
|
|
30
|
+
- UI utilities → import chalk/ora/table (no service/config imports)
|
|
31
|
+
- No circular dependencies
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Commander.js Program Setup
|
|
36
|
+
|
|
37
|
+
### Root Program
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// src/cli.ts
|
|
41
|
+
import { Command } from 'commander'
|
|
42
|
+
import { fileURLToPath } from 'node:url'
|
|
43
|
+
import { dirname, join } from 'node:path'
|
|
44
|
+
import { readFileSync } from 'node:fs'
|
|
45
|
+
|
|
46
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
47
|
+
const __dirname = dirname(__filename)
|
|
48
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8')) as {
|
|
49
|
+
name: string; version: string; description: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const program = new Command()
|
|
53
|
+
|
|
54
|
+
program
|
|
55
|
+
.name(pkg.name)
|
|
56
|
+
.description(pkg.description)
|
|
57
|
+
.version(pkg.version, '-V, --version')
|
|
58
|
+
// Global options available on all sub-commands via parent resolution
|
|
59
|
+
.option('-v, --verbose', 'enable verbose output', false)
|
|
60
|
+
.option('--json', 'output as JSON', false)
|
|
61
|
+
.option('--no-color', 'disable ANSI colours')
|
|
62
|
+
.addHelpCommand('help [command]', 'Display help for command')
|
|
63
|
+
// Prevent Commander from catching process.exit(0) as an error
|
|
64
|
+
.exitOverride()
|
|
65
|
+
|
|
66
|
+
// Register commands
|
|
67
|
+
registerInitCommand(program)
|
|
68
|
+
registerDeployCommand(program)
|
|
69
|
+
|
|
70
|
+
// Allow top-level await in the entry point
|
|
71
|
+
await program.parseAsync(process.argv)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Reading Parent Options Inside a Command
|
|
75
|
+
|
|
76
|
+
Commander stores parent options separately. Access global options from inside a command:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
.action(handleError(async (arg: string, opts: LocalOptions, command: Command) => {
|
|
80
|
+
// Walk up to program root for global options
|
|
81
|
+
const globalOpts = command.parent?.opts<GlobalOptions>() ?? {
|
|
82
|
+
verbose: false,
|
|
83
|
+
json: false,
|
|
84
|
+
color: true,
|
|
85
|
+
}
|
|
86
|
+
if (globalOpts.verbose) logger.enableVerbose()
|
|
87
|
+
if (globalOpts.json) output.enableJson()
|
|
88
|
+
}))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Command Patterns by Type
|
|
94
|
+
|
|
95
|
+
### Simple Command (no sub-commands)
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// src/commands/status/index.ts
|
|
99
|
+
import type { Command } from 'commander'
|
|
100
|
+
import { handleError } from '../../errors.js'
|
|
101
|
+
import { output } from '../../ui/output.js'
|
|
102
|
+
import { StatusService } from '../../services/status.service.js'
|
|
103
|
+
|
|
104
|
+
export interface StatusOptions {
|
|
105
|
+
environment: string
|
|
106
|
+
verbose: boolean
|
|
107
|
+
json: boolean
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function registerStatusCommand(program: Command): void {
|
|
111
|
+
program
|
|
112
|
+
.command('status')
|
|
113
|
+
.description('Check the deployment status of an environment')
|
|
114
|
+
.argument('[env]', 'target environment', 'production')
|
|
115
|
+
.option('-e, --environment <env>', 'environment override')
|
|
116
|
+
.action(handleError(async (env: string, opts: StatusOptions, cmd: Command) => {
|
|
117
|
+
const { verbose, json } = cmd.parent?.opts<{ verbose: boolean; json: boolean }>() ?? {}
|
|
118
|
+
if (verbose) logger.enableVerbose()
|
|
119
|
+
if (json) output.enableJson()
|
|
120
|
+
|
|
121
|
+
const service = new StatusService()
|
|
122
|
+
const result = await service.getStatus(opts.environment ?? env)
|
|
123
|
+
output.success(result, `Status: ${result.status}`)
|
|
124
|
+
}))
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Command with Sub-commands
|
|
129
|
+
|
|
130
|
+
Use `.addCommand()` to nest sub-commands. Each sub-command is its own registered handler.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// src/commands/config/index.ts
|
|
134
|
+
import type { Command } from 'commander'
|
|
135
|
+
import { registerConfigGetCommand } from './get.js'
|
|
136
|
+
import { registerConfigSetCommand } from './set.js'
|
|
137
|
+
import { registerConfigListCommand } from './list.js'
|
|
138
|
+
import { registerConfigResetCommand } from './reset.js'
|
|
139
|
+
|
|
140
|
+
export function registerConfigCommand(program: Command): void {
|
|
141
|
+
const configCmd = program
|
|
142
|
+
.command('config')
|
|
143
|
+
.description('Manage {{BINARY_NAME}} configuration')
|
|
144
|
+
.addHelpCommand(false)
|
|
145
|
+
|
|
146
|
+
registerConfigGetCommand(configCmd)
|
|
147
|
+
registerConfigSetCommand(configCmd)
|
|
148
|
+
registerConfigListCommand(configCmd)
|
|
149
|
+
registerConfigResetCommand(configCmd)
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// src/commands/config/get.ts
|
|
155
|
+
import type { Command } from 'commander'
|
|
156
|
+
import { handleError } from '../../errors.js'
|
|
157
|
+
import { getUserConfig } from '../../config/user.config.js'
|
|
158
|
+
import { output } from '../../ui/output.js'
|
|
159
|
+
|
|
160
|
+
export function registerConfigGetCommand(parent: Command): void {
|
|
161
|
+
parent
|
|
162
|
+
.command('get <key>')
|
|
163
|
+
.description('Print the value of a config key')
|
|
164
|
+
.action(handleError(async (key: string) => {
|
|
165
|
+
const config = getUserConfig()
|
|
166
|
+
const value = config.get(key as never)
|
|
167
|
+
if (value === undefined) {
|
|
168
|
+
output.failure('NOT_FOUND', `Config key not found: ${key}`)
|
|
169
|
+
process.exit(3)
|
|
170
|
+
}
|
|
171
|
+
output.success({ key, value }, `${key} = ${String(value)}`)
|
|
172
|
+
}))
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Command with Required Confirmation
|
|
177
|
+
|
|
178
|
+
For destructive operations, require explicit confirmation:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// src/commands/destroy/index.ts
|
|
182
|
+
import { confirm } from '@inquirer/prompts'
|
|
183
|
+
|
|
184
|
+
export function registerDestroyCommand(program: Command): void {
|
|
185
|
+
program
|
|
186
|
+
.command('destroy')
|
|
187
|
+
.description('Permanently delete a project (irreversible)')
|
|
188
|
+
.argument('<project-id>', 'ID of the project to delete')
|
|
189
|
+
.option('-f, --force', 'skip confirmation prompt', false)
|
|
190
|
+
.action(handleError(async (projectId: string, opts: DestroyOptions) => {
|
|
191
|
+
if (!opts.force) {
|
|
192
|
+
const confirmed = await confirm({
|
|
193
|
+
message: `Permanently delete project "${projectId}"? This cannot be undone.`,
|
|
194
|
+
default: false,
|
|
195
|
+
})
|
|
196
|
+
if (!confirmed) {
|
|
197
|
+
logger.info('Aborted.')
|
|
198
|
+
process.exit(0)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const service = new DestroyService()
|
|
203
|
+
await service.deleteProject(projectId)
|
|
204
|
+
output.success({ projectId, deleted: true }, `Project deleted: ${projectId}`)
|
|
205
|
+
}))
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Command with `--dry-run`
|
|
210
|
+
|
|
211
|
+
Every mutating command should support `--dry-run`. The service receives `dryRun` in its
|
|
212
|
+
options and returns a preview without side effects.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
.option('--dry-run', 'preview changes without applying them', false)
|
|
216
|
+
.action(handleError(async (opts: DeployOptions) => {
|
|
217
|
+
const service = new DeployService()
|
|
218
|
+
const result = await service.deploy({ ...opts })
|
|
219
|
+
|
|
220
|
+
if (opts.dryRun) {
|
|
221
|
+
output.success(result, 'Dry run complete — no changes applied')
|
|
222
|
+
} else {
|
|
223
|
+
output.success(result, `Deployed to ${result.environment}`)
|
|
224
|
+
}
|
|
225
|
+
}))
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Command Producing Tabular Output
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { printTable } from '../../ui/table.js'
|
|
232
|
+
import type { GlobalOptions } from '../../types/index.js'
|
|
233
|
+
|
|
234
|
+
.action(handleError(async (_opts: ListOptions, cmd: Command) => {
|
|
235
|
+
const { json } = cmd.parent?.opts<GlobalOptions>() ?? { json: false }
|
|
236
|
+
output.enableJson() // conditionally
|
|
237
|
+
|
|
238
|
+
const service = new ListService()
|
|
239
|
+
const items = await service.list()
|
|
240
|
+
|
|
241
|
+
if (json) {
|
|
242
|
+
output.success(items, '')
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (items.length === 0) {
|
|
247
|
+
logger.info('No items found.')
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
printTable({
|
|
252
|
+
head: ['Name', 'Status', 'Created'],
|
|
253
|
+
rows: items.map(i => [i.name, i.status, i.createdAt]),
|
|
254
|
+
})
|
|
255
|
+
logger.info(`${items.length.toString()} item(s) found.`)
|
|
256
|
+
}))
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Global Option Propagation Pattern
|
|
262
|
+
|
|
263
|
+
Commander does not automatically pass parent options to child commands. Adopt this
|
|
264
|
+
consistent pattern across all command action handlers:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
/** Extract global options from the root program */
|
|
268
|
+
function getGlobalOptions(cmd: Command): GlobalOptions {
|
|
269
|
+
// Walk up to root
|
|
270
|
+
let current: Command = cmd
|
|
271
|
+
while (current.parent) current = current.parent
|
|
272
|
+
return current.opts<GlobalOptions>()
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Or as a shared utility:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// src/utils/command.ts
|
|
280
|
+
import type { Command } from 'commander'
|
|
281
|
+
import type { GlobalOptions } from '../types/index.js'
|
|
282
|
+
|
|
283
|
+
export function setupCommandContext(cmd: Command): void {
|
|
284
|
+
const { verbose, json } = getGlobalOptions(cmd)
|
|
285
|
+
if (verbose) logger.enableVerbose()
|
|
286
|
+
if (json) output.enableJson()
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function getGlobalOptions(cmd: Command): GlobalOptions {
|
|
290
|
+
let root: Command = cmd
|
|
291
|
+
while (root.parent) root = root.parent
|
|
292
|
+
return root.opts<GlobalOptions>()
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Usage in every action handler:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
.action(handleError(async (arg: string, opts: MyOptions, cmd: Command) => {
|
|
300
|
+
setupCommandContext(cmd)
|
|
301
|
+
// rest of handler
|
|
302
|
+
}))
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Command Exit Code Conventions
|
|
308
|
+
|
|
309
|
+
| Scenario | Exit Code |
|
|
310
|
+
|----------------------------------|-----------|
|
|
311
|
+
| Success | `0` |
|
|
312
|
+
| General / unexpected error | `1` |
|
|
313
|
+
| Invalid argument / option value | `2` |
|
|
314
|
+
| Resource not found | `3` |
|
|
315
|
+
| Unauthorized / unauthenticated | `4` |
|
|
316
|
+
| Network / connectivity error | `5` |
|
|
317
|
+
| Configuration error | `6` |
|
|
318
|
+
| User aborted (Ctrl+C, no confirm)| `0` |
|
|
319
|
+
|
|
320
|
+
Always use `process.exit(code)` — never `throw` past the command action handler.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Help Text Conventions
|
|
325
|
+
|
|
326
|
+
Every command and option must have descriptive help text. Commander auto-generates
|
|
327
|
+
`--help` from these strings.
|
|
328
|
+
|
|
329
|
+
**Descriptions should:**
|
|
330
|
+
- Start with a capital letter
|
|
331
|
+
- End without a period (Commander adds none)
|
|
332
|
+
- Be actionable: "Deploy the project to an environment" not "Deployment"
|
|
333
|
+
- Include the default value in option descriptions when non-obvious
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
program
|
|
337
|
+
.command('deploy')
|
|
338
|
+
.description('Deploy the current project to a target environment')
|
|
339
|
+
.option('-e, --environment <name>', 'target environment name', 'staging')
|
|
340
|
+
.option('--strategy <type>', 'deployment strategy: rolling|blue-green', 'rolling')
|
|
341
|
+
.option('--timeout <seconds>', 'deployment timeout in seconds', '300')
|
|
342
|
+
.option('--dry-run', 'preview what would be deployed without deploying')
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Resulting `--help`:
|
|
346
|
+
```
|
|
347
|
+
Usage: my-tool deploy [options]
|
|
348
|
+
|
|
349
|
+
Deploy the current project to a target environment
|
|
350
|
+
|
|
351
|
+
Options:
|
|
352
|
+
-e, --environment <name> target environment name (default: "staging")
|
|
353
|
+
--strategy <type> deployment strategy: rolling|blue-green (default: "rolling")
|
|
354
|
+
--timeout <seconds> deployment timeout in seconds (default: "300")
|
|
355
|
+
--dry-run preview what would be deployed without deploying
|
|
356
|
+
-h, --help display help for command
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Async Error Handling in Commander
|
|
362
|
+
|
|
363
|
+
Commander v12+ supports `parseAsync`. All commands that do async work must be wrapped
|
|
364
|
+
with `handleError` (from `src/errors.ts`) to ensure clean exit codes:
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
// WRONG — unhandled rejection bubbles past Commander
|
|
368
|
+
.action(async (arg, opts) => {
|
|
369
|
+
const result = await service.execute(arg)
|
|
370
|
+
// ...
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
// CORRECT — handleError wraps and formats all thrown errors
|
|
374
|
+
.action(handleError(async (arg: string, opts: Opts) => {
|
|
375
|
+
const result = await service.execute(arg)
|
|
376
|
+
// ...
|
|
377
|
+
}))
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
The `handleError` wrapper catches:
|
|
381
|
+
- `CliError` instances → formats with code and exits with `exitCode`
|
|
382
|
+
- Any other `Error` → prints generic message, logs stack in verbose mode, exits `1`
|
|
383
|
+
- Non-Error throws → converts to string, exits `1`
|