@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.
Files changed (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,1391 @@
1
+ # Specification Template — TypeScript CLI Application
2
+
3
+ This is the authoritative template for the generated specification. The specification is
4
+ split into **two types of files**:
5
+
6
+ 1. **`SPECIFICATION.md`** (root) — Table of Contents, shared infrastructure, and all
7
+ application-level sections. Generated once per application.
8
+ 2. **`<command-name>/SPEC.md`** (per-command) — Self-contained command blueprint.
9
+ Generated once per command from PRD.md.
10
+
11
+ ```
12
+ specification/
13
+ ├── SPECIFICATION.md
14
+ ├── init/
15
+ │ └── SPEC.md
16
+ ├── deploy/
17
+ │ └── SPEC.md
18
+ └── ...
19
+ ```
20
+
21
+ Placeholders use `{{VARIABLE}}` syntax and must be replaced with actual values.
22
+
23
+ ---
24
+
25
+ # Part A: Root SPECIFICATION.md
26
+
27
+ ---
28
+
29
+ ## Table of Contents
30
+
31
+ Generate a TOC with anchor links. Include a **Commands** section linking to each
32
+ command's `SPEC.md`:
33
+
34
+ ```markdown
35
+ ## Table of Contents
36
+
37
+ ### Shared Infrastructure
38
+ - [1. Project Overview](#1-project-overview)
39
+ - [2. package.json Configuration](#2-packagejson-configuration)
40
+ - ...
41
+
42
+ ### Commands
43
+ - [init](init/SPEC.md)
44
+ - [deploy](deploy/SPEC.md)
45
+ - [config](config/SPEC.md)
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Section 1: Project Overview
51
+
52
+ ```
53
+ # {{APPLICATION_NAME}} — Technical Specification
54
+
55
+ ## 1. Project Overview
56
+
57
+ **Application Name**: {{APPLICATION_NAME}}
58
+ **Package Name**: {{PACKAGE_NAME}} (e.g., @scope/my-tool or my-tool)
59
+ **Binary Name**: {{BINARY_NAME}} (the executable command, e.g., my-tool)
60
+ **Node.js Version**: 22.x LTS (minimum)
61
+ **TypeScript**: 5.x
62
+ **Description**: {{APP_DESCRIPTION}}
63
+ **Versions Covered**: v0.1.0 — v{{LATEST_VERSION}}
64
+
65
+ ### Optional Components (Auto-Determined)
66
+ **Prompts**: {{yes|no}}
67
+ **User Config**: {{yes|no}}
68
+ **Project Config**: {{yes|no}}
69
+ **Shell Execution**: {{yes|no}}
70
+ **HTTP Client**: {{yes|no}}
71
+ **Auto-update**: {{yes|no}}
72
+ **Binary Packaging**: {{yes|no}}
73
+ **Table Output**: {{yes|no}}
74
+
75
+ ### Technology Stack
76
+ (Render core stack table from SKILL.md, plus selected optional versions)
77
+
78
+ ### Commands
79
+ | Command | Description | Aliases | Version |
80
+ |----------|----------------------------|---------|---------|
81
+ | init | Initialize a new project | i | v0.1.0 |
82
+ | deploy | Deploy to environment | d | v0.1.0 |
83
+ | config | Manage user configuration | | v0.1.0 |
84
+
85
+ ### Distribution Strategy
86
+ **npm package**: `npm install -g {{PACKAGE_NAME}}`
87
+ [If Binary Packaging = yes] **Standalone binaries**: GitHub Releases — Windows, macOS, Linux
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Section 2: package.json Configuration
93
+
94
+ Generate a complete `package.json` inside a fenced code block. Must be copy-pasteable.
95
+
96
+ ```json
97
+ {
98
+ "name": "{{PACKAGE_NAME}}",
99
+ "version": "0.1.0",
100
+ "description": "{{APP_DESCRIPTION}}",
101
+ "type": "module",
102
+ "license": "MIT",
103
+ "engines": {
104
+ "node": ">=22.0.0"
105
+ },
106
+ "bin": {
107
+ "{{BINARY_NAME}}": "./dist/cli.js"
108
+ },
109
+ "files": [
110
+ "dist",
111
+ "README.md"
112
+ ],
113
+ "scripts": {
114
+ "dev": "tsx watch src/cli.ts",
115
+ "build": "tsup",
116
+ "typecheck": "tsc --noEmit",
117
+ "test": "vitest run",
118
+ "test:watch": "vitest",
119
+ "lint": "eslint src --ext .ts",
120
+ "prepublishOnly": "npm run build && npm run typecheck && npm run test"
121
+ },
122
+ "dependencies": {
123
+ "commander": "^12.0.0",
124
+ "chalk": "^5.0.0",
125
+ "ora": "^8.0.0",
126
+ "zod": "^3.0.0"
127
+ // [conditional deps listed per section below]
128
+ },
129
+ "devDependencies": {
130
+ "typescript": "^5.0.0",
131
+ "tsup": "^8.0.0",
132
+ "vitest": "^2.0.0",
133
+ "@types/node": "^22.0.0",
134
+ "tsx": "^4.0.0",
135
+ "eslint": "^9.0.0",
136
+ "@typescript-eslint/parser": "^8.0.0",
137
+ "@typescript-eslint/eslint-plugin": "^8.0.0"
138
+ }
139
+ }
140
+ ```
141
+
142
+ **[If Prompts = yes]** add to `dependencies`:
143
+ ```json
144
+ "@inquirer/prompts": "^7.0.0"
145
+ ```
146
+
147
+ **[If User Config = yes]** add to `dependencies`:
148
+ ```json
149
+ "conf": "^13.0.0"
150
+ ```
151
+
152
+ **[If Project Config = yes]** add to `dependencies`:
153
+ ```json
154
+ "cosmiconfig": "^9.0.0"
155
+ ```
156
+
157
+ **[If Shell Execution = yes]** add to `dependencies`:
158
+ ```json
159
+ "execa": "^9.0.0"
160
+ ```
161
+
162
+ **[If HTTP Client = yes]** add to `dependencies`:
163
+ ```json
164
+ "got": "^14.0.0"
165
+ ```
166
+
167
+ **[If Auto-update = yes]** add to `dependencies`:
168
+ ```json
169
+ "update-notifier": "^7.0.0"
170
+ ```
171
+
172
+ **[If Table Output = yes]** add to `dependencies`:
173
+ ```json
174
+ "cli-table3": "^0.6.0",
175
+ "@types/cli-table3": "^0.6.0"
176
+ ```
177
+
178
+ **[If Local Database = yes]** add to `dependencies` and `devDependencies`:
179
+ ```json
180
+ // dependencies:
181
+ "better-sqlite3": "^11.0.0",
182
+ "drizzle-orm": "^0.36.0",
183
+ "env-paths": "^3.0.0",
184
+
185
+ // devDependencies:
186
+ "@types/better-sqlite3": "^7.0.0",
187
+ "drizzle-kit": "^0.28.0"
188
+ ```
189
+
190
+ And add scripts:
191
+ ```json
192
+ "db:generate": "drizzle-kit generate",
193
+ "db:migrate": "drizzle-kit migrate",
194
+ "db:studio": "drizzle-kit studio"
195
+ ```
196
+
197
+ **[If Binary Packaging = yes]** add to `devDependencies` and scripts:
198
+ ```json
199
+ // devDependencies:
200
+ "@yao-pkg/pkg": "^5.0.0"
201
+
202
+ // scripts:
203
+ "pkg:linux": "pkg . --target node22-linux-x64 --output dist/bin/{{BINARY_NAME}}-linux",
204
+ "pkg:macos": "pkg . --target node22-macos-x64 --output dist/bin/{{BINARY_NAME}}-macos",
205
+ "pkg:windows": "pkg . --target node22-win-x64 --output dist/bin/{{BINARY_NAME}}.exe",
206
+ "pkg:all": "npm run pkg:linux && npm run pkg:macos && npm run pkg:windows"
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Section 3: TypeScript & Build Configuration
212
+
213
+ ### 3.1 tsconfig.json
214
+
215
+ ```json
216
+ {
217
+ "compilerOptions": {
218
+ "target": "ES2022",
219
+ "module": "NodeNext",
220
+ "moduleResolution": "NodeNext",
221
+ "lib": ["ES2022"],
222
+ "outDir": "./dist",
223
+ "rootDir": "./src",
224
+ "strict": true,
225
+ "noUncheckedIndexedAccess": true,
226
+ "exactOptionalPropertyTypes": true,
227
+ "declaration": true,
228
+ "declarationMap": true,
229
+ "sourceMap": true,
230
+ "skipLibCheck": true,
231
+ "esModuleInterop": false,
232
+ "forceConsistentCasingInFileNames": true,
233
+ "resolveJsonModule": true
234
+ },
235
+ "include": ["src/**/*"],
236
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
237
+ }
238
+ ```
239
+
240
+ ### 3.2 tsup.config.ts
241
+
242
+ ```typescript
243
+ import { defineConfig } from 'tsup'
244
+
245
+ export default defineConfig({
246
+ entry: ['src/cli.ts'],
247
+ format: ['esm'],
248
+ target: 'node22',
249
+ platform: 'node',
250
+ clean: true,
251
+ sourcemap: process.env.NODE_ENV !== 'production',
252
+ minify: false,
253
+ shims: false,
254
+ dts: false,
255
+ banner: {
256
+ js: '#!/usr/bin/env node',
257
+ },
258
+ esbuildOptions(options) {
259
+ options.mainFields = ['module', 'main']
260
+ },
261
+ })
262
+ ```
263
+
264
+ ### 3.3 .eslintrc.json
265
+
266
+ ```json
267
+ {
268
+ "root": true,
269
+ "parser": "@typescript-eslint/parser",
270
+ "parserOptions": { "project": "./tsconfig.json" },
271
+ "plugins": ["@typescript-eslint"],
272
+ "extends": [
273
+ "eslint:recommended",
274
+ "plugin:@typescript-eslint/strict-type-checked"
275
+ ],
276
+ "rules": {
277
+ "@typescript-eslint/no-explicit-any": "error",
278
+ "@typescript-eslint/no-floating-promises": "error",
279
+ "no-console": ["error", { "allow": [] }]
280
+ }
281
+ }
282
+ ```
283
+
284
+ ### 3.4 .gitignore
285
+
286
+ ```gitignore
287
+ # Build output
288
+ dist/
289
+ *.d.ts
290
+
291
+ # Binaries
292
+ dist/bin/
293
+
294
+ # Dependencies
295
+ node_modules/
296
+
297
+ # Testing
298
+ coverage/
299
+
300
+ # Environment
301
+ .env
302
+ .env.*
303
+
304
+ # OS
305
+ .DS_Store
306
+ Thumbs.db
307
+
308
+ # IDE
309
+ .vscode/
310
+ .idea/
311
+ *.iml
312
+
313
+ # Logs
314
+ *.log
315
+ logs/
316
+
317
+ # Local development database [If Local Database = yes]
318
+ dev.db
319
+ dev.db-shm
320
+ dev.db-wal
321
+ .drizzle/
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Section 4: Application Entry Point
327
+
328
+ `src/cli.ts` is the Commander.js program root. It must:
329
+ 1. Inject the shebang (`#!/usr/bin/env node`) via tsup banner
330
+ 2. Optionally check for updates (non-blocking)
331
+ 3. Create the root `Command` with name, description, version
332
+ 4. Register global options shared by all commands
333
+ 5. Import and register each command
334
+ 6. Call `program.parse()`
335
+
336
+ ```typescript
337
+ // src/cli.ts
338
+ import { Command } from 'commander'
339
+ import { createRequire } from 'node:module'
340
+ import { fileURLToPath } from 'node:url'
341
+ import { dirname, join } from 'node:path'
342
+ import { readFileSync } from 'node:fs'
343
+ import { registerInitCommand } from './commands/init/index.js'
344
+ import { registerDeployCommand } from './commands/deploy/index.js'
345
+ import { registerConfigCommand } from './commands/config/index.js'
346
+ // [If Auto-update = yes]:
347
+ import { checkForUpdates } from './utils/updater.js'
348
+
349
+ const __filename = fileURLToPath(import.meta.url)
350
+ const __dirname = dirname(__filename)
351
+
352
+ const pkg = JSON.parse(
353
+ readFileSync(join(__dirname, '../package.json'), 'utf-8')
354
+ ) as { name: string; version: string; description: string }
355
+
356
+ // [If Auto-update = yes]: Fire and forget — never blocks the command
357
+ void checkForUpdates(pkg.name, pkg.version)
358
+
359
+ const program = new Command()
360
+
361
+ program
362
+ .name('{{BINARY_NAME}}')
363
+ .description(pkg.description)
364
+ .version(pkg.version, '-V, --version', 'output the current version')
365
+ .option('-v, --verbose', 'enable verbose output', false)
366
+ .option('--json', 'output results as JSON', false)
367
+ .option('--no-color', 'disable color output')
368
+ .addHelpCommand(true)
369
+
370
+ registerInitCommand(program)
371
+ registerDeployCommand(program)
372
+ registerConfigCommand(program)
373
+
374
+ program.parseAsync(process.argv).catch((err: unknown) => {
375
+ // Top-level fallback — most errors are caught inside commands
376
+ console.error(String(err))
377
+ process.exit(1)
378
+ })
379
+ ```
380
+
381
+ **[If Background Daemon = yes]** insert the daemon detection block immediately before
382
+ `const program = new Command()`:
383
+
384
+ ```typescript
385
+ // Must appear BEFORE Commander initialises — invisible to --help output
386
+ if (process.env['{{BINARY_NAME_UPPER}}_DAEMON_MODE'] === '1') {
387
+ const { runDaemon } = await import('./daemon/runner.js')
388
+ await runDaemon()
389
+ process.exit(0)
390
+ }
391
+ ```
392
+
393
+ > See `references/command-patterns.md` for the complete Commander.js patterns: root
394
+ > program setup, global option propagation (`getGlobalOptions`), sub-command groups,
395
+ > `--dry-run`, confirmation prompts, tabular output, help text conventions, exit code
396
+ > table, and the `handleError` async wrapper used in every action handler.
397
+
398
+ ---
399
+
400
+ ## Section 5: Project Directory Structure
401
+
402
+ ```
403
+ {{BINARY_NAME}}/
404
+ ├── src/
405
+ │ ├── cli.ts ← Program entry point
406
+ │ ├── types/
407
+ │ │ └── index.ts ← Shared TypeScript types
408
+ │ ├── errors.ts ← CliError class + handleError()
409
+ │ ├── commands/
410
+ │ │ ├── init/
411
+ │ │ │ ├── index.ts ← registerInitCommand(program)
412
+ │ │ │ └── prompts.ts ← [If Prompts] Inquirer prompt flows
413
+ │ │ ├── deploy/
414
+ │ │ │ ├── index.ts
415
+ │ │ │ └── prompts.ts
416
+ │ │ └── config/ ← [If User Config] config sub-commands
417
+ │ │ └── index.ts
418
+ │ ├── services/
419
+ │ │ ├── init.service.ts ← InitService (no CLI I/O)
420
+ │ │ ├── deploy.service.ts ← DeployService
421
+ │ │ └── config.service.ts ← [If User Config] ConfigService
422
+ │ ├── config/
423
+ │ │ ├── user.config.ts ← [If User Config] conf setup
424
+ │ │ └── project.config.ts ← [If Project Config] cosmiconfig loader
425
+ │ ├── services/
426
+ │ │ └── http.client.ts ← [If HTTP Client] got instance
427
+ │ ├── db/ ← [If Local Database]
428
+ │ │ ├── client.ts ← better-sqlite3 singleton + migration runner
429
+ │ │ ├── path.ts ← OS data directory via env-paths
430
+ │ │ ├── schema.ts ← Drizzle table definitions (all entities)
431
+ │ │ └── repositories/
432
+ │ │ └── <entity>.repository.ts ← One file per entity type
433
+ │ ├── utils/
434
+ │ │ ├── signal.ts ← [If any Async] SIGINT/SIGTERM handler
435
+ │ │ ├── poll.ts ← [If Polling] polling loop utility
436
+ │ │ ├── batch.ts ← [If Inline Batch] chunk processor
437
+ │ │ ├── shell.ts ← [If Shell Execution] execa wrapper
438
+ │ │ └── updater.ts ← [If Auto-update] update-notifier
439
+ │ ├── daemon/
440
+ │ │ └── runner.ts ← [If Background Daemon] daemon event loop
441
+ │ └── ui/
442
+ │ ├── logger.ts ← chalk-based logger
443
+ │ ├── spinner.ts ← ora wrapper
444
+ │ ├── progress.ts ← [If Inline Batch] ProgressBar (ora + percentage)
445
+ │ ├── table.ts ← [If Table Output] cli-table3 wrapper
446
+ │ └── output.ts ← Unified output (human + --json)
447
+ ├── test/
448
+ │ ├── commands/
449
+ │ │ ├── init.test.ts
450
+ │ │ └── deploy.test.ts
451
+ │ └── services/
452
+ │ ├── init.service.test.ts
453
+ │ └── deploy.service.test.ts
454
+ ├── tsconfig.json
455
+ ├── tsup.config.ts
456
+ ├── vitest.config.ts
457
+ ├── drizzle.config.ts ← [If Local Database]
458
+ ├── package.json
459
+ ├── .eslintrc.json
460
+ ├── .gitignore
461
+ └── README.md
462
+
463
+ drizzle/ ← [If Local Database] Generated SQL migrations
464
+ ├── 0000_initial_schema.sql
465
+ └── meta/
466
+ └── _journal.json
467
+ ```
468
+
469
+ Use actual command names from PRD.md throughout — never generic placeholders like
470
+ `command1`, `command2`.
471
+
472
+ ---
473
+
474
+ ## Section 6: Shared Types
475
+
476
+ ```typescript
477
+ // src/types/index.ts
478
+
479
+ /** Global options available on every command */
480
+ export interface GlobalOptions {
481
+ verbose: boolean
482
+ json: boolean
483
+ color: boolean
484
+ }
485
+
486
+ /** Standard success result shape for --json output */
487
+ export interface CliResult<T = unknown> {
488
+ success: true
489
+ data: T
490
+ }
491
+
492
+ /** Standard error result shape for --json output */
493
+ export interface CliErrorResult {
494
+ success: false
495
+ error: {
496
+ code: string
497
+ message: string
498
+ }
499
+ }
500
+
501
+ export type CliOutput<T = unknown> = CliResult<T> | CliErrorResult
502
+
503
+ // Add application-specific shared types from MODEL.md here
504
+ // e.g.:
505
+ // export interface ProjectConfig { ... }
506
+ // export interface DeployTarget { name: string; url: string }
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Section 7: Terminal UI Utilities
512
+
513
+ ### 7.1 Logger
514
+
515
+ ```typescript
516
+ // src/ui/logger.ts
517
+ import chalk from 'chalk'
518
+
519
+ let verboseEnabled = false
520
+
521
+ export const logger = {
522
+ enableVerbose(): void { verboseEnabled = true },
523
+
524
+ info(message: string): void {
525
+ process.stdout.write(chalk.blue('ℹ') + ' ' + message + '\n')
526
+ },
527
+
528
+ success(message: string): void {
529
+ process.stdout.write(chalk.green('✔') + ' ' + message + '\n')
530
+ },
531
+
532
+ warn(message: string): void {
533
+ process.stderr.write(chalk.yellow('⚠') + ' ' + chalk.yellow(message) + '\n')
534
+ },
535
+
536
+ error(message: string): void {
537
+ process.stderr.write(chalk.red('✖') + ' ' + chalk.red(message) + '\n')
538
+ },
539
+
540
+ debug(message: string): void {
541
+ if (!verboseEnabled) return
542
+ process.stderr.write(chalk.gray('[debug] ') + chalk.gray(message) + '\n')
543
+ },
544
+
545
+ /** Print a section header */
546
+ heading(message: string): void {
547
+ process.stdout.write('\n' + chalk.bold(message) + '\n')
548
+ },
549
+ }
550
+ ```
551
+
552
+ ### 7.2 Spinner
553
+
554
+ ```typescript
555
+ // src/ui/spinner.ts
556
+ import ora, { type Ora } from 'ora'
557
+
558
+ export function createSpinner(text: string): Ora {
559
+ return ora({ text, color: 'cyan' })
560
+ }
561
+
562
+ export async function withSpinner<T>(
563
+ text: string,
564
+ fn: () => Promise<T>,
565
+ successText?: string,
566
+ ): Promise<T> {
567
+ const spinner = createSpinner(text)
568
+ spinner.start()
569
+ try {
570
+ const result = await fn()
571
+ spinner.succeed(successText ?? text)
572
+ return result
573
+ } catch (error) {
574
+ spinner.fail()
575
+ throw error
576
+ }
577
+ }
578
+ ```
579
+
580
+ ### 7.3 Output Handler
581
+
582
+ ```typescript
583
+ // src/ui/output.ts
584
+ import type { CliOutput } from '../types/index.js'
585
+ import { logger } from './logger.js'
586
+
587
+ let jsonMode = false
588
+
589
+ export const output = {
590
+ enableJson(): void { jsonMode = true },
591
+
592
+ /** Print a success result — JSON if --json, formatted otherwise */
593
+ success<T>(data: T, humanSummary: string): void {
594
+ if (jsonMode) {
595
+ process.stdout.write(JSON.stringify({ success: true, data } satisfies CliOutput<T>, null, 2) + '\n')
596
+ } else {
597
+ logger.success(humanSummary)
598
+ }
599
+ },
600
+
601
+ /** Print an error result — JSON if --json, formatted otherwise */
602
+ failure(code: string, message: string): void {
603
+ if (jsonMode) {
604
+ process.stdout.write(JSON.stringify({ success: false, error: { code, message } } satisfies CliOutput, null, 2) + '\n')
605
+ } else {
606
+ logger.error(message)
607
+ }
608
+ },
609
+
610
+ /** Print raw human-readable text (not affected by --json) */
611
+ raw(text: string): void {
612
+ process.stdout.write(text + '\n')
613
+ },
614
+ }
615
+ ```
616
+
617
+ ### 7.4 Table Output *(conditional — include only if Table Output = yes)*
618
+
619
+ ```typescript
620
+ // src/ui/table.ts
621
+ import Table from 'cli-table3'
622
+
623
+ export interface TableOptions {
624
+ head: string[]
625
+ rows: string[][]
626
+ compact?: boolean
627
+ }
628
+
629
+ export function printTable({ head, rows, compact = false }: TableOptions): void {
630
+ const table = new Table({
631
+ head: head.map(h => `\u001b[1m${h}\u001b[0m`), // bold headers
632
+ style: compact ? { compact: true } : {},
633
+ })
634
+ for (const row of rows) {
635
+ table.push(row)
636
+ }
637
+ process.stdout.write(table.toString() + '\n')
638
+ }
639
+ ```
640
+
641
+ ---
642
+
643
+ ## Section 8: Error Handling
644
+
645
+ ```typescript
646
+ // src/errors.ts
647
+ import { output } from './ui/output.js'
648
+ import { logger } from './ui/logger.js'
649
+
650
+ export const ExitCodes = {
651
+ SUCCESS: 0,
652
+ GENERAL_ERROR: 1,
653
+ INVALID_INPUT: 2,
654
+ NOT_FOUND: 3,
655
+ UNAUTHORIZED: 4,
656
+ NETWORK_ERROR: 5,
657
+ CONFIG_ERROR: 6,
658
+ } as const
659
+
660
+ export type ExitCode = (typeof ExitCodes)[keyof typeof ExitCodes]
661
+
662
+ export class CliError extends Error {
663
+ constructor(
664
+ message: string,
665
+ public readonly code: string,
666
+ public readonly exitCode: ExitCode = ExitCodes.GENERAL_ERROR,
667
+ ) {
668
+ super(message)
669
+ this.name = 'CliError'
670
+ }
671
+
672
+ static invalidInput(message: string): CliError {
673
+ return new CliError(message, 'INVALID_INPUT', ExitCodes.INVALID_INPUT)
674
+ }
675
+
676
+ static notFound(resource: string): CliError {
677
+ return new CliError(`${resource} not found`, 'NOT_FOUND', ExitCodes.NOT_FOUND)
678
+ }
679
+
680
+ static unauthorized(message = 'Not authenticated. Run `{{BINARY_NAME}} config login`.'): CliError {
681
+ return new CliError(message, 'UNAUTHORIZED', ExitCodes.UNAUTHORIZED)
682
+ }
683
+
684
+ static networkError(message: string): CliError {
685
+ return new CliError(message, 'NETWORK_ERROR', ExitCodes.NETWORK_ERROR)
686
+ }
687
+
688
+ static configError(message: string): CliError {
689
+ return new CliError(message, 'CONFIG_ERROR', ExitCodes.CONFIG_ERROR)
690
+ }
691
+ }
692
+
693
+ /**
694
+ * Wrap a command action handler to catch and format all errors.
695
+ * Usage: program.command('deploy').action(handleError(async (opts) => { ... }))
696
+ */
697
+ export function handleError<TArgs extends unknown[]>(
698
+ fn: (...args: TArgs) => Promise<void>,
699
+ ): (...args: TArgs) => Promise<void> {
700
+ return async (...args: TArgs): Promise<void> => {
701
+ try {
702
+ await fn(...args)
703
+ } catch (err: unknown) {
704
+ if (err instanceof CliError) {
705
+ output.failure(err.code, err.message)
706
+ process.exit(err.exitCode)
707
+ }
708
+ const message = err instanceof Error ? err.message : String(err)
709
+ output.failure('UNKNOWN_ERROR', message)
710
+ logger.debug(err instanceof Error ? (err.stack ?? message) : message)
711
+ process.exit(ExitCodes.GENERAL_ERROR)
712
+ }
713
+ }
714
+ }
715
+ ```
716
+
717
+ ---
718
+
719
+ ## Section 9: User Configuration Management *(conditional — include only if User Config = yes)*
720
+
721
+ See `references/config-patterns.md` for the complete implementation. This section must
722
+ include:
723
+
724
+ - `src/config/user.config.ts` — `conf` instance with typed schema (Zod), typed getter/setter
725
+ accessors, and key migration logic
726
+ - `src/commands/config/index.ts` — `config get`, `config set`, `config list`, `config reset`
727
+ sub-commands using `commander`'s `.addCommand()` chain
728
+ - Complete code sample for all four sub-commands
729
+ - All config key names, types, and defaults extracted from MODEL.md
730
+
731
+ ---
732
+
733
+ ## Section 10: Project Configuration Management *(conditional — include only if Project Config = yes)*
734
+
735
+ See `references/config-patterns.md` for the complete implementation. This section must
736
+ include:
737
+
738
+ - `src/config/project.config.ts` — cosmiconfig loader with named search path
739
+ (`{{BINARY_NAME}}rc`, `{{BINARY_NAME}}.config.js`, `package.json#{{BINARY_NAME}}`)
740
+ - Zod schema for project config validation
741
+ - Precedence merge function: CLI flags > env vars > project config > defaults
742
+ - `loadProjectConfig()` exported function used by commands that need project context
743
+
744
+ ---
745
+
746
+ ## Section 11: HTTP Client Setup *(conditional — include only if HTTP Client = yes)*
747
+
748
+ ```typescript
749
+ // src/services/http.client.ts
750
+ import got, { type Got, HTTPError } from 'got'
751
+ import { getUserConfig } from '../config/user.config.js'
752
+ import { CliError } from '../errors.js'
753
+
754
+ export function createHttpClient(baseUrl?: string): Got {
755
+ const config = getUserConfig()
756
+ const token = config.get('apiToken')
757
+
758
+ return got.extend({
759
+ prefixUrl: baseUrl ?? config.get('apiBaseUrl'),
760
+ headers: {
761
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
762
+ 'User-Agent': '{{PACKAGE_NAME}}/{{VERSION}}',
763
+ 'Content-Type': 'application/json',
764
+ Accept: 'application/json',
765
+ },
766
+ retry: {
767
+ limit: 2,
768
+ methods: ['GET'],
769
+ statusCodes: [408, 429, 500, 502, 503, 504],
770
+ },
771
+ timeout: {
772
+ request: 30_000,
773
+ },
774
+ hooks: {
775
+ beforeError: [
776
+ (error) => {
777
+ if (error instanceof HTTPError) {
778
+ const { statusCode, body } = error.response
779
+ const message = typeof body === 'string'
780
+ ? (JSON.parse(body) as { message?: string }).message ?? body
781
+ : 'HTTP request failed'
782
+ if (statusCode === 401) throw CliError.unauthorized()
783
+ if (statusCode === 404) throw CliError.notFound('Resource')
784
+ throw CliError.networkError(`HTTP ${statusCode.toString()}: ${message}`)
785
+ }
786
+ return error
787
+ },
788
+ ],
789
+ },
790
+ })
791
+ }
792
+
793
+ /** Singleton HTTP client — initialized on first call */
794
+ let _client: Got | null = null
795
+
796
+ export function getHttpClient(): Got {
797
+ _client ??= createHttpClient()
798
+ return _client
799
+ }
800
+ ```
801
+
802
+ ---
803
+
804
+ ## Section 12: Shell Execution Utilities *(conditional — include only if Shell Execution = yes)*
805
+
806
+ ```typescript
807
+ // src/utils/shell.ts
808
+ import { execa, type Options as ExecaOptions } from 'execa'
809
+ import { logger } from '../ui/logger.js'
810
+ import { CliError } from '../errors.js'
811
+
812
+ export interface RunOptions extends ExecaOptions {
813
+ /** If true, errors are logged but not thrown */
814
+ ignoreErrors?: boolean
815
+ /** Descriptive label for logging */
816
+ label?: string
817
+ }
818
+
819
+ export async function run(
820
+ command: string,
821
+ args: readonly string[] = [],
822
+ options: RunOptions = {},
823
+ ): Promise<string> {
824
+ const { ignoreErrors = false, label, ...execaOptions } = options
825
+ const displayCmd = [command, ...args].join(' ')
826
+
827
+ logger.debug(`${label ?? 'run'}: ${displayCmd}`)
828
+
829
+ try {
830
+ const result = await execa(command, args, {
831
+ ...execaOptions,
832
+ all: true,
833
+ })
834
+ return result.stdout
835
+ } catch (err: unknown) {
836
+ if (ignoreErrors) {
837
+ logger.debug(`Command failed (ignored): ${displayCmd}`)
838
+ return ''
839
+ }
840
+ const message = err instanceof Error ? err.message : String(err)
841
+ throw new CliError(
842
+ `Command failed: ${displayCmd}\n${message}`,
843
+ 'SHELL_ERROR',
844
+ )
845
+ }
846
+ }
847
+ ```
848
+
849
+ ---
850
+
851
+ ## Section 13: Auto-Update Notifier *(conditional — include only if Auto-update = yes)*
852
+
853
+ ```typescript
854
+ // src/utils/updater.ts
855
+ import updateNotifier from 'update-notifier'
856
+
857
+ /**
858
+ * Non-blocking update check. Shows a notification on the NEXT run after
859
+ * an update is detected (update-notifier caches the check result for 24h).
860
+ */
861
+ export function checkForUpdates(packageName: string, currentVersion: string): void {
862
+ // update-notifier is CommonJS — use createRequire for ESM compat
863
+ const notifier = updateNotifier({
864
+ pkg: { name: packageName, version: currentVersion },
865
+ updateCheckInterval: 1000 * 60 * 60 * 24, // 24 hours
866
+ })
867
+ notifier.notify({ isGlobal: true })
868
+ }
869
+ ```
870
+
871
+ ---
872
+
873
+ ## Section 14: Testing Strategy
874
+
875
+ See `references/testing-patterns.md` for the full testing patterns.
876
+
877
+ ### vitest.config.ts
878
+
879
+ ```typescript
880
+ import { defineConfig } from 'vitest/config'
881
+
882
+ export default defineConfig({
883
+ test: {
884
+ globals: true,
885
+ environment: 'node',
886
+ include: ['test/**/*.test.ts'],
887
+ coverage: {
888
+ provider: 'v8',
889
+ include: ['src/**/*.ts'],
890
+ exclude: ['src/cli.ts', 'src/ui/**', 'src/**/*.d.ts'],
891
+ },
892
+ },
893
+ })
894
+ ```
895
+
896
+ ### Testing Approach
897
+
898
+ - **Service tests**: Pure unit tests — no file system, no network, no process.exit
899
+ Inject all dependencies via constructor; mock with `vi.fn()`
900
+ - **Command tests**: Integration-style — invoke Commander's `parseAsync` with
901
+ controlled `argv`, assert on `output.success`/`output.failure` calls
902
+ - **Config tests**: Use `tmp` directories; reset conf store in `beforeEach`
903
+
904
+ ---
905
+
906
+ ## Section 15: Packaging & Distribution *(conditional — include only if Binary Packaging = yes)*
907
+
908
+ See `references/packaging-patterns.md` for the full packaging guide. This section must
909
+ include:
910
+
911
+ - `pkg` field in `package.json` with scripts, assets, and targets
912
+ - Platform-specific npm scripts (`pkg:linux`, `pkg:macos`, `pkg:windows`, `pkg:all`)
913
+ - GitHub Actions workflow for automated release (triggered on `v*` tag)
914
+ - Checksum generation for release assets
915
+ - macOS code signing note (ad-hoc signing with `codesign`)
916
+ - Windows note (Windows Defender SmartScreen bypass for unsigned binaries)
917
+
918
+ For npm-only distribution, include:
919
+ - `"prepublishOnly"` script sequence (typecheck → test → build)
920
+ - `.npmignore` listing excluded files (`src/`, `test/`, config files)
921
+ - `"engines"` field asserting Node.js version requirement
922
+
923
+ ---
924
+
925
+ ## Section 16: Local Database *(conditional — include only if Local Database = yes)*
926
+
927
+ See `references/database-patterns.md` for the complete implementation. This section must
928
+ include:
929
+
930
+ ### 16.1 drizzle.config.ts
931
+
932
+ ```typescript
933
+ import type { Config } from 'drizzle-kit'
934
+
935
+ export default {
936
+ schema: './src/db/schema.ts',
937
+ out: './drizzle',
938
+ dialect: 'sqlite',
939
+ dbCredentials: {
940
+ url: './dev.db', // local dev only — production uses OS data directory
941
+ },
942
+ } satisfies Config
943
+ ```
944
+
945
+ ### 16.2 Database path resolution (`src/db/path.ts`)
946
+
947
+ `env-paths` wrapper that resolves the OS-appropriate data directory and ensures it
948
+ exists before returning the SQLite file path. Must produce:
949
+ - macOS: `~/Library/Application Support/{{BINARY_NAME}}/data.db`
950
+ - Linux: `~/.local/share/{{BINARY_NAME}}/data.db`
951
+ - Windows: `%APPDATA%\{{BINARY_NAME}}\data.db`
952
+
953
+ ### 16.3 Schema (`src/db/schema.ts`)
954
+
955
+ Drizzle table definitions for **every entity type** derived from the model files in
956
+ `context/model/`. Use actual table names and columns from model definitions. Every
957
+ table must include:
958
+ - `id` — `text().primaryKey()` storing a UUIDv4 generated in the repository
959
+ - Timestamp columns as `integer('col', { mode: 'timestamp' })` mapping to `Date`
960
+ - JSON payload columns as `text()` — serialise/deserialise in the repository layer
961
+
962
+ ### 16.4 Database client (`src/db/client.ts`)
963
+
964
+ Singleton `getDb()` function and `createDbClient(dbPath?)` factory:
965
+ - Opens `better-sqlite3` with `journal_mode = WAL`, `foreign_keys = ON`, `synchronous = NORMAL`
966
+ - Runs `migrate(db, { migrationsFolder })` on creation — idempotent, zero-penalty once migrations are current
967
+ - `closeDb()` for graceful shutdown and test cleanup
968
+ - `createDbClient(':memory:')` used in all tests
969
+
970
+ ### 16.5 Repositories (`src/db/repositories/`)
971
+
972
+ One repository class per entity. Each class:
973
+ - Receives `DbClient` via constructor
974
+ - Exposes typed CRUD methods matching the user stories for that entity
975
+ - Never leaks Drizzle internals — public methods return plain typed objects
976
+ - Uses `db.transaction()` for multi-step writes
977
+
978
+ Include the complete repository code for every entity derived from the model files.
979
+
980
+ ### 16.6 Migration files
981
+
982
+ List the initial migration SQL file (`drizzle/0000_initial_schema.sql`) that creates
983
+ all tables. The SQL must match the Drizzle schema exactly:
984
+ - Integer primary keys or `CHAR(36)` UUIDs
985
+ - `FOREIGN KEY` constraints matching the schema relationships
986
+ - Indexes matching the query patterns in the repositories
987
+
988
+ Remind the developer: run `npm run db:generate` after any schema change, commit the
989
+ generated file, and the CLI will apply it automatically on next run.
990
+
991
+ ---
992
+
993
+ ## Section 17: Async Patterns *(conditional — include only if any async pattern detected)*
994
+
995
+ See `references/async-patterns.md` for complete implementation. Include only the
996
+ sub-sections that apply. Multiple sub-sections may coexist in the same application.
997
+
998
+ ### 17a. Shared Signal Handler *(include whenever any async pattern is yes)*
999
+
1000
+ ```typescript
1001
+ // src/utils/signal.ts — generated whenever any async pattern is selected
1002
+ export type CleanupFn = () => void | Promise<void>
1003
+
1004
+ /**
1005
+ * Register SIGINT (Ctrl+C) and SIGTERM handlers. Returns a dispose function
1006
+ * that removes the listeners when the loop exits normally.
1007
+ * Exit codes: 130 for SIGINT (POSIX convention), 0 for SIGTERM (clean shutdown).
1008
+ */
1009
+ export function onSignal(cleanup: CleanupFn): () => void {
1010
+ let handled = false
1011
+ const handler = async (signal: NodeJS.Signals): Promise<void> => {
1012
+ if (handled) return
1013
+ handled = true
1014
+ process.stderr.write('\n')
1015
+ try { await cleanup() } finally {
1016
+ process.exit(signal === 'SIGTERM' ? 0 : 130)
1017
+ }
1018
+ }
1019
+ process.once('SIGINT', () => { void handler('SIGINT') })
1020
+ process.once('SIGTERM', () => { void handler('SIGTERM') })
1021
+ return () => {
1022
+ process.off('SIGINT', handler as never)
1023
+ process.off('SIGTERM', handler as never)
1024
+ }
1025
+ }
1026
+ ```
1027
+
1028
+ ### 17b. Polling *(conditional — include only if Polling = yes)*
1029
+
1030
+ The spec must include:
1031
+
1032
+ - **`src/utils/poll.ts`** — Complete `poll<T>(options)` with `check`, `intervalMs`,
1033
+ `timeoutMs`, `label`, and `onTick`. Spinner managed internally. `onSignal` registered
1034
+ and disposed within the function. Returns `{ value: T, elapsedMs: number }`.
1035
+ - **Command integration** — For every user story with a `--watch`/`--follow`/`--wait`
1036
+ flag, show both code paths in the action handler:
1037
+ - Without flag → trigger, print result ID, `output.success()`, return
1038
+ - With flag → trigger → `poll()` → interpret terminal state → `output.success()` or exit 1
1039
+ - **Output contracts** — human and `--json` form for both paths (immediate and polled)
1040
+ - **Timeout** (exit 1, message "Polling timed out after Xs") and **Ctrl+C** (exit 130,
1041
+ message "Polling stopped — operation continues in the background")
1042
+
1043
+ ### 17c. Inline Batch Processing *(conditional — include only if Inline Batch = yes)*
1044
+
1045
+ The spec must include:
1046
+
1047
+ - **`src/utils/batch.ts`** — Complete `runBatch<TInput, TOutput>(options)` with `total`,
1048
+ `items`, `process`, `concurrency`, `bail`, `onProgress`. Returns `BatchSummary`.
1049
+ - **`src/ui/progress.ts`** — `ProgressBar` wrapping ora: `update(BatchProgress)` shows
1050
+ `[pct%] n/total — ETA Xs`, plus `succeed()` / `fail()` / `stop()` methods.
1051
+ - **Service resume pattern** — when `Local Database = yes`, the service filters out
1052
+ already-processed IDs before returning the item list (checkpoint / resume support).
1053
+ - **Command options** — `--concurrency <n>`, `--bail`, `--dry-run` on every command
1054
+ using `runBatch`, mapped to `BatchOptions` fields.
1055
+ - **Exit code table**: all succeeded → 0; any failed → 1; Ctrl+C → 130.
1056
+
1057
+ ### 17d. Background Daemon *(conditional — include only if Background Daemon = yes)*
1058
+
1059
+ The spec must include:
1060
+
1061
+ - **`src/cli.ts` modification** — daemon mode detection block checking
1062
+ `MY_TOOL_DAEMON_MODE === '1'` placed BEFORE `program.name(...)`. Show exact code.
1063
+ - **`src/daemon/runner.ts`** — `runDaemon()` with event loop, `onSignal`, `tick()` stub
1064
+ annotated with which NFRs define the actual work, and `TICK_INTERVAL_MS` constant
1065
+ derived from the NFRs (e.g. "checks every 30 seconds" → `30_000`).
1066
+ - **`src/services/daemon.service.ts`** — Complete `DaemonManager`:
1067
+ `start()`, `stop(timeoutMs)`, `isRunning()` (signal-0 check), `getStatus()`,
1068
+ `getRecentLogs(n)`. PID and start time stored in `conf`.
1069
+ - **`src/commands/daemon/index.ts`** — `start`, `stop`, `status`, `logs` sub-commands.
1070
+ - **`conf` schema additions** — `daemonPid: z.number().int().positive().optional()` and
1071
+ `daemonStartedAt: z.string().optional()` added to `UserConfigSchema`.
1072
+
1073
+
1074
+ Each command from PRD.md gets its own `<command-name>/SPEC.md`.
1075
+
1076
+ ---
1077
+
1078
+ ## Command SPEC.md Structure
1079
+
1080
+ Each command SPEC.md follows the template below. See `references/command-patterns.md`
1081
+ for the full Commander.js patterns — the generated spec must draw from those patterns
1082
+ and substitute actual command names, flags, aliases, and option defaults from the
1083
+ `model/<command>/model.md` context file. Generic placeholders (e.g. `<n>`, `<template>`)
1084
+ must be replaced with the real argument names from the model.
1085
+
1086
+ ```markdown
1087
+ # `{{command}}` Command — Specification
1088
+
1089
+ > Part of [{{APPLICATION_NAME}} Technical Specification](../SPECIFICATION.md)
1090
+
1091
+ ## Overview
1092
+
1093
+ **Command**: `{{BINARY_NAME}} {{command}}`
1094
+ **Aliases**: `{{aliases}}`
1095
+ **Description**: {{description}}
1096
+ **Version**: {{version}}
1097
+
1098
+ ## Traceability
1099
+
1100
+ ### User Stories
1101
+ | ID | Version | Description |
1102
+ |-------------|---------|-------------------------------------|
1103
+ | USCLI00101 | v0.1.0 | As a user, I want to... |
1104
+
1105
+ ### Non-Functional Requirements
1106
+ | ID | Version | Description |
1107
+ |-------------|---------|-------------------------------------|
1108
+ | NFRCLI0001 | v0.1.0 | Output must be filterable by --json |
1109
+
1110
+ ### Constraints
1111
+ | ID | Version | Description |
1112
+ |-------------|---------|-------------------------------------|
1113
+ | CONSCLI001 | v0.1.0 | Must support --dry-run |
1114
+
1115
+ ### Removed / Replaced
1116
+ | ID | Type | Removed In | Replaced By | Reason |
1117
+ |-------------|-------------|------------|-------------|--------|
1118
+ | _None._ | | | | |
1119
+ ```
1120
+
1121
+ ## 2. Usage
1122
+
1123
+ ```
1124
+ {{BINARY_NAME}} {{command}} [arguments] [options]
1125
+
1126
+ Arguments:
1127
+ <name> Project name (required)
1128
+
1129
+ Options:
1130
+ -t, --template Template to use (default: "default")
1131
+ -y, --yes Skip prompts and use defaults
1132
+ --dry-run Print what would happen without executing
1133
+ -h, --help Display help for command
1134
+ ```
1135
+
1136
+ ## 3. Command Registration
1137
+
1138
+ ```typescript
1139
+ // src/commands/{{command}}/index.ts
1140
+ import type { Command } from 'commander'
1141
+ import { handleError } from '../../errors.js'
1142
+ import { logger } from '../../ui/logger.js'
1143
+ import { output } from '../../ui/output.js'
1144
+ // [If Prompts = yes]:
1145
+ import { prompt{{Command}}Options } from './prompts.js'
1146
+
1147
+ export function register{{Command}}Command(program: Command): void {
1148
+ program
1149
+ .command('{{command}}')
1150
+ .alias('{{alias}}')
1151
+ .description('{{description}}')
1152
+ .argument('<name>', 'project name')
1153
+ .option('-t, --template <template>', 'template to use', 'default')
1154
+ .option('-y, --yes', 'skip prompts and use defaults', false)
1155
+ .option('--dry-run', 'preview without executing', false)
1156
+ .action(handleError(async (name: string, opts: {{Command}}Options) => {
1157
+ // Setup
1158
+ if (opts.verbose) logger.enableVerbose()
1159
+ if (opts.json) output.enableJson()
1160
+
1161
+ // [If Prompts = yes] Collect missing options interactively
1162
+ const resolvedOpts = opts.yes ? opts : await prompt{{Command}}Options(name, opts)
1163
+
1164
+ // Delegate to service
1165
+ const service = new {{Command}}Service()
1166
+ const result = await service.execute(name, resolvedOpts)
1167
+
1168
+ // Output
1169
+ output.success(result, `{{Command}} complete: ${name}`)
1170
+ }))
1171
+ }
1172
+ ```
1173
+
1174
+ ## 4. Argument & Option Contracts
1175
+
1176
+ | Flag | Short | Type | Default | Required | Description |
1177
+ |--------------------|-------|-----------|-------------|----------|--------------------------|
1178
+ | `<name>` | — | `string` | — | Yes | Project name |
1179
+ | `--template` | `-t` | `string` | `"default"` | No | Template identifier |
1180
+ | `--yes` | `-y` | `boolean` | `false` | No | Skip all prompts |
1181
+ | `--dry-run` | — | `boolean` | `false` | No | Preview without changes |
1182
+
1183
+ ### TypeScript Interface
1184
+
1185
+ ```typescript
1186
+ export interface {{Command}}Options {
1187
+ template: string
1188
+ yes: boolean
1189
+ dryRun: boolean
1190
+ verbose: boolean
1191
+ json: boolean
1192
+ }
1193
+ ```
1194
+
1195
+ ### Zod Validation Schema
1196
+
1197
+ ```typescript
1198
+ import { z } from 'zod'
1199
+
1200
+ export const {{command}}OptionsSchema = z.object({
1201
+ template: z.string().min(1).default('default'),
1202
+ yes: z.boolean().default(false),
1203
+ dryRun: z.boolean().default(false),
1204
+ verbose: z.boolean().default(false),
1205
+ json: z.boolean().default(false),
1206
+ })
1207
+ ```
1208
+
1209
+ ## 5. Prompt Flow *(conditional — include only if Prompts = yes for this command)*
1210
+
1211
+ ```typescript
1212
+ // src/commands/{{command}}/prompts.ts
1213
+ import { input, select, confirm } from '@inquirer/prompts'
1214
+ import type { {{Command}}Options } from './index.js'
1215
+
1216
+ export async function prompt{{Command}}Options(
1217
+ name: string,
1218
+ partial: Partial<{{Command}}Options>,
1219
+ ): Promise<{{Command}}Options> {
1220
+ const template = partial.template ?? await select({
1221
+ message: 'Select a template:',
1222
+ choices: [
1223
+ { name: 'Default', value: 'default', description: 'Minimal starter' },
1224
+ { name: 'Full', value: 'full', description: 'All features enabled' },
1225
+ ],
1226
+ })
1227
+
1228
+ const confirm_ = await confirm({
1229
+ message: `Create project "${name}" with template "${template}"?`,
1230
+ default: true,
1231
+ })
1232
+
1233
+ if (!confirm_) {
1234
+ process.stdout.write('Aborted.\n')
1235
+ process.exit(0)
1236
+ }
1237
+
1238
+ return {
1239
+ template,
1240
+ yes: false,
1241
+ dryRun: partial.dryRun ?? false,
1242
+ verbose: partial.verbose ?? false,
1243
+ json: partial.json ?? false,
1244
+ }
1245
+ }
1246
+ ```
1247
+
1248
+ ## 6. Service Interface
1249
+
1250
+ ```typescript
1251
+ // src/services/{{command}}.service.ts (public interface)
1252
+ export interface I{{Command}}Service {
1253
+ execute(name: string, options: {{Command}}Options): Promise<{{Command}}Result>
1254
+ }
1255
+
1256
+ export interface {{Command}}Result {
1257
+ name: string
1258
+ template: string
1259
+ path: string
1260
+ files: string[]
1261
+ dryRun: boolean
1262
+ }
1263
+ ```
1264
+
1265
+ ## 7. Service Implementation
1266
+
1267
+ ```typescript
1268
+ // src/services/{{command}}.service.ts
1269
+ import { join } from 'node:path'
1270
+ import { mkdir, writeFile } from 'node:fs/promises'
1271
+ import { CliError } from '../errors.js'
1272
+ import { logger } from '../ui/logger.js'
1273
+ import type { {{Command}}Options, {{Command}}Result, I{{Command}}Service } from './{{command}}.service.js'
1274
+
1275
+ export class {{Command}}Service implements I{{Command}}Service {
1276
+ async execute(name: string, options: {{Command}}Options): Promise<{{Command}}Result> {
1277
+ logger.debug(`Executing {{command}} for "${name}" with template "${options.template}"`)
1278
+
1279
+ const targetPath = join(process.cwd(), name)
1280
+
1281
+ // Service has NO terminal output — returns data only
1282
+ if (options.dryRun) {
1283
+ return { name, template: options.template, path: targetPath, files: [], dryRun: true }
1284
+ }
1285
+
1286
+ // Real implementation (derive from user stories)
1287
+ // ...
1288
+
1289
+ return {
1290
+ name,
1291
+ template: options.template,
1292
+ path: targetPath,
1293
+ files: ['package.json', 'tsconfig.json', 'README.md'],
1294
+ dryRun: false,
1295
+ }
1296
+ }
1297
+ }
1298
+ ```
1299
+
1300
+ ## 8. Output Contract
1301
+
1302
+ ### Human-readable output
1303
+
1304
+ ```
1305
+ ✔ {{Command}} complete: my-project
1306
+
1307
+ Created 3 files in /Users/you/my-project:
1308
+ • package.json
1309
+ • tsconfig.json
1310
+ • README.md
1311
+
1312
+ Run `cd my-project && npm install` to get started.
1313
+ ```
1314
+
1315
+ ### JSON output (`--json`)
1316
+
1317
+ ```json
1318
+ {
1319
+ "success": true,
1320
+ "data": {
1321
+ "name": "my-project",
1322
+ "template": "default",
1323
+ "path": "/Users/you/my-project",
1324
+ "files": ["package.json", "tsconfig.json", "README.md"],
1325
+ "dryRun": false
1326
+ }
1327
+ }
1328
+ ```
1329
+
1330
+ ### JSON error output
1331
+
1332
+ ```json
1333
+ {
1334
+ "success": false,
1335
+ "error": {
1336
+ "code": "INVALID_INPUT",
1337
+ "message": "Project name cannot contain spaces"
1338
+ }
1339
+ }
1340
+ ```
1341
+
1342
+ ### Async output contracts *(include only for commands with async patterns)*
1343
+
1344
+ **If this command uses `--watch` / `--follow` (Polling):**
1345
+
1346
+ | Scenario | Exit Code | Human output | JSON `success` |
1347
+ |----------|-----------|--------------|----------------|
1348
+ | Triggered, no --watch | 0 | `ℹ Job started: job-123` | `true` |
1349
+ | --watch, succeeded | 0 | `✔ Done in 14.2s` | `true` |
1350
+ | --watch, failed | 1 | `✖ Job failed: <reason>` | `false` |
1351
+ | --watch, timed out | 1 | `✖ Timed out after 300s` | `false` |
1352
+ | Ctrl+C during poll | 130 | `⚠ Polling stopped — continues in background` | N/A |
1353
+
1354
+ **If this command uses `runBatch` (Inline Batch):**
1355
+
1356
+ | Scenario | Exit Code | Human output |
1357
+ |----------|-----------|--------------|
1358
+ | All succeeded | 0 | `✔ Synced 432 resources in 24.1s` |
1359
+ | Partial failure | 1 | `✖ Completed with 3 error(s)` |
1360
+ | --bail triggered | 1 | `✖ Stopped after first error` |
1361
+ | Ctrl+C | 130 | `⚠ Batch interrupted — stopping after current chunk` |
1362
+
1363
+ ## 9. Error Cases
1364
+
1365
+ | Scenario | Error Code | Exit Code | Message shown to user |
1366
+ |---------------------------------|-----------------|-----------|-----------------------------------------------|
1367
+ | Name contains invalid chars | `INVALID_INPUT` | 2 | "Project name may only contain..." |
1368
+ | Target directory already exists | `CONFLICT` | 1 | "Directory already exists: /path/to/dir" |
1369
+ | Template not found | `NOT_FOUND` | 3 | "Template 'xyz' not found" |
1370
+ | File system write failure | `IO_ERROR` | 1 | "Failed to create project files: <reason>" |
1371
+
1372
+ ## 10. Tests
1373
+
1374
+ (See `references/testing-patterns.md` for full test patterns)
1375
+
1376
+ ```typescript
1377
+ // test/commands/{{command}}.test.ts
1378
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
1379
+ import { {{Command}}Service } from '../../src/services/{{command}}.service.js'
1380
+
1381
+ describe('{{Command}}Service', () => {
1382
+ it('returns dryRun result without creating files', async () => {
1383
+ const service = new {{Command}}Service()
1384
+ const result = await service.execute('my-project', {
1385
+ template: 'default', yes: true, dryRun: true, verbose: false, json: false,
1386
+ })
1387
+ expect(result.dryRun).toBe(true)
1388
+ expect(result.files).toHaveLength(0)
1389
+ })
1390
+ })
1391
+ ```