@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,430 @@
|
|
|
1
|
+
# Configuration Management Patterns — conf + cosmiconfig
|
|
2
|
+
|
|
3
|
+
This reference describes the two configuration layers for CLI applications. Include
|
|
4
|
+
relevant content in Sections 9 and 10 of the generated specification.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Configuration Layers Overview
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
12
|
+
│ Precedence order (highest → lowest) │
|
|
13
|
+
│ │
|
|
14
|
+
│ 1. CLI flags --environment staging (per-invocation) │
|
|
15
|
+
│ 2. Env variables MY_TOOL_ENV=staging (per-session) │
|
|
16
|
+
│ 3. Project config .mytoolrc / mytool.config.js (per-project) │
|
|
17
|
+
│ 4. User config ~/.config/my-tool/config.json (per-user) │
|
|
18
|
+
│ 5. Built-in defaults (hardcoded in source) │
|
|
19
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **User config** (`conf`) — global, per-user settings: API tokens, preferences,
|
|
23
|
+
default values. Stored in the OS user config directory
|
|
24
|
+
- **Project config** (`cosmiconfig`) — per-workspace/repo settings: project-specific
|
|
25
|
+
options, team conventions. Committed to source control
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## User Configuration — conf
|
|
30
|
+
|
|
31
|
+
`conf` stores JSON in the OS user config directory and provides a typed, schema-validated
|
|
32
|
+
key-value store. It uses the OS keychain on macOS for sensitive fields (optional).
|
|
33
|
+
|
|
34
|
+
### Setup
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// src/config/user.config.ts
|
|
38
|
+
import Conf from 'conf'
|
|
39
|
+
import { z } from 'zod'
|
|
40
|
+
|
|
41
|
+
// 1. Define schema with Zod for runtime validation
|
|
42
|
+
const UserConfigSchema = z.object({
|
|
43
|
+
apiToken: z.string().optional(),
|
|
44
|
+
apiBaseUrl: z.string().url().default('https://api.example.com'),
|
|
45
|
+
defaultEnv: z.enum(['staging', 'production']).default('staging'),
|
|
46
|
+
telemetryEnabled: z.boolean().default(true),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export type UserConfigData = z.infer<typeof UserConfigSchema>
|
|
50
|
+
|
|
51
|
+
// 2. conf stores values as plain JSON (no Zod at rest — validated on read)
|
|
52
|
+
type ConfStore = Partial<UserConfigData>
|
|
53
|
+
|
|
54
|
+
const conf = new Conf<ConfStore>({
|
|
55
|
+
projectName: '{{BINARY_NAME}}',
|
|
56
|
+
projectVersion: 1, // increment when schema changes to trigger migration
|
|
57
|
+
schema: {
|
|
58
|
+
// JSON Schema (conf uses ajv) for basic type checking
|
|
59
|
+
apiToken: { type: 'string' },
|
|
60
|
+
apiBaseUrl: { type: 'string' },
|
|
61
|
+
defaultEnv: { type: 'string', enum: ['staging', 'production'] },
|
|
62
|
+
telemetryEnabled: { type: 'boolean' },
|
|
63
|
+
},
|
|
64
|
+
defaults: {
|
|
65
|
+
apiBaseUrl: 'https://api.example.com',
|
|
66
|
+
defaultEnv: 'staging',
|
|
67
|
+
telemetryEnabled: true,
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// 3. Typed accessor — always validates on read
|
|
72
|
+
export function getUserConfig(): {
|
|
73
|
+
get<K extends keyof UserConfigData>(key: K): UserConfigData[K]
|
|
74
|
+
set<K extends keyof UserConfigData>(key: K, value: UserConfigData[K]): void
|
|
75
|
+
delete<K extends keyof UserConfigData>(key: K): void
|
|
76
|
+
all(): UserConfigData
|
|
77
|
+
clear(): void
|
|
78
|
+
path: string
|
|
79
|
+
} {
|
|
80
|
+
return {
|
|
81
|
+
get<K extends keyof UserConfigData>(key: K): UserConfigData[K] {
|
|
82
|
+
const raw = conf.get(key)
|
|
83
|
+
const parsed = UserConfigSchema.shape[key].safeParse(raw)
|
|
84
|
+
if (!parsed.success) {
|
|
85
|
+
// Fall back to default
|
|
86
|
+
return UserConfigSchema.shape[key].parse(undefined)
|
|
87
|
+
}
|
|
88
|
+
return parsed.data as UserConfigData[K]
|
|
89
|
+
},
|
|
90
|
+
set<K extends keyof UserConfigData>(key: K, value: UserConfigData[K]): void {
|
|
91
|
+
UserConfigSchema.shape[key].parse(value) // validate before write
|
|
92
|
+
conf.set(key, value)
|
|
93
|
+
},
|
|
94
|
+
delete<K extends keyof UserConfigData>(key: K): void {
|
|
95
|
+
conf.delete(key)
|
|
96
|
+
},
|
|
97
|
+
all(): UserConfigData {
|
|
98
|
+
return UserConfigSchema.parse(conf.store)
|
|
99
|
+
},
|
|
100
|
+
clear(): void {
|
|
101
|
+
conf.clear()
|
|
102
|
+
},
|
|
103
|
+
get path(): string {
|
|
104
|
+
return conf.path
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Config Sub-commands
|
|
111
|
+
|
|
112
|
+
Every CLI using `conf` should expose `config get`, `config set`, `config list`, and
|
|
113
|
+
`config reset` sub-commands so users can inspect and modify their settings without
|
|
114
|
+
editing JSON manually.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// src/commands/config/list.ts
|
|
118
|
+
import type { Command } from 'commander'
|
|
119
|
+
import { handleError } from '../../errors.js'
|
|
120
|
+
import { getUserConfig } from '../../config/user.config.js'
|
|
121
|
+
import { output } from '../../ui/output.js'
|
|
122
|
+
import { printTable } from '../../ui/table.js'
|
|
123
|
+
|
|
124
|
+
export function registerConfigListCommand(parent: Command): void {
|
|
125
|
+
parent
|
|
126
|
+
.command('list')
|
|
127
|
+
.alias('ls')
|
|
128
|
+
.description('List all configuration values')
|
|
129
|
+
.action(handleError(async (_opts, cmd: Command) => {
|
|
130
|
+
const { json } = cmd.parent?.parent?.opts<{ json: boolean }>() ?? { json: false }
|
|
131
|
+
if (json) output.enableJson()
|
|
132
|
+
|
|
133
|
+
const config = getUserConfig()
|
|
134
|
+
const all = config.all()
|
|
135
|
+
|
|
136
|
+
output.success(all, '') // JSON mode
|
|
137
|
+
|
|
138
|
+
if (!json) {
|
|
139
|
+
printTable({
|
|
140
|
+
head: ['Key', 'Value'],
|
|
141
|
+
rows: Object.entries(all).map(([k, v]) => [k, String(v)]),
|
|
142
|
+
})
|
|
143
|
+
logger.info(`Config stored at: ${config.path}`)
|
|
144
|
+
}
|
|
145
|
+
}))
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// src/commands/config/set.ts
|
|
151
|
+
import type { Command } from 'commander'
|
|
152
|
+
import { handleError, CliError } from '../../errors.js'
|
|
153
|
+
import { getUserConfig } from '../../config/user.config.js'
|
|
154
|
+
import { output } from '../../ui/output.js'
|
|
155
|
+
import type { UserConfigData } from '../../config/user.config.js'
|
|
156
|
+
|
|
157
|
+
export function registerConfigSetCommand(parent: Command): void {
|
|
158
|
+
parent
|
|
159
|
+
.command('set <key> <value>')
|
|
160
|
+
.description('Set a configuration value')
|
|
161
|
+
.action(handleError(async (key: string, rawValue: string) => {
|
|
162
|
+
const config = getUserConfig()
|
|
163
|
+
const validKeys = Object.keys(config.all()) as (keyof UserConfigData)[]
|
|
164
|
+
|
|
165
|
+
if (!validKeys.includes(key as keyof UserConfigData)) {
|
|
166
|
+
throw CliError.invalidInput(
|
|
167
|
+
`Unknown config key: "${key}". Valid keys: ${validKeys.join(', ')}`
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Coerce string to correct type
|
|
172
|
+
let value: unknown = rawValue
|
|
173
|
+
if (rawValue === 'true') value = true
|
|
174
|
+
if (rawValue === 'false') value = false
|
|
175
|
+
if (!isNaN(Number(rawValue)) && rawValue !== '') value = Number(rawValue)
|
|
176
|
+
|
|
177
|
+
config.set(key as keyof UserConfigData, value as never)
|
|
178
|
+
output.success({ key, value }, `Set ${key} = ${rawValue}`)
|
|
179
|
+
}))
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// src/commands/config/reset.ts
|
|
185
|
+
import type { Command } from 'commander'
|
|
186
|
+
import { confirm } from '@inquirer/prompts'
|
|
187
|
+
import { handleError } from '../../errors.js'
|
|
188
|
+
import { getUserConfig } from '../../config/user.config.js'
|
|
189
|
+
import { output, logger } from '../../ui/output.js'
|
|
190
|
+
|
|
191
|
+
export function registerConfigResetCommand(parent: Command): void {
|
|
192
|
+
parent
|
|
193
|
+
.command('reset')
|
|
194
|
+
.description('Reset all configuration to defaults')
|
|
195
|
+
.option('-f, --force', 'skip confirmation', false)
|
|
196
|
+
.action(handleError(async (opts: { force: boolean }) => {
|
|
197
|
+
if (!opts.force) {
|
|
198
|
+
const confirmed = await confirm({
|
|
199
|
+
message: 'Reset all configuration to defaults?',
|
|
200
|
+
default: false,
|
|
201
|
+
})
|
|
202
|
+
if (!confirmed) {
|
|
203
|
+
logger.info('Aborted.')
|
|
204
|
+
process.exit(0)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
getUserConfig().clear()
|
|
208
|
+
output.success({}, 'Configuration reset to defaults')
|
|
209
|
+
}))
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Login / Logout Commands *(for API-token-based auth)*
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// src/commands/login/index.ts
|
|
217
|
+
import { password } from '@inquirer/prompts'
|
|
218
|
+
import type { Command } from 'commander'
|
|
219
|
+
import { handleError } from '../../errors.js'
|
|
220
|
+
import { getUserConfig } from '../../config/user.config.js'
|
|
221
|
+
import { AuthService } from '../../services/auth.service.js'
|
|
222
|
+
import { output } from '../../ui/output.js'
|
|
223
|
+
|
|
224
|
+
export function registerLoginCommand(program: Command): void {
|
|
225
|
+
program
|
|
226
|
+
.command('login')
|
|
227
|
+
.description('Authenticate with {{APPLICATION_NAME}}')
|
|
228
|
+
.option('--token <token>', 'API token (or use interactive prompt)')
|
|
229
|
+
.action(handleError(async (opts: { token?: string }) => {
|
|
230
|
+
const token = opts.token ?? await password({
|
|
231
|
+
message: 'Enter your API token:',
|
|
232
|
+
mask: '*',
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const authService = new AuthService()
|
|
236
|
+
const user = await authService.validateToken(token)
|
|
237
|
+
|
|
238
|
+
getUserConfig().set('apiToken', token)
|
|
239
|
+
|
|
240
|
+
output.success({ user: user.email }, `Logged in as ${user.email}`)
|
|
241
|
+
}))
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// src/commands/logout/index.ts
|
|
247
|
+
export function registerLogoutCommand(program: Command): void {
|
|
248
|
+
program
|
|
249
|
+
.command('logout')
|
|
250
|
+
.description('Remove stored credentials')
|
|
251
|
+
.action(handleError(async () => {
|
|
252
|
+
getUserConfig().delete('apiToken')
|
|
253
|
+
output.success({}, 'Logged out — credentials removed')
|
|
254
|
+
}))
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Project Configuration — cosmiconfig
|
|
261
|
+
|
|
262
|
+
`cosmiconfig` searches for a project-level config file starting from the current working
|
|
263
|
+
directory and traversing upward. Useful when the CLI tool operates within a project
|
|
264
|
+
(e.g., a deployment tool, a linter, a build tool).
|
|
265
|
+
|
|
266
|
+
### Supported Config File Formats
|
|
267
|
+
|
|
268
|
+
cosmiconfig automatically searches for (in order):
|
|
269
|
+
- `package.json` — `"{{BINARY_NAME}}"` field
|
|
270
|
+
- `.{{BINARY_NAME}}rc` — JSON or YAML
|
|
271
|
+
- `.{{BINARY_NAME}}rc.json`
|
|
272
|
+
- `.{{BINARY_NAME}}rc.yaml` / `.{{BINARY_NAME}}rc.yml`
|
|
273
|
+
- `.{{BINARY_NAME}}rc.js` / `.{{BINARY_NAME}}rc.mjs` / `.{{BINARY_NAME}}rc.cjs`
|
|
274
|
+
- `{{BINARY_NAME}}.config.js` / `{{BINARY_NAME}}.config.mjs` / `{{BINARY_NAME}}.config.cjs`
|
|
275
|
+
|
|
276
|
+
### Setup
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// src/config/project.config.ts
|
|
280
|
+
import { cosmiconfig } from 'cosmiconfig'
|
|
281
|
+
import { z } from 'zod'
|
|
282
|
+
import { CliError } from '../errors.js'
|
|
283
|
+
|
|
284
|
+
// 1. Define schema
|
|
285
|
+
const ProjectConfigSchema = z.object({
|
|
286
|
+
environment: z.enum(['staging', 'production', 'development']).default('staging'),
|
|
287
|
+
region: z.string().default('us-east-1'),
|
|
288
|
+
services: z.array(z.string()).default([]),
|
|
289
|
+
hooks: z.object({
|
|
290
|
+
preDeploy: z.string().optional(),
|
|
291
|
+
postDeploy: z.string().optional(),
|
|
292
|
+
}).default({}),
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>
|
|
296
|
+
|
|
297
|
+
const DEFAULT_CONFIG: ProjectConfig = ProjectConfigSchema.parse({})
|
|
298
|
+
|
|
299
|
+
// 2. Cached search result
|
|
300
|
+
let _cachedConfig: ProjectConfig | null = null
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Load and validate the project config file. Searches up from CWD.
|
|
304
|
+
* Returns defaults if no config file found (non-fatal).
|
|
305
|
+
*/
|
|
306
|
+
export async function loadProjectConfig(searchFrom?: string): Promise<ProjectConfig> {
|
|
307
|
+
if (_cachedConfig) return _cachedConfig
|
|
308
|
+
|
|
309
|
+
const explorer = cosmiconfig('{{BINARY_NAME}}', {
|
|
310
|
+
searchStrategy: 'project',
|
|
311
|
+
stopDir: process.env['HOME'] ?? '/',
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
const result = await explorer.search(searchFrom ?? process.cwd())
|
|
315
|
+
|
|
316
|
+
if (!result || result.isEmpty) {
|
|
317
|
+
_cachedConfig = DEFAULT_CONFIG
|
|
318
|
+
return DEFAULT_CONFIG
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const parsed = ProjectConfigSchema.safeParse(result.config)
|
|
322
|
+
if (!parsed.success) {
|
|
323
|
+
throw CliError.configError(
|
|
324
|
+
`Invalid config in ${result.filepath}:\n${parsed.error.message}`
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
_cachedConfig = parsed.data
|
|
329
|
+
return parsed.data
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Reset the config cache (useful in tests) */
|
|
333
|
+
export function resetProjectConfigCache(): void {
|
|
334
|
+
_cachedConfig = null
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Merging Config Precedence
|
|
339
|
+
|
|
340
|
+
In command handlers, merge configuration sources in the correct precedence order:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
// src/utils/resolve-config.ts
|
|
344
|
+
import { loadProjectConfig } from '../config/project.config.js'
|
|
345
|
+
import { getUserConfig } from '../config/user.config.js'
|
|
346
|
+
import type { DeployOptions } from '../commands/deploy/index.js'
|
|
347
|
+
|
|
348
|
+
export interface ResolvedDeployConfig {
|
|
349
|
+
environment: string
|
|
350
|
+
region: string
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export async function resolveDeployConfig(
|
|
354
|
+
cliOpts: Partial<DeployOptions>
|
|
355
|
+
): Promise<ResolvedDeployConfig> {
|
|
356
|
+
const projectConfig = await loadProjectConfig()
|
|
357
|
+
const userConfig = getUserConfig()
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
// CLI flag > env var > project config > user default > hardcoded default
|
|
361
|
+
environment:
|
|
362
|
+
cliOpts.environment // CLI flag (highest)
|
|
363
|
+
?? process.env['MY_TOOL_ENV'] // env var
|
|
364
|
+
?? projectConfig.environment // project config
|
|
365
|
+
?? userConfig.get('defaultEnv') // user preference
|
|
366
|
+
?? 'staging', // hardcoded default (lowest)
|
|
367
|
+
|
|
368
|
+
region:
|
|
369
|
+
cliOpts.region
|
|
370
|
+
?? process.env['MY_TOOL_REGION']
|
|
371
|
+
?? projectConfig.region
|
|
372
|
+
?? 'us-east-1',
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Sample Config Files
|
|
378
|
+
|
|
379
|
+
The spec must include example config files that developers can copy:
|
|
380
|
+
|
|
381
|
+
**`.mytoolrc.json`**
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"environment": "production",
|
|
385
|
+
"region": "eu-west-1",
|
|
386
|
+
"services": ["api", "worker"],
|
|
387
|
+
"hooks": {
|
|
388
|
+
"preDeploy": "npm test",
|
|
389
|
+
"postDeploy": "npm run notify"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**`mytool.config.js`** (for dynamic configs using JS)
|
|
395
|
+
```javascript
|
|
396
|
+
// mytool.config.js
|
|
397
|
+
export default {
|
|
398
|
+
environment: process.env.NODE_ENV === 'production' ? 'production' : 'staging',
|
|
399
|
+
region: 'us-east-1',
|
|
400
|
+
services: ['api', 'worker'],
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**`package.json` field**
|
|
405
|
+
```json
|
|
406
|
+
{
|
|
407
|
+
"name": "my-project",
|
|
408
|
+
"my-tool": {
|
|
409
|
+
"environment": "staging",
|
|
410
|
+
"services": ["api"]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Environment Variable Support
|
|
418
|
+
|
|
419
|
+
Every config key should have an environment variable equivalent. Define a mapping table
|
|
420
|
+
in the spec:
|
|
421
|
+
|
|
422
|
+
| Config Key | Environment Variable | Priority |
|
|
423
|
+
|--------------------|---------------------------|----------|
|
|
424
|
+
| `environment` | `MY_TOOL_ENV` | 2nd |
|
|
425
|
+
| `region` | `MY_TOOL_REGION` | 2nd |
|
|
426
|
+
| `apiToken` | `MY_TOOL_TOKEN` | 2nd |
|
|
427
|
+
| `apiBaseUrl` | `MY_TOOL_API_URL` | 2nd |
|
|
428
|
+
|
|
429
|
+
Load environment variables in the `resolveConfig` utility, not in services directly.
|
|
430
|
+
This keeps services testable without process.env manipulation.
|