claude-code-workflow 7.2.14 → 7.2.15
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/.claude/commands/workflow/analyze-with-file.md +7 -0
- package/.codex/skills/analyze-with-file/SKILL.md +1181 -1182
- package/.codex/skills/brainstorm/SKILL.md +723 -725
- package/.codex/skills/brainstorm-with-file/SKILL.md +10 -5
- package/.codex/skills/clean/SKILL.md +33 -26
- package/.codex/skills/collaborative-plan-with-file/SKILL.md +830 -831
- package/.codex/skills/csv-wave-pipeline/SKILL.md +906 -906
- package/.codex/skills/issue-discover/SKILL.md +57 -50
- package/.codex/skills/issue-discover/phases/01-issue-new.md +18 -11
- package/.codex/skills/issue-discover/phases/02-discover.md +31 -26
- package/.codex/skills/issue-discover/phases/03-discover-by-prompt.md +13 -11
- package/.codex/skills/issue-discover/phases/04-quick-execute.md +32 -27
- package/.codex/skills/parallel-dev-cycle/SKILL.md +402 -402
- package/.codex/skills/project-documentation-workflow/SKILL.md +13 -3
- package/.codex/skills/roadmap-with-file/SKILL.md +901 -897
- package/.codex/skills/session-sync/SKILL.md +222 -212
- package/.codex/skills/spec-add/SKILL.md +620 -613
- package/.codex/skills/spec-generator/SKILL.md +2 -2
- package/.codex/skills/spec-generator/phases/01-5-requirement-clarification.md +10 -10
- package/.codex/skills/spec-generator/phases/01-discovery.md +11 -18
- package/.codex/skills/spec-generator/phases/02-product-brief.md +5 -5
- package/.codex/skills/spec-generator/phases/03-requirements.md +7 -7
- package/.codex/skills/spec-generator/phases/04-architecture.md +4 -4
- package/.codex/skills/spec-generator/phases/05-epics-stories.md +5 -6
- package/.codex/skills/spec-generator/phases/06-readiness-check.md +10 -17
- package/.codex/skills/spec-generator/phases/07-issue-export.md +326 -329
- package/.codex/skills/spec-setup/SKILL.md +669 -657
- package/.codex/skills/team-arch-opt/SKILL.md +50 -50
- package/.codex/skills/team-arch-opt/agents/completion-handler.md +3 -3
- package/.codex/skills/team-brainstorm/SKILL.md +724 -725
- package/.codex/skills/team-coordinate/SKILL.md +51 -51
- package/.codex/skills/team-coordinate/agents/completion-handler.md +3 -3
- package/.codex/skills/team-coordinate/agents/plan-reviewer.md +4 -4
- package/.codex/skills/team-designer/SKILL.md +691 -691
- package/.codex/skills/team-designer/agents/requirement-clarifier.md +11 -12
- package/.codex/skills/team-executor/SKILL.md +45 -45
- package/.codex/skills/team-frontend/SKILL.md +45 -45
- package/.codex/skills/team-frontend/agents/completion-handler.md +3 -3
- package/.codex/skills/team-frontend/agents/qa-gate-reviewer.md +4 -4
- package/.codex/skills/team-frontend-debug/SKILL.md +50 -50
- package/.codex/skills/team-frontend-debug/agents/completion-handler.md +3 -3
- package/.codex/skills/team-frontend-debug/agents/conditional-skip-gate.md +4 -4
- package/.codex/skills/team-issue/SKILL.md +751 -740
- package/.codex/skills/team-iterdev/SKILL.md +825 -826
- package/.codex/skills/team-lifecycle-v4/SKILL.md +775 -775
- package/.codex/skills/team-lifecycle-v4/agents/quality-gate.md +165 -165
- package/.codex/skills/team-lifecycle-v4/agents/requirement-clarifier.md +163 -163
- package/.codex/skills/team-perf-opt/SKILL.md +50 -50
- package/.codex/skills/team-perf-opt/agents/completion-handler.md +3 -3
- package/.codex/skills/team-planex-v2/SKILL.md +652 -637
- package/.codex/skills/team-quality-assurance/SKILL.md +51 -52
- package/.codex/skills/team-review/SKILL.md +40 -40
- package/.codex/skills/team-roadmap-dev/SKILL.md +51 -51
- package/.codex/skills/team-roadmap-dev/agents/roadmap-discusser.md +8 -8
- package/.codex/skills/team-tech-debt/SKILL.md +50 -50
- package/.codex/skills/team-tech-debt/agents/plan-approver.md +5 -5
- package/.codex/skills/team-testing/SKILL.md +51 -52
- package/.codex/skills/team-uidesign/SKILL.md +40 -40
- package/.codex/skills/team-uidesign/agents/completion-handler.md +177 -177
- package/.codex/skills/team-ultra-analyze/SKILL.md +786 -787
- package/.codex/skills/team-ultra-analyze/agents/discussion-feedback.md +8 -8
- package/.codex/skills/team-ux-improve/SKILL.md +51 -52
- package/.codex/skills/team-ux-improve/agents/ux-designer.md +2 -2
- package/.codex/skills/team-ux-improve/agents/ux-explorer.md +1 -1
- package/.codex/skills/unified-execute-with-file/SKILL.md +797 -796
- package/.codex/skills/workflow-execute/SKILL.md +1117 -1118
- package/.codex/skills/workflow-lite-planex/SKILL.md +1144 -1141
- package/.codex/skills/workflow-plan/SKILL.md +631 -636
- package/.codex/skills/workflow-tdd-plan/SKILL.md +753 -759
- package/.codex/skills/workflow-test-fix-cycle/SKILL.md +402 -392
- package/README.md +25 -0
- package/ccw/dist/commands/install.d.ts.map +1 -1
- package/ccw/dist/commands/install.js +12 -0
- package/ccw/dist/commands/install.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,613 +1,620 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: spec-add
|
|
3
|
-
description: Add specs, conventions, constraints, or learnings to project guidelines interactively or automatically
|
|
4
|
-
argument-hint: "[-y|--yes] [--type <convention|constraint|learning>] [--category <category>] [--dimension <specs|personal>] [--scope <global|project>] [--interactive] \"rule text\""
|
|
5
|
-
allowed-tools:
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Spec Add Command
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Unified command for adding specs one at a time. Supports both interactive wizard mode and direct CLI mode.
|
|
13
|
-
|
|
14
|
-
**Key Features**:
|
|
15
|
-
- Supports both project specs and personal specs
|
|
16
|
-
- Scope selection (global vs project) for personal specs
|
|
17
|
-
- Category-based organization for workflow stages
|
|
18
|
-
- Interactive wizard mode with smart defaults
|
|
19
|
-
- Direct CLI mode with auto-detection of type and category
|
|
20
|
-
- Auto-confirm mode (`-y`/`--yes`) for scripted usage
|
|
21
|
-
|
|
22
|
-
## Use Cases
|
|
23
|
-
|
|
24
|
-
1. **During Session**: Capture important decisions as they're made
|
|
25
|
-
2. **After Session**: Reflect on lessons learned before archiving
|
|
26
|
-
3. **Proactive**: Add team conventions or architectural rules
|
|
27
|
-
4. **Interactive**: Guided wizard for adding rules with full control over dimension, scope, and category
|
|
28
|
-
|
|
29
|
-
## Usage
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
$spec-add # Interactive wizard (all prompts)
|
|
33
|
-
$spec-add --interactive # Explicit interactive wizard
|
|
34
|
-
$spec-add "Use async/await instead of callbacks" # Direct mode (auto-detect type)
|
|
35
|
-
$spec-add -y "No direct DB access" --type constraint # Auto-confirm, skip confirmation
|
|
36
|
-
$spec-add --scope global --dimension personal # Create global personal spec (interactive)
|
|
37
|
-
$spec-add --dimension specs --category exploration # Project spec in exploration category (interactive)
|
|
38
|
-
$spec-add "Cache invalidation requires event sourcing" --type learning --category architecture
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Parameters
|
|
42
|
-
|
|
43
|
-
| Parameter | Type | Required | Default | Description |
|
|
44
|
-
|-----------|------|----------|---------|-------------|
|
|
45
|
-
| `rule` | string | Yes (unless `--interactive`) | - | The rule, convention, or insight to add |
|
|
46
|
-
| `--type` | enum | No | auto-detect | Type: `convention`, `constraint`, `learning` |
|
|
47
|
-
| `--category` | string | No | auto-detect / `general` | Category for organization (see categories below) |
|
|
48
|
-
| `--dimension` | enum | No | Interactive | `specs` (project) or `personal` |
|
|
49
|
-
| `--scope` | enum | No | `project` | `global` or `project` (only for personal dimension) |
|
|
50
|
-
| `--interactive` | flag | No | - | Launch full guided wizard for adding rules |
|
|
51
|
-
| `-y` / `--yes` | flag | No | - | Auto-categorize and add without confirmation |
|
|
52
|
-
|
|
53
|
-
### Type Categories
|
|
54
|
-
|
|
55
|
-
**convention** - Coding style preferences (goes to `conventions` section)
|
|
56
|
-
- Subcategories: `coding_style`, `naming_patterns`, `file_structure`, `documentation`
|
|
57
|
-
|
|
58
|
-
**constraint** - Hard rules that must not be violated (goes to `constraints` section)
|
|
59
|
-
- Subcategories: `architecture`, `tech_stack`, `performance`, `security`
|
|
60
|
-
|
|
61
|
-
**learning** - Session-specific insights (goes to `learnings` array)
|
|
62
|
-
- Subcategories: `architecture`, `performance`, `security`, `testing`, `process`, `other`
|
|
63
|
-
|
|
64
|
-
### Workflow Stage Categories (for `--category`)
|
|
65
|
-
|
|
66
|
-
| Category | Use Case | Example Rules |
|
|
67
|
-
|----------|----------|---------------|
|
|
68
|
-
| `general` | Applies to all stages | "Use TypeScript strict mode" |
|
|
69
|
-
| `exploration` | Code exploration, debugging | "Always trace the call stack before modifying" |
|
|
70
|
-
| `planning` | Task planning, requirements | "Break down tasks into 2-hour chunks" |
|
|
71
|
-
| `execution` | Implementation, testing | "Run tests after each file modification" |
|
|
72
|
-
|
|
73
|
-
## Execution Process
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
Input Parsing:
|
|
77
|
-
|- Parse: rule text (positional argument, optional if --interactive)
|
|
78
|
-
|- Parse: --type (convention|constraint|learning)
|
|
79
|
-
|- Parse: --category (subcategory)
|
|
80
|
-
|- Parse: --dimension (specs|personal)
|
|
81
|
-
|- Parse: --scope (global|project)
|
|
82
|
-
|- Parse: --interactive (flag)
|
|
83
|
-
+- Parse: -y / --yes (flag)
|
|
84
|
-
|
|
85
|
-
Step 1: Parse Input
|
|
86
|
-
|
|
87
|
-
Step 2: Determine Mode
|
|
88
|
-
|- If --interactive OR no rule text -> Full Interactive Wizard (Path A)
|
|
89
|
-
+- If rule text provided -> Direct Mode (Path B)
|
|
90
|
-
|
|
91
|
-
Path A: Interactive Wizard
|
|
92
|
-
|- Step A1: Ask dimension (if not specified)
|
|
93
|
-
|- Step A2: Ask scope (if personal + scope not specified)
|
|
94
|
-
|- Step A3: Ask category (if not specified)
|
|
95
|
-
|- Step A4: Ask type (convention|constraint|learning)
|
|
96
|
-
|- Step A5: Ask content (rule text)
|
|
97
|
-
+- Continue to Step 3
|
|
98
|
-
|
|
99
|
-
Path B: Direct Mode
|
|
100
|
-
|- Step B1: Auto-detect type (if not specified) using detectType()
|
|
101
|
-
|- Step B2: Auto-detect category (if not specified) using detectCategory()
|
|
102
|
-
|- Step B3: Default dimension to 'specs' if not specified
|
|
103
|
-
+- Continue to Step 3
|
|
104
|
-
|
|
105
|
-
Step 3: Determine Target File
|
|
106
|
-
|- specs dimension -> .ccw/specs/coding-conventions.md or architecture-constraints.md
|
|
107
|
-
+- personal dimension -> ~/.ccw/personal/ or .ccw/personal/
|
|
108
|
-
|
|
109
|
-
Step 4: Validate and Write Spec
|
|
110
|
-
|- Ensure target directory and file exist
|
|
111
|
-
|- Check for duplicates
|
|
112
|
-
|- Append rule to appropriate section
|
|
113
|
-
+- Run ccw spec rebuild
|
|
114
|
-
|
|
115
|
-
Step 5: Display Confirmation
|
|
116
|
-
+- If -y/--yes: Minimal output
|
|
117
|
-
+- Otherwise: Full confirmation with location details
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Implementation
|
|
121
|
-
|
|
122
|
-
### Step 1: Parse Input
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
// Parse arguments
|
|
126
|
-
const args = "$ARGUMENTS"
|
|
127
|
-
const argsLower = args.toLowerCase()
|
|
128
|
-
|
|
129
|
-
// Extract flags
|
|
130
|
-
const AUTO_YES = argsLower.includes('--yes') || argsLower.includes('-y')
|
|
131
|
-
const isInteractive = argsLower.includes('--interactive')
|
|
132
|
-
|
|
133
|
-
// Extract named parameters
|
|
134
|
-
const hasType = argsLower.includes('--type')
|
|
135
|
-
const hasCategory = argsLower.includes('--category')
|
|
136
|
-
const hasDimension = argsLower.includes('--dimension')
|
|
137
|
-
const hasScope = argsLower.includes('--scope')
|
|
138
|
-
|
|
139
|
-
let type = hasType ? args.match(/--type\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
140
|
-
let category = hasCategory ? args.match(/--category\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
141
|
-
let dimension = hasDimension ? args.match(/--dimension\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
142
|
-
let scope = hasScope ? args.match(/--scope\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
143
|
-
|
|
144
|
-
// Extract rule text (everything before flags, or quoted string)
|
|
145
|
-
let ruleText = args
|
|
146
|
-
.replace(/--type\s+\w+/gi, '')
|
|
147
|
-
.replace(/--category\s+\w+/gi, '')
|
|
148
|
-
.replace(/--dimension\s+\w+/gi, '')
|
|
149
|
-
.replace(/--scope\s+\w+/gi, '')
|
|
150
|
-
.replace(/--interactive/gi, '')
|
|
151
|
-
.replace(/--yes/gi, '')
|
|
152
|
-
.replace(/-y\b/gi, '')
|
|
153
|
-
.replace(/^["']|["']$/g, '')
|
|
154
|
-
.trim()
|
|
155
|
-
|
|
156
|
-
// Validate values
|
|
157
|
-
if (scope && !['global', 'project'].includes(scope)) {
|
|
158
|
-
console.log("Invalid scope. Use 'global' or 'project'.")
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
if (dimension && !['specs', 'personal'].includes(dimension)) {
|
|
162
|
-
console.log("Invalid dimension. Use 'specs' or 'personal'.")
|
|
163
|
-
return
|
|
164
|
-
}
|
|
165
|
-
if (type && !['convention', 'constraint', 'learning'].includes(type)) {
|
|
166
|
-
console.log("Invalid type. Use 'convention', 'constraint', or 'learning'.")
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
if (category) {
|
|
170
|
-
const validCategories = [
|
|
171
|
-
'general', 'exploration', 'planning', 'execution',
|
|
172
|
-
'coding_style', 'naming_patterns', 'file_structure', 'documentation',
|
|
173
|
-
'architecture', 'tech_stack', 'performance', 'security',
|
|
174
|
-
'testing', 'process', 'other'
|
|
175
|
-
]
|
|
176
|
-
if (!validCategories.includes(category)) {
|
|
177
|
-
console.log(`Invalid category. Valid categories: ${validCategories.join(', ')}`)
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### Step 2: Determine Mode
|
|
184
|
-
|
|
185
|
-
```javascript
|
|
186
|
-
const useInteractiveWizard = isInteractive || !ruleText
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Path A: Interactive Wizard
|
|
190
|
-
|
|
191
|
-
```javascript
|
|
192
|
-
if (useInteractiveWizard) {
|
|
193
|
-
|
|
194
|
-
// --- Step A1: Ask dimension (if not specified) ---
|
|
195
|
-
if (!dimension) {
|
|
196
|
-
if (AUTO_YES) {
|
|
197
|
-
dimension = 'specs' // Default to project specs in auto mode
|
|
198
|
-
} else {
|
|
199
|
-
const dimensionAnswer =
|
|
200
|
-
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
{ label: "
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{ label: "
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
: "
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (type === '
|
|
331
|
-
if (/\b(
|
|
332
|
-
if (/\b(
|
|
333
|
-
if (/\b(
|
|
334
|
-
return '
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
} else {
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (!
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
- **
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
-
|
|
608
|
-
-
|
|
609
|
-
-
|
|
610
|
-
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1
|
+
---
|
|
2
|
+
name: spec-add
|
|
3
|
+
description: Add specs, conventions, constraints, or learnings to project guidelines interactively or automatically
|
|
4
|
+
argument-hint: "[-y|--yes] [--type <convention|constraint|learning>] [--category <category>] [--dimension <specs|personal>] [--scope <global|project>] [--interactive] \"rule text\""
|
|
5
|
+
allowed-tools: request_user_input, Read, Write, Edit, Bash, Glob, Grep
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Spec Add Command
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Unified command for adding specs one at a time. Supports both interactive wizard mode and direct CLI mode.
|
|
13
|
+
|
|
14
|
+
**Key Features**:
|
|
15
|
+
- Supports both project specs and personal specs
|
|
16
|
+
- Scope selection (global vs project) for personal specs
|
|
17
|
+
- Category-based organization for workflow stages
|
|
18
|
+
- Interactive wizard mode with smart defaults
|
|
19
|
+
- Direct CLI mode with auto-detection of type and category
|
|
20
|
+
- Auto-confirm mode (`-y`/`--yes`) for scripted usage
|
|
21
|
+
|
|
22
|
+
## Use Cases
|
|
23
|
+
|
|
24
|
+
1. **During Session**: Capture important decisions as they're made
|
|
25
|
+
2. **After Session**: Reflect on lessons learned before archiving
|
|
26
|
+
3. **Proactive**: Add team conventions or architectural rules
|
|
27
|
+
4. **Interactive**: Guided wizard for adding rules with full control over dimension, scope, and category
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
$spec-add # Interactive wizard (all prompts)
|
|
33
|
+
$spec-add --interactive # Explicit interactive wizard
|
|
34
|
+
$spec-add "Use async/await instead of callbacks" # Direct mode (auto-detect type)
|
|
35
|
+
$spec-add -y "No direct DB access" --type constraint # Auto-confirm, skip confirmation
|
|
36
|
+
$spec-add --scope global --dimension personal # Create global personal spec (interactive)
|
|
37
|
+
$spec-add --dimension specs --category exploration # Project spec in exploration category (interactive)
|
|
38
|
+
$spec-add "Cache invalidation requires event sourcing" --type learning --category architecture
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Parameters
|
|
42
|
+
|
|
43
|
+
| Parameter | Type | Required | Default | Description |
|
|
44
|
+
|-----------|------|----------|---------|-------------|
|
|
45
|
+
| `rule` | string | Yes (unless `--interactive`) | - | The rule, convention, or insight to add |
|
|
46
|
+
| `--type` | enum | No | auto-detect | Type: `convention`, `constraint`, `learning` |
|
|
47
|
+
| `--category` | string | No | auto-detect / `general` | Category for organization (see categories below) |
|
|
48
|
+
| `--dimension` | enum | No | Interactive | `specs` (project) or `personal` |
|
|
49
|
+
| `--scope` | enum | No | `project` | `global` or `project` (only for personal dimension) |
|
|
50
|
+
| `--interactive` | flag | No | - | Launch full guided wizard for adding rules |
|
|
51
|
+
| `-y` / `--yes` | flag | No | - | Auto-categorize and add without confirmation |
|
|
52
|
+
|
|
53
|
+
### Type Categories
|
|
54
|
+
|
|
55
|
+
**convention** - Coding style preferences (goes to `conventions` section)
|
|
56
|
+
- Subcategories: `coding_style`, `naming_patterns`, `file_structure`, `documentation`
|
|
57
|
+
|
|
58
|
+
**constraint** - Hard rules that must not be violated (goes to `constraints` section)
|
|
59
|
+
- Subcategories: `architecture`, `tech_stack`, `performance`, `security`
|
|
60
|
+
|
|
61
|
+
**learning** - Session-specific insights (goes to `learnings` array)
|
|
62
|
+
- Subcategories: `architecture`, `performance`, `security`, `testing`, `process`, `other`
|
|
63
|
+
|
|
64
|
+
### Workflow Stage Categories (for `--category`)
|
|
65
|
+
|
|
66
|
+
| Category | Use Case | Example Rules |
|
|
67
|
+
|----------|----------|---------------|
|
|
68
|
+
| `general` | Applies to all stages | "Use TypeScript strict mode" |
|
|
69
|
+
| `exploration` | Code exploration, debugging | "Always trace the call stack before modifying" |
|
|
70
|
+
| `planning` | Task planning, requirements | "Break down tasks into 2-hour chunks" |
|
|
71
|
+
| `execution` | Implementation, testing | "Run tests after each file modification" |
|
|
72
|
+
|
|
73
|
+
## Execution Process
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
Input Parsing:
|
|
77
|
+
|- Parse: rule text (positional argument, optional if --interactive)
|
|
78
|
+
|- Parse: --type (convention|constraint|learning)
|
|
79
|
+
|- Parse: --category (subcategory)
|
|
80
|
+
|- Parse: --dimension (specs|personal)
|
|
81
|
+
|- Parse: --scope (global|project)
|
|
82
|
+
|- Parse: --interactive (flag)
|
|
83
|
+
+- Parse: -y / --yes (flag)
|
|
84
|
+
|
|
85
|
+
Step 1: Parse Input
|
|
86
|
+
|
|
87
|
+
Step 2: Determine Mode
|
|
88
|
+
|- If --interactive OR no rule text -> Full Interactive Wizard (Path A)
|
|
89
|
+
+- If rule text provided -> Direct Mode (Path B)
|
|
90
|
+
|
|
91
|
+
Path A: Interactive Wizard
|
|
92
|
+
|- Step A1: Ask dimension (if not specified)
|
|
93
|
+
|- Step A2: Ask scope (if personal + scope not specified)
|
|
94
|
+
|- Step A3: Ask category (if not specified)
|
|
95
|
+
|- Step A4: Ask type (convention|constraint|learning)
|
|
96
|
+
|- Step A5: Ask content (rule text)
|
|
97
|
+
+- Continue to Step 3
|
|
98
|
+
|
|
99
|
+
Path B: Direct Mode
|
|
100
|
+
|- Step B1: Auto-detect type (if not specified) using detectType()
|
|
101
|
+
|- Step B2: Auto-detect category (if not specified) using detectCategory()
|
|
102
|
+
|- Step B3: Default dimension to 'specs' if not specified
|
|
103
|
+
+- Continue to Step 3
|
|
104
|
+
|
|
105
|
+
Step 3: Determine Target File
|
|
106
|
+
|- specs dimension -> .ccw/specs/coding-conventions.md or architecture-constraints.md
|
|
107
|
+
+- personal dimension -> ~/.ccw/personal/ or .ccw/personal/
|
|
108
|
+
|
|
109
|
+
Step 4: Validate and Write Spec
|
|
110
|
+
|- Ensure target directory and file exist
|
|
111
|
+
|- Check for duplicates
|
|
112
|
+
|- Append rule to appropriate section
|
|
113
|
+
+- Run ccw spec rebuild
|
|
114
|
+
|
|
115
|
+
Step 5: Display Confirmation
|
|
116
|
+
+- If -y/--yes: Minimal output
|
|
117
|
+
+- Otherwise: Full confirmation with location details
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Implementation
|
|
121
|
+
|
|
122
|
+
### Step 1: Parse Input
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
// Parse arguments
|
|
126
|
+
const args = "$ARGUMENTS"
|
|
127
|
+
const argsLower = args.toLowerCase()
|
|
128
|
+
|
|
129
|
+
// Extract flags
|
|
130
|
+
const AUTO_YES = argsLower.includes('--yes') || argsLower.includes('-y')
|
|
131
|
+
const isInteractive = argsLower.includes('--interactive')
|
|
132
|
+
|
|
133
|
+
// Extract named parameters
|
|
134
|
+
const hasType = argsLower.includes('--type')
|
|
135
|
+
const hasCategory = argsLower.includes('--category')
|
|
136
|
+
const hasDimension = argsLower.includes('--dimension')
|
|
137
|
+
const hasScope = argsLower.includes('--scope')
|
|
138
|
+
|
|
139
|
+
let type = hasType ? args.match(/--type\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
140
|
+
let category = hasCategory ? args.match(/--category\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
141
|
+
let dimension = hasDimension ? args.match(/--dimension\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
142
|
+
let scope = hasScope ? args.match(/--scope\s+(\w+)/i)?.[1]?.toLowerCase() : null
|
|
143
|
+
|
|
144
|
+
// Extract rule text (everything before flags, or quoted string)
|
|
145
|
+
let ruleText = args
|
|
146
|
+
.replace(/--type\s+\w+/gi, '')
|
|
147
|
+
.replace(/--category\s+\w+/gi, '')
|
|
148
|
+
.replace(/--dimension\s+\w+/gi, '')
|
|
149
|
+
.replace(/--scope\s+\w+/gi, '')
|
|
150
|
+
.replace(/--interactive/gi, '')
|
|
151
|
+
.replace(/--yes/gi, '')
|
|
152
|
+
.replace(/-y\b/gi, '')
|
|
153
|
+
.replace(/^["']|["']$/g, '')
|
|
154
|
+
.trim()
|
|
155
|
+
|
|
156
|
+
// Validate values
|
|
157
|
+
if (scope && !['global', 'project'].includes(scope)) {
|
|
158
|
+
console.log("Invalid scope. Use 'global' or 'project'.")
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
if (dimension && !['specs', 'personal'].includes(dimension)) {
|
|
162
|
+
console.log("Invalid dimension. Use 'specs' or 'personal'.")
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
if (type && !['convention', 'constraint', 'learning'].includes(type)) {
|
|
166
|
+
console.log("Invalid type. Use 'convention', 'constraint', or 'learning'.")
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
if (category) {
|
|
170
|
+
const validCategories = [
|
|
171
|
+
'general', 'exploration', 'planning', 'execution',
|
|
172
|
+
'coding_style', 'naming_patterns', 'file_structure', 'documentation',
|
|
173
|
+
'architecture', 'tech_stack', 'performance', 'security',
|
|
174
|
+
'testing', 'process', 'other'
|
|
175
|
+
]
|
|
176
|
+
if (!validCategories.includes(category)) {
|
|
177
|
+
console.log(`Invalid category. Valid categories: ${validCategories.join(', ')}`)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Step 2: Determine Mode
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const useInteractiveWizard = isInteractive || !ruleText
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Path A: Interactive Wizard
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
if (useInteractiveWizard) {
|
|
193
|
+
|
|
194
|
+
// --- Step A1: Ask dimension (if not specified) ---
|
|
195
|
+
if (!dimension) {
|
|
196
|
+
if (AUTO_YES) {
|
|
197
|
+
dimension = 'specs' // Default to project specs in auto mode
|
|
198
|
+
} else {
|
|
199
|
+
const dimensionAnswer = request_user_input({
|
|
200
|
+
questions: [{
|
|
201
|
+
header: "Dimension",
|
|
202
|
+
id: "dimension",
|
|
203
|
+
question: "What type of spec do you want to create?",
|
|
204
|
+
options: [
|
|
205
|
+
{ label: "Project Spec(Recommended)", description: "Coding conventions, constraints, quality rules for this project (stored in .ccw/specs/)" },
|
|
206
|
+
{ label: "Personal Spec", description: "Personal preferences and constraints that follow you across projects (stored in ~/.ccw/specs/personal/ or .ccw/specs/personal/)" }
|
|
207
|
+
]
|
|
208
|
+
}]
|
|
209
|
+
}) // BLOCKS (wait for user response)
|
|
210
|
+
dimension = dimensionAnswer.answers.dimension.answers[0] === "Project Spec(Recommended)" ? "specs" : "personal"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// --- Step A2: Ask scope (if personal + scope not specified) ---
|
|
215
|
+
if (dimension === 'personal' && !scope) {
|
|
216
|
+
if (AUTO_YES) {
|
|
217
|
+
scope = 'project' // Default to project scope in auto mode
|
|
218
|
+
} else {
|
|
219
|
+
const scopeAnswer = request_user_input({
|
|
220
|
+
questions: [{
|
|
221
|
+
header: "Scope",
|
|
222
|
+
id: "scope",
|
|
223
|
+
question: "Where should this personal spec be stored?",
|
|
224
|
+
options: [
|
|
225
|
+
{ label: "Global(Recommended)", description: "Apply to ALL projects (~/.ccw/specs/personal/)" },
|
|
226
|
+
{ label: "Project-only", description: "Apply only to this project (.ccw/specs/personal/)" }
|
|
227
|
+
]
|
|
228
|
+
}]
|
|
229
|
+
}) // BLOCKS (wait for user response)
|
|
230
|
+
scope = scopeAnswer.answers.scope.answers[0] === "Global(Recommended)" ? "global" : "project"
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --- Step A3: Ask category (if not specified) ---
|
|
235
|
+
if (!category) {
|
|
236
|
+
if (AUTO_YES) {
|
|
237
|
+
category = 'general' // Default to general in auto mode
|
|
238
|
+
} else {
|
|
239
|
+
const categoryAnswer = request_user_input({
|
|
240
|
+
questions: [{
|
|
241
|
+
header: "Category",
|
|
242
|
+
id: "category",
|
|
243
|
+
question: "Which workflow stage does this spec apply to?",
|
|
244
|
+
options: [
|
|
245
|
+
{ label: "General(Recommended)", description: "Applies to all stages (default)" },
|
|
246
|
+
{ label: "Exploration", description: "Code exploration, analysis, debugging" },
|
|
247
|
+
{ label: "Planning", description: "Task planning, requirements gathering" }
|
|
248
|
+
]
|
|
249
|
+
}]
|
|
250
|
+
}) // BLOCKS (wait for user response)
|
|
251
|
+
const categoryLabel = categoryAnswer.answers.category.answers[0]
|
|
252
|
+
category = categoryLabel.includes("General") ? "general"
|
|
253
|
+
: categoryLabel.includes("Exploration") ? "exploration"
|
|
254
|
+
: categoryLabel.includes("Planning") ? "planning"
|
|
255
|
+
: "execution"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// --- Step A4: Ask type (if not specified) ---
|
|
260
|
+
if (!type) {
|
|
261
|
+
if (AUTO_YES) {
|
|
262
|
+
type = 'convention' // Default to convention in auto mode
|
|
263
|
+
} else {
|
|
264
|
+
const typeAnswer = request_user_input({
|
|
265
|
+
questions: [{
|
|
266
|
+
header: "Rule Type",
|
|
267
|
+
id: "rule_type",
|
|
268
|
+
question: "What type of rule is this?",
|
|
269
|
+
options: [
|
|
270
|
+
{ label: "Convention", description: "Coding style preference (e.g., use functional components)" },
|
|
271
|
+
{ label: "Constraint", description: "Hard rule that must not be violated (e.g., no direct DB access)" },
|
|
272
|
+
{ label: "Learning", description: "Insight or lesson learned (e.g., cache invalidation needs events)" }
|
|
273
|
+
]
|
|
274
|
+
}]
|
|
275
|
+
}) // BLOCKS (wait for user response)
|
|
276
|
+
const typeLabel = typeAnswer.answers.rule_type.answers[0]
|
|
277
|
+
type = typeLabel.includes("Convention") ? "convention"
|
|
278
|
+
: typeLabel.includes("Constraint") ? "constraint"
|
|
279
|
+
: "learning"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// --- Step A5: Ask content (rule text) ---
|
|
284
|
+
if (!ruleText) {
|
|
285
|
+
if (AUTO_YES) {
|
|
286
|
+
console.log("Error: Rule text is required in auto mode. Provide rule text as argument.")
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
const contentAnswer = request_user_input({
|
|
290
|
+
questions: [{
|
|
291
|
+
header: "Content",
|
|
292
|
+
id: "content",
|
|
293
|
+
question: "Enter the rule or guideline text:",
|
|
294
|
+
options: [
|
|
295
|
+
{ label: "Type in Other", description: "Enter your rule text via the Other input field" }
|
|
296
|
+
]
|
|
297
|
+
}]
|
|
298
|
+
}) // BLOCKS (wait for user response)
|
|
299
|
+
ruleText = contentAnswer.answers.content.answers[0]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Path B: Direct Mode
|
|
306
|
+
|
|
307
|
+
**Auto-detect type if not specified**:
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
function detectType(ruleText) {
|
|
311
|
+
const text = ruleText.toLowerCase();
|
|
312
|
+
|
|
313
|
+
// Constraint indicators
|
|
314
|
+
if (/\b(no|never|must not|forbidden|prohibited|always must)\b/.test(text)) {
|
|
315
|
+
return 'constraint';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Learning indicators
|
|
319
|
+
if (/\b(learned|discovered|realized|found that|turns out)\b/.test(text)) {
|
|
320
|
+
return 'learning';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Default to convention
|
|
324
|
+
return 'convention';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function detectCategory(ruleText, type) {
|
|
328
|
+
const text = ruleText.toLowerCase();
|
|
329
|
+
|
|
330
|
+
if (type === 'constraint' || type === 'learning') {
|
|
331
|
+
if (/\b(architecture|layer|module|dependency|circular)\b/.test(text)) return 'architecture';
|
|
332
|
+
if (/\b(security|auth|permission|sanitize|xss|sql)\b/.test(text)) return 'security';
|
|
333
|
+
if (/\b(performance|cache|lazy|async|sync|slow)\b/.test(text)) return 'performance';
|
|
334
|
+
if (/\b(test|coverage|mock|stub)\b/.test(text)) return 'testing';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (type === 'convention') {
|
|
338
|
+
if (/\b(name|naming|prefix|suffix|camel|pascal)\b/.test(text)) return 'naming_patterns';
|
|
339
|
+
if (/\b(file|folder|directory|structure|organize)\b/.test(text)) return 'file_structure';
|
|
340
|
+
if (/\b(doc|comment|jsdoc|readme)\b/.test(text)) return 'documentation';
|
|
341
|
+
return 'coding_style';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return type === 'constraint' ? 'tech_stack' : 'other';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!useInteractiveWizard) {
|
|
348
|
+
if (!type) {
|
|
349
|
+
type = detectType(ruleText)
|
|
350
|
+
}
|
|
351
|
+
if (!category) {
|
|
352
|
+
category = detectCategory(ruleText, type)
|
|
353
|
+
}
|
|
354
|
+
if (!dimension) {
|
|
355
|
+
dimension = 'specs' // Default to project specs in direct mode
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Step 3: Ensure Guidelines File Exists
|
|
361
|
+
|
|
362
|
+
**Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)**
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
bash(test -f .ccw/specs/coding-conventions.md && echo "EXISTS" || echo "NOT_FOUND")
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**If NOT_FOUND**, initialize spec system:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
Bash('ccw spec init')
|
|
372
|
+
Bash('ccw spec rebuild')
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Step 4: Determine Target File
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
const path = require('path')
|
|
379
|
+
const os = require('os')
|
|
380
|
+
|
|
381
|
+
const isConvention = type === 'convention'
|
|
382
|
+
const isConstraint = type === 'constraint'
|
|
383
|
+
const isLearning = type === 'learning'
|
|
384
|
+
|
|
385
|
+
let targetFile
|
|
386
|
+
let targetDir
|
|
387
|
+
|
|
388
|
+
if (dimension === 'specs') {
|
|
389
|
+
// Project specs - use .ccw/specs/ (same as frontend/backend spec-index-builder)
|
|
390
|
+
targetDir = '.ccw/specs'
|
|
391
|
+
if (isConstraint) {
|
|
392
|
+
targetFile = path.join(targetDir, 'architecture-constraints.md')
|
|
393
|
+
} else {
|
|
394
|
+
targetFile = path.join(targetDir, 'coding-conventions.md')
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
// Personal specs - use .ccw/personal/ (same as backend spec-index-builder)
|
|
398
|
+
if (scope === 'global') {
|
|
399
|
+
targetDir = path.join(os.homedir(), '.ccw', 'personal')
|
|
400
|
+
} else {
|
|
401
|
+
targetDir = path.join('.ccw', 'personal')
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Create type-based filename
|
|
405
|
+
const typePrefix = isConstraint ? 'constraints' : isLearning ? 'learnings' : 'conventions'
|
|
406
|
+
targetFile = path.join(targetDir, `${typePrefix}.md`)
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Step 5: Build Entry
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
function buildEntry(rule, type, category, sessionId) {
|
|
414
|
+
if (type === 'learning') {
|
|
415
|
+
return {
|
|
416
|
+
date: new Date().toISOString().split('T')[0],
|
|
417
|
+
session_id: sessionId || null,
|
|
418
|
+
insight: rule,
|
|
419
|
+
category: category,
|
|
420
|
+
context: null
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// For conventions and constraints, just return the rule string
|
|
425
|
+
return rule;
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Step 6: Write Spec
|
|
430
|
+
|
|
431
|
+
```javascript
|
|
432
|
+
const fs = require('fs')
|
|
433
|
+
|
|
434
|
+
// Ensure directory exists
|
|
435
|
+
if (!fs.existsSync(targetDir)) {
|
|
436
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Check if file exists
|
|
440
|
+
const fileExists = fs.existsSync(targetFile)
|
|
441
|
+
|
|
442
|
+
if (!fileExists) {
|
|
443
|
+
// Create new file with frontmatter
|
|
444
|
+
const frontmatter = `---
|
|
445
|
+
title: ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
|
|
446
|
+
readMode: optional
|
|
447
|
+
priority: medium
|
|
448
|
+
category: ${category}
|
|
449
|
+
scope: ${dimension === 'personal' ? scope : 'project'}
|
|
450
|
+
dimension: ${dimension}
|
|
451
|
+
keywords: [${category}, ${isConstraint ? 'constraint' : isLearning ? 'learning' : 'convention'}]
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
# ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
|
|
455
|
+
|
|
456
|
+
`
|
|
457
|
+
fs.writeFileSync(targetFile, frontmatter, 'utf8')
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Read existing content
|
|
461
|
+
let content = fs.readFileSync(targetFile, 'utf8')
|
|
462
|
+
|
|
463
|
+
// Deduplicate: skip if rule text already exists in the file
|
|
464
|
+
if (content.includes(ruleText)) {
|
|
465
|
+
console.log(`
|
|
466
|
+
Rule already exists in ${targetFile}
|
|
467
|
+
Text: "${ruleText}"
|
|
468
|
+
`)
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Format the new rule based on type
|
|
473
|
+
let newRule
|
|
474
|
+
if (isLearning) {
|
|
475
|
+
const entry = buildEntry(ruleText, type, category)
|
|
476
|
+
newRule = `- [learning/${category}] ${entry.insight} (${entry.date})`
|
|
477
|
+
} else {
|
|
478
|
+
newRule = `- [${category}] ${ruleText}`
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Append the rule
|
|
482
|
+
content = content.trimEnd() + '\n' + newRule + '\n'
|
|
483
|
+
fs.writeFileSync(targetFile, content, 'utf8')
|
|
484
|
+
|
|
485
|
+
// Rebuild spec index
|
|
486
|
+
Bash('ccw spec rebuild')
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Step 7: Display Confirmation
|
|
490
|
+
|
|
491
|
+
**If `-y`/`--yes` (auto mode)**:
|
|
492
|
+
```
|
|
493
|
+
Spec added: [${type}/${category}] "${ruleText}" -> ${targetFile}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Otherwise (full confirmation)**:
|
|
497
|
+
```
|
|
498
|
+
Spec created successfully
|
|
499
|
+
|
|
500
|
+
Dimension: ${dimension}
|
|
501
|
+
Scope: ${dimension === 'personal' ? scope : 'project'}
|
|
502
|
+
Category: ${category}
|
|
503
|
+
Type: ${type}
|
|
504
|
+
Rule: "${ruleText}"
|
|
505
|
+
|
|
506
|
+
Location: ${targetFile}
|
|
507
|
+
|
|
508
|
+
Use 'ccw spec list' to view all specs
|
|
509
|
+
Use 'ccw spec load --category ${category}' to load specs by category
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Target File Resolution
|
|
513
|
+
|
|
514
|
+
### Project Specs (dimension: specs)
|
|
515
|
+
```
|
|
516
|
+
.ccw/specs/
|
|
517
|
+
|- coding-conventions.md <- conventions, learnings
|
|
518
|
+
|- architecture-constraints.md <- constraints
|
|
519
|
+
+- quality-rules.md <- quality rules
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Personal Specs (dimension: personal)
|
|
523
|
+
```
|
|
524
|
+
# Global (~/.ccw/personal/)
|
|
525
|
+
~/.ccw/personal/
|
|
526
|
+
|- conventions.md <- personal conventions (all projects)
|
|
527
|
+
|- constraints.md <- personal constraints (all projects)
|
|
528
|
+
+- learnings.md <- personal learnings (all projects)
|
|
529
|
+
|
|
530
|
+
# Project-local (.ccw/personal/)
|
|
531
|
+
.ccw/personal/
|
|
532
|
+
|- conventions.md <- personal conventions (this project only)
|
|
533
|
+
|- constraints.md <- personal constraints (this project only)
|
|
534
|
+
+- learnings.md <- personal learnings (this project only)
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
## Examples
|
|
538
|
+
|
|
539
|
+
### Interactive Wizard
|
|
540
|
+
```bash
|
|
541
|
+
$spec-add --interactive
|
|
542
|
+
# Prompts for: dimension -> scope (if personal) -> category -> type -> content
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Add a Convention (Direct)
|
|
546
|
+
```bash
|
|
547
|
+
$spec-add "Use async/await instead of callbacks" --type convention --category coding_style
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Result in `.ccw/specs/coding-conventions.md`:
|
|
551
|
+
```markdown
|
|
552
|
+
- [coding_style] Use async/await instead of callbacks
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Add an Architectural Constraint (Direct)
|
|
556
|
+
```bash
|
|
557
|
+
$spec-add "No direct DB access from controllers" --type constraint --category architecture
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Result in `.ccw/specs/architecture-constraints.md`:
|
|
561
|
+
```markdown
|
|
562
|
+
- [architecture] No direct DB access from controllers
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
### Capture a Learning (Direct, Auto-detect)
|
|
566
|
+
```bash
|
|
567
|
+
$spec-add "Cache invalidation requires event sourcing for consistency" --type learning
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Result in `.ccw/specs/coding-conventions.md`:
|
|
571
|
+
```markdown
|
|
572
|
+
- [learning/architecture] Cache invalidation requires event sourcing for consistency (2026-03-06)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Auto-confirm Mode
|
|
576
|
+
```bash
|
|
577
|
+
$spec-add -y "No direct DB access from controllers" --type constraint
|
|
578
|
+
# Auto-detects category as 'architecture', writes without confirmation prompt
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Personal Spec (Global)
|
|
582
|
+
```bash
|
|
583
|
+
$spec-add --scope global --dimension personal --type convention "Prefer descriptive variable names"
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
Result in `~/.ccw/personal/conventions.md`:
|
|
587
|
+
```markdown
|
|
588
|
+
- [general] Prefer descriptive variable names
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Personal Spec (Project)
|
|
592
|
+
```bash
|
|
593
|
+
$spec-add --scope project --dimension personal --type constraint "No ORM in this project"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Result in `.ccw/personal/constraints.md`:
|
|
597
|
+
```markdown
|
|
598
|
+
- [general] No ORM in this project
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
## Error Handling
|
|
602
|
+
|
|
603
|
+
- **Duplicate Rule**: Warn and skip if exact rule text already exists in target file
|
|
604
|
+
- **Invalid Category**: Suggest valid categories for the type
|
|
605
|
+
- **Invalid Scope**: Exit with error - must be 'global' or 'project'
|
|
606
|
+
- **Invalid Dimension**: Exit with error - must be 'specs' or 'personal'
|
|
607
|
+
- **Invalid Type**: Exit with error - must be 'convention', 'constraint', or 'learning'
|
|
608
|
+
- **File not writable**: Check permissions, suggest manual creation
|
|
609
|
+
- **Invalid path**: Exit with error message
|
|
610
|
+
- **File Corruption**: Backup existing file before modification
|
|
611
|
+
|
|
612
|
+
## Related Commands
|
|
613
|
+
|
|
614
|
+
- `$spec-setup` - Initialize project with specs scaffold
|
|
615
|
+
- `$session-sync` - Quick-sync session work to specs and project-tech
|
|
616
|
+
- `$workflow-session-start` - Start a session
|
|
617
|
+
- `$workflow-session-complete` - Complete session (prompts for learnings)
|
|
618
|
+
- `ccw spec list` - View all specs
|
|
619
|
+
- `ccw spec load --category <cat>` - Load filtered specs
|
|
620
|
+
- `ccw spec rebuild` - Rebuild spec index
|