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.
Files changed (75) hide show
  1. package/.claude/commands/workflow/analyze-with-file.md +7 -0
  2. package/.codex/skills/analyze-with-file/SKILL.md +1181 -1182
  3. package/.codex/skills/brainstorm/SKILL.md +723 -725
  4. package/.codex/skills/brainstorm-with-file/SKILL.md +10 -5
  5. package/.codex/skills/clean/SKILL.md +33 -26
  6. package/.codex/skills/collaborative-plan-with-file/SKILL.md +830 -831
  7. package/.codex/skills/csv-wave-pipeline/SKILL.md +906 -906
  8. package/.codex/skills/issue-discover/SKILL.md +57 -50
  9. package/.codex/skills/issue-discover/phases/01-issue-new.md +18 -11
  10. package/.codex/skills/issue-discover/phases/02-discover.md +31 -26
  11. package/.codex/skills/issue-discover/phases/03-discover-by-prompt.md +13 -11
  12. package/.codex/skills/issue-discover/phases/04-quick-execute.md +32 -27
  13. package/.codex/skills/parallel-dev-cycle/SKILL.md +402 -402
  14. package/.codex/skills/project-documentation-workflow/SKILL.md +13 -3
  15. package/.codex/skills/roadmap-with-file/SKILL.md +901 -897
  16. package/.codex/skills/session-sync/SKILL.md +222 -212
  17. package/.codex/skills/spec-add/SKILL.md +620 -613
  18. package/.codex/skills/spec-generator/SKILL.md +2 -2
  19. package/.codex/skills/spec-generator/phases/01-5-requirement-clarification.md +10 -10
  20. package/.codex/skills/spec-generator/phases/01-discovery.md +11 -18
  21. package/.codex/skills/spec-generator/phases/02-product-brief.md +5 -5
  22. package/.codex/skills/spec-generator/phases/03-requirements.md +7 -7
  23. package/.codex/skills/spec-generator/phases/04-architecture.md +4 -4
  24. package/.codex/skills/spec-generator/phases/05-epics-stories.md +5 -6
  25. package/.codex/skills/spec-generator/phases/06-readiness-check.md +10 -17
  26. package/.codex/skills/spec-generator/phases/07-issue-export.md +326 -329
  27. package/.codex/skills/spec-setup/SKILL.md +669 -657
  28. package/.codex/skills/team-arch-opt/SKILL.md +50 -50
  29. package/.codex/skills/team-arch-opt/agents/completion-handler.md +3 -3
  30. package/.codex/skills/team-brainstorm/SKILL.md +724 -725
  31. package/.codex/skills/team-coordinate/SKILL.md +51 -51
  32. package/.codex/skills/team-coordinate/agents/completion-handler.md +3 -3
  33. package/.codex/skills/team-coordinate/agents/plan-reviewer.md +4 -4
  34. package/.codex/skills/team-designer/SKILL.md +691 -691
  35. package/.codex/skills/team-designer/agents/requirement-clarifier.md +11 -12
  36. package/.codex/skills/team-executor/SKILL.md +45 -45
  37. package/.codex/skills/team-frontend/SKILL.md +45 -45
  38. package/.codex/skills/team-frontend/agents/completion-handler.md +3 -3
  39. package/.codex/skills/team-frontend/agents/qa-gate-reviewer.md +4 -4
  40. package/.codex/skills/team-frontend-debug/SKILL.md +50 -50
  41. package/.codex/skills/team-frontend-debug/agents/completion-handler.md +3 -3
  42. package/.codex/skills/team-frontend-debug/agents/conditional-skip-gate.md +4 -4
  43. package/.codex/skills/team-issue/SKILL.md +751 -740
  44. package/.codex/skills/team-iterdev/SKILL.md +825 -826
  45. package/.codex/skills/team-lifecycle-v4/SKILL.md +775 -775
  46. package/.codex/skills/team-lifecycle-v4/agents/quality-gate.md +165 -165
  47. package/.codex/skills/team-lifecycle-v4/agents/requirement-clarifier.md +163 -163
  48. package/.codex/skills/team-perf-opt/SKILL.md +50 -50
  49. package/.codex/skills/team-perf-opt/agents/completion-handler.md +3 -3
  50. package/.codex/skills/team-planex-v2/SKILL.md +652 -637
  51. package/.codex/skills/team-quality-assurance/SKILL.md +51 -52
  52. package/.codex/skills/team-review/SKILL.md +40 -40
  53. package/.codex/skills/team-roadmap-dev/SKILL.md +51 -51
  54. package/.codex/skills/team-roadmap-dev/agents/roadmap-discusser.md +8 -8
  55. package/.codex/skills/team-tech-debt/SKILL.md +50 -50
  56. package/.codex/skills/team-tech-debt/agents/plan-approver.md +5 -5
  57. package/.codex/skills/team-testing/SKILL.md +51 -52
  58. package/.codex/skills/team-uidesign/SKILL.md +40 -40
  59. package/.codex/skills/team-uidesign/agents/completion-handler.md +177 -177
  60. package/.codex/skills/team-ultra-analyze/SKILL.md +786 -787
  61. package/.codex/skills/team-ultra-analyze/agents/discussion-feedback.md +8 -8
  62. package/.codex/skills/team-ux-improve/SKILL.md +51 -52
  63. package/.codex/skills/team-ux-improve/agents/ux-designer.md +2 -2
  64. package/.codex/skills/team-ux-improve/agents/ux-explorer.md +1 -1
  65. package/.codex/skills/unified-execute-with-file/SKILL.md +797 -796
  66. package/.codex/skills/workflow-execute/SKILL.md +1117 -1118
  67. package/.codex/skills/workflow-lite-planex/SKILL.md +1144 -1141
  68. package/.codex/skills/workflow-plan/SKILL.md +631 -636
  69. package/.codex/skills/workflow-tdd-plan/SKILL.md +753 -759
  70. package/.codex/skills/workflow-test-fix-cycle/SKILL.md +402 -392
  71. package/README.md +25 -0
  72. package/ccw/dist/commands/install.d.ts.map +1 -1
  73. package/ccw/dist/commands/install.js +12 -0
  74. package/ccw/dist/commands/install.js.map +1 -1
  75. 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: AskUserQuestion, 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 = ASK_USER([
200
- {
201
- id: "dimension", type: "select",
202
- prompt: "What type of spec do you want to create?",
203
- options: [
204
- { label: "Project Spec", description: "Coding conventions, constraints, quality rules for this project (stored in .ccw/specs/)" },
205
- { label: "Personal Spec", description: "Personal preferences and constraints that follow you across projects (stored in ~/.ccw/specs/personal/ or .ccw/specs/personal/)" }
206
- ]
207
- }
208
- ]) // BLOCKS (wait for user response)
209
- dimension = dimensionAnswer.dimension === "Project Spec" ? "specs" : "personal"
210
- }
211
- }
212
-
213
- // --- Step A2: Ask scope (if personal + scope not specified) ---
214
- if (dimension === 'personal' && !scope) {
215
- if (AUTO_YES) {
216
- scope = 'project' // Default to project scope in auto mode
217
- } else {
218
- const scopeAnswer = ASK_USER([
219
- {
220
- id: "scope", type: "select",
221
- prompt: "Where should this personal spec be stored?",
222
- options: [
223
- { label: "Global (Recommended)", description: "Apply to ALL projects (~/.ccw/specs/personal/)" },
224
- { label: "Project-only", description: "Apply only to this project (.ccw/specs/personal/)" }
225
- ]
226
- }
227
- ]) // BLOCKS (wait for user response)
228
- scope = scopeAnswer.scope.includes("Global") ? "global" : "project"
229
- }
230
- }
231
-
232
- // --- Step A3: Ask category (if not specified) ---
233
- if (!category) {
234
- if (AUTO_YES) {
235
- category = 'general' // Default to general in auto mode
236
- } else {
237
- const categoryAnswer = ASK_USER([
238
- {
239
- id: "category", type: "select",
240
- prompt: "Which workflow stage does this spec apply to?",
241
- options: [
242
- { label: "General (Recommended)", description: "Applies to all stages (default)" },
243
- { label: "Exploration", description: "Code exploration, analysis, debugging" },
244
- { label: "Planning", description: "Task planning, requirements gathering" },
245
- { label: "Execution", description: "Implementation, testing, deployment" }
246
- ]
247
- }
248
- ]) // BLOCKS (wait for user response)
249
- const categoryLabel = categoryAnswer.category
250
- category = categoryLabel.includes("General") ? "general"
251
- : categoryLabel.includes("Exploration") ? "exploration"
252
- : categoryLabel.includes("Planning") ? "planning"
253
- : "execution"
254
- }
255
- }
256
-
257
- // --- Step A4: Ask type (if not specified) ---
258
- if (!type) {
259
- if (AUTO_YES) {
260
- type = 'convention' // Default to convention in auto mode
261
- } else {
262
- const typeAnswer = ASK_USER([
263
- {
264
- id: "type", type: "select",
265
- prompt: "What type of rule is this?",
266
- options: [
267
- { label: "Convention", description: "Coding style preference (e.g., use functional components)" },
268
- { label: "Constraint", description: "Hard rule that must not be violated (e.g., no direct DB access)" },
269
- { label: "Learning", description: "Insight or lesson learned (e.g., cache invalidation needs events)" }
270
- ]
271
- }
272
- ]) // BLOCKS (wait for user response)
273
- const typeLabel = typeAnswer.type
274
- type = typeLabel.includes("Convention") ? "convention"
275
- : typeLabel.includes("Constraint") ? "constraint"
276
- : "learning"
277
- }
278
- }
279
-
280
- // --- Step A5: Ask content (rule text) ---
281
- if (!ruleText) {
282
- if (AUTO_YES) {
283
- console.log("Error: Rule text is required in auto mode. Provide rule text as argument.")
284
- return
285
- }
286
- const contentAnswer = ASK_USER([
287
- {
288
- id: "content", type: "text",
289
- prompt: "Enter the rule or guideline text:"
290
- }
291
- ]) // BLOCKS (wait for user response)
292
- ruleText = contentAnswer.content
293
- }
294
-
295
- }
296
- ```
297
-
298
- ### Path B: Direct Mode
299
-
300
- **Auto-detect type if not specified**:
301
-
302
- ```javascript
303
- function detectType(ruleText) {
304
- const text = ruleText.toLowerCase();
305
-
306
- // Constraint indicators
307
- if (/\b(no|never|must not|forbidden|prohibited|always must)\b/.test(text)) {
308
- return 'constraint';
309
- }
310
-
311
- // Learning indicators
312
- if (/\b(learned|discovered|realized|found that|turns out)\b/.test(text)) {
313
- return 'learning';
314
- }
315
-
316
- // Default to convention
317
- return 'convention';
318
- }
319
-
320
- function detectCategory(ruleText, type) {
321
- const text = ruleText.toLowerCase();
322
-
323
- if (type === 'constraint' || type === 'learning') {
324
- if (/\b(architecture|layer|module|dependency|circular)\b/.test(text)) return 'architecture';
325
- if (/\b(security|auth|permission|sanitize|xss|sql)\b/.test(text)) return 'security';
326
- if (/\b(performance|cache|lazy|async|sync|slow)\b/.test(text)) return 'performance';
327
- if (/\b(test|coverage|mock|stub)\b/.test(text)) return 'testing';
328
- }
329
-
330
- if (type === 'convention') {
331
- if (/\b(name|naming|prefix|suffix|camel|pascal)\b/.test(text)) return 'naming_patterns';
332
- if (/\b(file|folder|directory|structure|organize)\b/.test(text)) return 'file_structure';
333
- if (/\b(doc|comment|jsdoc|readme)\b/.test(text)) return 'documentation';
334
- return 'coding_style';
335
- }
336
-
337
- return type === 'constraint' ? 'tech_stack' : 'other';
338
- }
339
-
340
- if (!useInteractiveWizard) {
341
- if (!type) {
342
- type = detectType(ruleText)
343
- }
344
- if (!category) {
345
- category = detectCategory(ruleText, type)
346
- }
347
- if (!dimension) {
348
- dimension = 'specs' // Default to project specs in direct mode
349
- }
350
- }
351
- ```
352
-
353
- ### Step 3: Ensure Guidelines File Exists
354
-
355
- **Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)**
356
-
357
- ```bash
358
- bash(test -f .ccw/specs/coding-conventions.md && echo "EXISTS" || echo "NOT_FOUND")
359
- ```
360
-
361
- **If NOT_FOUND**, initialize spec system:
362
-
363
- ```bash
364
- Bash('ccw spec init')
365
- Bash('ccw spec rebuild')
366
- ```
367
-
368
- ### Step 4: Determine Target File
369
-
370
- ```javascript
371
- const path = require('path')
372
- const os = require('os')
373
-
374
- const isConvention = type === 'convention'
375
- const isConstraint = type === 'constraint'
376
- const isLearning = type === 'learning'
377
-
378
- let targetFile
379
- let targetDir
380
-
381
- if (dimension === 'specs') {
382
- // Project specs - use .ccw/specs/ (same as frontend/backend spec-index-builder)
383
- targetDir = '.ccw/specs'
384
- if (isConstraint) {
385
- targetFile = path.join(targetDir, 'architecture-constraints.md')
386
- } else {
387
- targetFile = path.join(targetDir, 'coding-conventions.md')
388
- }
389
- } else {
390
- // Personal specs - use .ccw/personal/ (same as backend spec-index-builder)
391
- if (scope === 'global') {
392
- targetDir = path.join(os.homedir(), '.ccw', 'personal')
393
- } else {
394
- targetDir = path.join('.ccw', 'personal')
395
- }
396
-
397
- // Create type-based filename
398
- const typePrefix = isConstraint ? 'constraints' : isLearning ? 'learnings' : 'conventions'
399
- targetFile = path.join(targetDir, `${typePrefix}.md`)
400
- }
401
- ```
402
-
403
- ### Step 5: Build Entry
404
-
405
- ```javascript
406
- function buildEntry(rule, type, category, sessionId) {
407
- if (type === 'learning') {
408
- return {
409
- date: new Date().toISOString().split('T')[0],
410
- session_id: sessionId || null,
411
- insight: rule,
412
- category: category,
413
- context: null
414
- };
415
- }
416
-
417
- // For conventions and constraints, just return the rule string
418
- return rule;
419
- }
420
- ```
421
-
422
- ### Step 6: Write Spec
423
-
424
- ```javascript
425
- const fs = require('fs')
426
-
427
- // Ensure directory exists
428
- if (!fs.existsSync(targetDir)) {
429
- fs.mkdirSync(targetDir, { recursive: true })
430
- }
431
-
432
- // Check if file exists
433
- const fileExists = fs.existsSync(targetFile)
434
-
435
- if (!fileExists) {
436
- // Create new file with frontmatter
437
- const frontmatter = `---
438
- title: ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
439
- readMode: optional
440
- priority: medium
441
- category: ${category}
442
- scope: ${dimension === 'personal' ? scope : 'project'}
443
- dimension: ${dimension}
444
- keywords: [${category}, ${isConstraint ? 'constraint' : isLearning ? 'learning' : 'convention'}]
445
- ---
446
-
447
- # ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
448
-
449
- `
450
- fs.writeFileSync(targetFile, frontmatter, 'utf8')
451
- }
452
-
453
- // Read existing content
454
- let content = fs.readFileSync(targetFile, 'utf8')
455
-
456
- // Deduplicate: skip if rule text already exists in the file
457
- if (content.includes(ruleText)) {
458
- console.log(`
459
- Rule already exists in ${targetFile}
460
- Text: "${ruleText}"
461
- `)
462
- return
463
- }
464
-
465
- // Format the new rule based on type
466
- let newRule
467
- if (isLearning) {
468
- const entry = buildEntry(ruleText, type, category)
469
- newRule = `- [learning/${category}] ${entry.insight} (${entry.date})`
470
- } else {
471
- newRule = `- [${category}] ${ruleText}`
472
- }
473
-
474
- // Append the rule
475
- content = content.trimEnd() + '\n' + newRule + '\n'
476
- fs.writeFileSync(targetFile, content, 'utf8')
477
-
478
- // Rebuild spec index
479
- Bash('ccw spec rebuild')
480
- ```
481
-
482
- ### Step 7: Display Confirmation
483
-
484
- **If `-y`/`--yes` (auto mode)**:
485
- ```
486
- Spec added: [${type}/${category}] "${ruleText}" -> ${targetFile}
487
- ```
488
-
489
- **Otherwise (full confirmation)**:
490
- ```
491
- Spec created successfully
492
-
493
- Dimension: ${dimension}
494
- Scope: ${dimension === 'personal' ? scope : 'project'}
495
- Category: ${category}
496
- Type: ${type}
497
- Rule: "${ruleText}"
498
-
499
- Location: ${targetFile}
500
-
501
- Use 'ccw spec list' to view all specs
502
- Use 'ccw spec load --category ${category}' to load specs by category
503
- ```
504
-
505
- ## Target File Resolution
506
-
507
- ### Project Specs (dimension: specs)
508
- ```
509
- .ccw/specs/
510
- |- coding-conventions.md <- conventions, learnings
511
- |- architecture-constraints.md <- constraints
512
- +- quality-rules.md <- quality rules
513
- ```
514
-
515
- ### Personal Specs (dimension: personal)
516
- ```
517
- # Global (~/.ccw/personal/)
518
- ~/.ccw/personal/
519
- |- conventions.md <- personal conventions (all projects)
520
- |- constraints.md <- personal constraints (all projects)
521
- +- learnings.md <- personal learnings (all projects)
522
-
523
- # Project-local (.ccw/personal/)
524
- .ccw/personal/
525
- |- conventions.md <- personal conventions (this project only)
526
- |- constraints.md <- personal constraints (this project only)
527
- +- learnings.md <- personal learnings (this project only)
528
- ```
529
-
530
- ## Examples
531
-
532
- ### Interactive Wizard
533
- ```bash
534
- $spec-add --interactive
535
- # Prompts for: dimension -> scope (if personal) -> category -> type -> content
536
- ```
537
-
538
- ### Add a Convention (Direct)
539
- ```bash
540
- $spec-add "Use async/await instead of callbacks" --type convention --category coding_style
541
- ```
542
-
543
- Result in `.ccw/specs/coding-conventions.md`:
544
- ```markdown
545
- - [coding_style] Use async/await instead of callbacks
546
- ```
547
-
548
- ### Add an Architectural Constraint (Direct)
549
- ```bash
550
- $spec-add "No direct DB access from controllers" --type constraint --category architecture
551
- ```
552
-
553
- Result in `.ccw/specs/architecture-constraints.md`:
554
- ```markdown
555
- - [architecture] No direct DB access from controllers
556
- ```
557
-
558
- ### Capture a Learning (Direct, Auto-detect)
559
- ```bash
560
- $spec-add "Cache invalidation requires event sourcing for consistency" --type learning
561
- ```
562
-
563
- Result in `.ccw/specs/coding-conventions.md`:
564
- ```markdown
565
- - [learning/architecture] Cache invalidation requires event sourcing for consistency (2026-03-06)
566
- ```
567
-
568
- ### Auto-confirm Mode
569
- ```bash
570
- $spec-add -y "No direct DB access from controllers" --type constraint
571
- # Auto-detects category as 'architecture', writes without confirmation prompt
572
- ```
573
-
574
- ### Personal Spec (Global)
575
- ```bash
576
- $spec-add --scope global --dimension personal --type convention "Prefer descriptive variable names"
577
- ```
578
-
579
- Result in `~/.ccw/personal/conventions.md`:
580
- ```markdown
581
- - [general] Prefer descriptive variable names
582
- ```
583
-
584
- ### Personal Spec (Project)
585
- ```bash
586
- $spec-add --scope project --dimension personal --type constraint "No ORM in this project"
587
- ```
588
-
589
- Result in `.ccw/personal/constraints.md`:
590
- ```markdown
591
- - [general] No ORM in this project
592
- ```
593
-
594
- ## Error Handling
595
-
596
- - **Duplicate Rule**: Warn and skip if exact rule text already exists in target file
597
- - **Invalid Category**: Suggest valid categories for the type
598
- - **Invalid Scope**: Exit with error - must be 'global' or 'project'
599
- - **Invalid Dimension**: Exit with error - must be 'specs' or 'personal'
600
- - **Invalid Type**: Exit with error - must be 'convention', 'constraint', or 'learning'
601
- - **File not writable**: Check permissions, suggest manual creation
602
- - **Invalid path**: Exit with error message
603
- - **File Corruption**: Backup existing file before modification
604
-
605
- ## Related Commands
606
-
607
- - `$spec-setup` - Initialize project with specs scaffold
608
- - `$session-sync` - Quick-sync session work to specs and project-tech
609
- - `$workflow-session-start` - Start a session
610
- - `$workflow-session-complete` - Complete session (prompts for learnings)
611
- - `ccw spec list` - View all specs
612
- - `ccw spec load --category <cat>` - Load filtered specs
613
- - `ccw spec rebuild` - Rebuild spec index
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