| name | spec-add |
| description | Add specs, conventions, constraints, or learnings to project guidelines interactively or automatically |
| argument-hint | [-y|--yes] [--type <convention|constraint|learning>] [--category <category>] [--dimension <specs|personal>] [--scope <global|project>] [--interactive] "rule text" |
| allowed-tools | request_user_input, Read, Write, Edit, Bash, Glob, Grep |
Spec Add Command
Overview
Unified command for adding specs one at a time. Supports both interactive wizard mode and direct CLI mode.
Key Features:
- Supports both project specs and personal specs
- Scope selection (global vs project) for personal specs
- Category-based organization for workflow stages
- Interactive wizard mode with smart defaults
- Direct CLI mode with auto-detection of type and category
- Auto-confirm mode (
-y/--yes) for scripted usage
Use Cases
- During Session: Capture important decisions as they're made
- After Session: Reflect on lessons learned before archiving
- Proactive: Add team conventions or architectural rules
- Interactive: Guided wizard for adding rules with full control over dimension, scope, and category
Usage
$spec-add
$spec-add --interactive
$spec-add "Use async/await instead of callbacks"
$spec-add -y "No direct DB access" --type constraint
$spec-add --scope global --dimension personal
$spec-add --dimension specs --category exploration
$spec-add "Cache invalidation requires event sourcing" --type learning --category architecture
Parameters
| Parameter | Type | Required | Default | Description |
|---|
rule | string | Yes (unless --interactive) | - | The rule, convention, or insight to add |
--type | enum | No | auto-detect | Type: convention, constraint, learning |
--category | string | No | auto-detect / general | Category for organization (see categories below) |
--dimension | enum | No | Interactive | specs (project) or personal |
--scope | enum | No | project | global or project (only for personal dimension) |
--interactive | flag | No | - | Launch full guided wizard for adding rules |
-y / --yes | flag | No | - | Auto-categorize and add without confirmation |
Type Categories
convention - Coding style preferences (goes to conventions section)
- Subcategories:
coding_style, naming_patterns, file_structure, documentation
constraint - Hard rules that must not be violated (goes to constraints section)
- Subcategories:
architecture, tech_stack, performance, security
learning - Session-specific insights (goes to learnings array)
- Subcategories:
architecture, performance, security, testing, process, other
Workflow Stage Categories (for --category)
| Category | Use Case | Example Rules |
|---|
general | Applies to all stages | "Use TypeScript strict mode" |
exploration | Code exploration, debugging | "Always trace the call stack before modifying" |
planning | Task planning, requirements | "Break down tasks into 2-hour chunks" |
execution | Implementation, testing | "Run tests after each file modification" |
Execution Process
Input Parsing:
|- Parse: rule text (positional argument, optional if --interactive)
|- Parse: --type (convention|constraint|learning)
|- Parse: --category (subcategory)
|- Parse: --dimension (specs|personal)
|- Parse: --scope (global|project)
|- Parse: --interactive (flag)
+- Parse: -y / --yes (flag)
Step 1: Parse Input
Step 2: Determine Mode
|- If --interactive OR no rule text -> Full Interactive Wizard (Path A)
+- If rule text provided -> Direct Mode (Path B)
Path A: Interactive Wizard
|- Step A1: Ask dimension (if not specified)
|- Step A2: Ask scope (if personal + scope not specified)
|- Step A3: Ask category (if not specified)
|- Step A4: Ask type (convention|constraint|learning)
|- Step A5: Ask content (rule text)
+- Continue to Step 3
Path B: Direct Mode
|- Step B1: Auto-detect type (if not specified) using detectType()
|- Step B2: Auto-detect category (if not specified) using detectCategory()
|- Step B3: Default dimension to 'specs' if not specified
+- Continue to Step 3
Step 3: Determine Target File
|- specs dimension -> .ccw/specs/coding-conventions.md or architecture-constraints.md
+- personal dimension -> ~/.ccw/personal/ or .ccw/personal/
Step 4: Validate and Write Spec
|- Ensure target directory and file exist
|- Check for duplicates
|- Append rule to appropriate section
+- Run ccw spec rebuild
Step 5: Display Confirmation
+- If -y/--yes: Minimal output
+- Otherwise: Full confirmation with location details
Implementation
Step 1: Parse Input
const args = "$ARGUMENTS"
const argsLower = args.toLowerCase()
const AUTO_YES = argsLower.includes('--yes') || argsLower.includes('-y')
const isInteractive = argsLower.includes('--interactive')
const hasType = argsLower.includes('--type')
const hasCategory = argsLower.includes('--category')
const hasDimension = argsLower.includes('--dimension')
const hasScope = argsLower.includes('--scope')
let type = hasType ? args.match(/--type\s+(\w+)/i)?.[1]?.toLowerCase() : null
let category = hasCategory ? args.match(/--category\s+(\w+)/i)?.[1]?.toLowerCase() : null
let dimension = hasDimension ? args.match(/--dimension\s+(\w+)/i)?.[1]?.toLowerCase() : null
let scope = hasScope ? args.match()?.[]?.() :
ruleText = args
.(, )
.(, )
.(, )
.(, )
.(, )
.(, )
.(, )
.(, )
.()
(scope && ![, ].(scope)) {
.()
}
(dimension && ![, ].(dimension)) {
.()
}
(type && ![, , ].(type)) {
.()
}
(category) {
validCategories = [
, , , ,
, , , ,
, , , ,
, ,
]
(!validCategories.(category)) {
.()
}
}
Step 2: Determine Mode
const useInteractiveWizard = isInteractive || !ruleText
Path A: Interactive Wizard
if (useInteractiveWizard) {
if (!dimension) {
if (AUTO_YES) {
dimension = 'specs'
} else {
const dimensionAnswer = functions.request_user_input({
questions: [{
header: "Dimension",
id: "dimension",
question: "What type of spec do you want to create?",
options: [
{ label: "Project Spec(Recommended)", description: "Coding conventions, constraints, quality rules for this project (stored in .ccw/specs/)" },
{ label: "Personal Spec", description: "Personal preferences and constraints that follow you across projects (stored in ~/.ccw/specs/personal/ or .ccw/specs/personal/)" }
]
}]
})
dimension = dimensionAnswer.answers.dimension.answers[0] === "Project Spec(Recommended)" ? "specs" : "personal"
}
}
if (dimension === 'personal' && !scope) {
if (AUTO_YES) {
scope = 'project'
} else {
scopeAnswer = functions.({
: [{
: ,
: ,
: ,
: [
{ : , : },
{ : , : }
]
}]
})
scope = scopeAnswer...[] === ? :
}
}
(!category) {
() {
category =
} {
categoryAnswer = functions.({
: [{
: ,
: ,
: ,
: [
{ : , : },
{ : , : },
{ : , : }
]
}]
})
categoryLabel = categoryAnswer...[]
category = categoryLabel.() ?
: categoryLabel.() ?
: categoryLabel.() ?
:
}
}
(!type) {
() {
type =
} {
typeAnswer = functions.({
: [{
: ,
: ,
: ,
: [
{ : , : },
{ : , : },
{ : , : }
]
}]
})
typeLabel = typeAnswer...[]
type = typeLabel.() ?
: typeLabel.() ?
:
}
}
(!ruleText) {
() {
.()
}
contentAnswer = functions.({
: [{
: ,
: ,
: ,
: [
{ : , : }
]
}]
})
ruleText = contentAnswer...[]
}
}
Path B: Direct Mode
Auto-detect type if not specified:
function detectType(ruleText) {
const text = ruleText.toLowerCase();
if (/\b(no|never|must not|forbidden|prohibited|always must)\b/.test(text)) {
return 'constraint';
}
if (/\b(learned|discovered|realized|found that|turns out)\b/.test(text)) {
return 'learning';
}
return 'convention';
}
function detectCategory(ruleText, type) {
const text = ruleText.toLowerCase();
if (type === 'constraint' || type === 'learning') {
if (/\b(architecture|layer|module|dependency|circular)\b/.test(text)) return 'architecture';
if (/\b(security|auth|permission|sanitize|xss|sql)\b/.test(text)) return 'security';
if (/\b(performance|cache|lazy|async|sync|slow)\b/.test(text)) return 'performance';
if (/\b(test|coverage|mock|stub)\b/.test(text)) return ;
}
(type === ) {
(.(text)) ;
(.(text)) ;
(.(text)) ;
;
}
type === ? : ;
}
(!useInteractiveWizard) {
(!type) {
type = (ruleText)
}
(!category) {
category = (ruleText, type)
}
(!dimension) {
dimension =
}
}
Step 3: Ensure Guidelines File Exists
Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)
bash(test -f .ccw/specs/coding-conventions.md && echo "EXISTS" || echo "NOT_FOUND")
If NOT_FOUND, initialize spec system:
Bash('ccw spec init')
Bash('ccw spec rebuild')
Step 4: Determine Target File
const path = require('path')
const os = require('os')
const isConvention = type === 'convention'
const isConstraint = type === 'constraint'
const isLearning = type === 'learning'
let targetFile
let targetDir
if (dimension === 'specs') {
targetDir = '.ccw/specs'
if (isConstraint) {
targetFile = path.join(targetDir, 'architecture-constraints.md')
} else {
targetFile = path.join(targetDir, 'coding-conventions.md')
}
} else {
if (scope === 'global') {
targetDir = path.join(os.homedir(), '.ccw', 'personal')
} else {
targetDir = path.join('.ccw', 'personal')
}
const typePrefix = isConstraint ? 'constraints' : isLearning ? 'learnings' : 'conventions'
targetFile = path.join(targetDir, `${typePrefix}.md`)
}
Step 5: Build Entry
function buildEntry(rule, type, category, sessionId) {
if (type === 'learning') {
return {
date: new Date().toISOString().split('T')[0],
session_id: sessionId || null,
insight: rule,
category: category,
context: null
};
}
return rule;
}
Step 6: Write Spec
const fs = require('fs')
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true })
}
const fileExists = fs.existsSync(targetFile)
if (!fileExists) {
const frontmatter = `---
title: ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
readMode: optional
priority: medium
category: ${category}
scope: ${dimension === 'personal' ? scope : 'project'}
dimension: ${dimension}
keywords: [${category}, ${isConstraint ? 'constraint' : isLearning ? 'learning' : 'convention'}]
---
# ${dimension === 'specs' ? 'Project' : 'Personal'} ${isConstraint ? 'Constraints' : isLearning ? 'Learnings' : 'Conventions'}
`
fs.writeFileSync(targetFile, frontmatter, 'utf8')
}
let content = fs.readFileSync(targetFile, 'utf8')
(content.(ruleText)) {
.()
}
newRule
(isLearning) {
entry = (ruleText, type, category)
newRule =
} {
newRule =
}
content = content.() + + newRule +
fs.(targetFile, content, )
()
Step 7: Display Confirmation
If -y/--yes (auto mode):
Spec added: [${type}/${category}] "${ruleText}" -> ${targetFile}
Otherwise (full confirmation):
Spec created successfully
Dimension: ${dimension}
Scope: ${dimension === 'personal' ? scope : 'project'}
Category: ${category}
Type: ${type}
Rule: "${ruleText}"
Location: ${targetFile}
Use 'ccw spec list' to view all specs
Use 'ccw spec load --category ${category}' to load specs by category
Target File Resolution
Project Specs (dimension: specs)
.ccw/specs/
|- coding-conventions.md <- conventions, learnings
|- architecture-constraints.md <- constraints
+- quality-rules.md <- quality rules
Personal Specs (dimension: personal)
# Global (~/.ccw/personal/)
~/.ccw/personal/
|- conventions.md <- personal conventions (all projects)
|- constraints.md <- personal constraints (all projects)
+- learnings.md <- personal learnings (all projects)
# Project-local (.ccw/personal/)
.ccw/personal/
|- conventions.md <- personal conventions (this project only)
|- constraints.md <- personal constraints (this project only)
+- learnings.md <- personal learnings (this project only)
Examples
Interactive Wizard
$spec-add --interactive
Add a Convention (Direct)
$spec-add "Use async/await instead of callbacks" --type convention --category coding_style
Result in .ccw/specs/coding-conventions.md:
- [coding_style] Use async/await instead of callbacks
Add an Architectural Constraint (Direct)
$spec-add "No direct DB access from controllers" --type constraint --category architecture
Result in .ccw/specs/architecture-constraints.md:
- [architecture] No direct DB access from controllers
Capture a Learning (Direct, Auto-detect)
$spec-add "Cache invalidation requires event sourcing for consistency" --type learning
Result in .ccw/specs/coding-conventions.md:
- [learning/architecture] Cache invalidation requires event sourcing for consistency (2026-03-06)
Auto-confirm Mode
$spec-add -y "No direct DB access from controllers" --type constraint
Personal Spec (Global)
$spec-add --scope global --dimension personal --type convention "Prefer descriptive variable names"
Result in ~/.ccw/personal/conventions.md:
- [general] Prefer descriptive variable names
Personal Spec (Project)
$spec-add --scope project --dimension personal --type constraint "No ORM in this project"
Result in .ccw/personal/constraints.md:
- [general] No ORM in this project
Error Handling
- Duplicate Rule: Warn and skip if exact rule text already exists in target file
- Invalid Category: Suggest valid categories for the type
- Invalid Scope: Exit with error - must be 'global' or 'project'
- Invalid Dimension: Exit with error - must be 'specs' or 'personal'
- Invalid Type: Exit with error - must be 'convention', 'constraint', or 'learning'
- File not writable: Check permissions, suggest manual creation
- Invalid path: Exit with error message
- File Corruption: Backup existing file before modification
Related Commands
$spec-setup - Initialize project with specs scaffold
$session-sync - Quick-sync session work to specs and project-tech
$workflow-session-start - Start a session
$workflow-session-complete - Complete session (prompts for learnings)
ccw spec list - View all specs
ccw spec load --category <cat> - Load filtered specs
ccw spec rebuild - Rebuild spec index