feat: add spec-setup command for project initialization and interactive configuration

- Introduced a new command `spec-setup` to initialize project-level state.
- Generates `.workflow/project-tech.json` and `.ccw/specs/*.md` files.
- Implements a multi-round interactive questionnaire for configuring project guidelines.
- Supports flags for regeneration, skipping specs, and resetting existing content.
- Integrates analysis via `cli-explore-agent` for comprehensive project understanding.
- Provides detailed execution process and error handling for various scenarios.
This commit is contained in:
catlog22
2026-03-06 16:49:35 +08:00
parent f2d4364c69
commit a9469a5e3b
25 changed files with 3472 additions and 1819 deletions

View File

@@ -706,10 +706,19 @@ if (!autoMode) {
| Export | Copy plan.md + plan-note.md to user-specified location |
| Done | Display artifact paths, end workflow |
### Step 4.5: Sync Session State
```bash
$session-sync -y "Plan complete: {domains} domains, {tasks} tasks"
```
Updates specs/*.md with planning insights and project-tech.json with planning session entry.
**Success Criteria**:
- `plan.md` generated with complete summary
- `.task/TASK-*.json` collected at session root (consumable by unified-execute)
- All artifacts present in session directory
- Session state synced via `$session-sync`
- User informed of completion and next steps
---

View File

@@ -119,6 +119,7 @@ Phase 4: Completion & Summary
└─ Ref: phases/04-completion-summary.md
├─ Generate unified summary report
├─ Update final state
├─ Sync session state: $session-sync -y "Dev cycle complete: {iterations} iterations"
├─ Close all agents
└─ Output: final cycle report with continuation instructions
```

View File

@@ -224,6 +224,7 @@ Phase 8: Fix Execution
Phase 9: Fix Completion
└─ Ref: phases/09-fix-completion.md
├─ Aggregate results → fix-summary.md
├─ Sync session state: $session-sync -y "Review cycle complete: {findings} findings, {fixed} fixed"
└─ Optional: complete workflow session if all fixes successful
Complete: Review reports + optional fix results
@@ -473,3 +474,9 @@ review-cycle src/auth/**
# Step 2: Fix (continue or standalone)
review-cycle --fix ${projectRoot}/.workflow/active/WFS-{session-id}/.review/
```
### Session Sync
```bash
# Auto-synced at Phase 9 (fix completion)
$session-sync -y "Review cycle complete: {findings} findings, {fixed} fixed"
```

View File

@@ -0,0 +1,212 @@
---
name: session-sync
description: Quick-sync session work to specs/*.md and project-tech.json
argument-hint: "[-y|--yes] [\"what was done\"]"
allowed-tools: AskUserQuestion, Read, Write, Edit, Bash, Glob, Grep
---
# Session Sync
One-shot update `specs/*.md` + `project-tech.json` from current session context.
**Design**: Scan context -> extract -> write. No interactive wizards.
## Usage
```bash
$session-sync # Sync with preview + confirmation
$session-sync -y # Auto-sync, skip confirmation
$session-sync "Added JWT auth flow" # Sync with explicit summary
$session-sync -y "Fixed N+1 query" # Auto-sync with summary
```
## Process
```
Step 1: Gather Context
|- git diff --stat HEAD~3..HEAD (recent changes)
|- Active session folder (.workflow/.lite-plan/*) if exists
+- User summary ($ARGUMENTS or auto-generate from git log)
Step 2: Extract Updates
|- Guidelines: conventions / constraints / learnings
+- Tech: development_index entry
Step 3: Preview & Confirm (skip if --yes)
Step 4: Write both files
Step 5: One-line confirmation
```
## Implementation
### Step 1: Gather Context
```javascript
const AUTO_YES = "$ARGUMENTS".includes('--yes') || "$ARGUMENTS".includes('-y')
const userSummary = "$ARGUMENTS".replace(/--yes|-y/g, '').trim()
// Recent changes
const gitStat = Bash('git diff --stat HEAD~3..HEAD 2>/dev/null || git diff --stat HEAD 2>/dev/null')
const gitLog = Bash('git log --oneline -5')
// Active session (optional)
const sessionFolders = Glob('.workflow/.lite-plan/*/plan.json')
let sessionContext = null
if (sessionFolders.length > 0) {
const latest = sessionFolders[sessionFolders.length - 1]
sessionContext = JSON.parse(Read(latest))
}
// Build summary
const summary = userSummary
|| sessionContext?.summary
|| gitLog.split('\n')[0].replace(/^[a-f0-9]+ /, '')
```
### Step 2: Extract Updates
Analyze context and produce two update payloads. Use LLM reasoning (current agent) -- no CLI calls.
```javascript
// -- Guidelines extraction --
// Scan git diff + session for:
// - New patterns adopted -> convention
// - Restrictions discovered -> constraint
// - Surprises / gotchas -> learning
//
// Output: array of { type, category, text }
// RULE: Only extract genuinely reusable insights. Skip trivial/obvious items.
// RULE: Deduplicate against existing guidelines before adding.
// Load existing specs via ccw spec load
const existingSpecs = Bash('ccw spec load --dimension specs 2>/dev/null || echo ""')
const guidelineUpdates = [] // populated by agent analysis
// -- Tech extraction --
// Build one development_index entry from session work
function detectCategory(text) {
text = text.toLowerCase()
if (/\b(fix|bug|error|crash)\b/.test(text)) return 'bugfix'
if (/\b(refactor|cleanup|reorganize)\b/.test(text)) return 'refactor'
if (/\b(doc|readme|comment)\b/.test(text)) return 'docs'
if (/\b(add|new|create|implement)\b/.test(text)) return 'feature'
return 'enhancement'
}
function detectSubFeature(gitStat) {
// Most-changed directory from git diff --stat
const dirs = gitStat.match(/\S+\//g) || []
const counts = {}
dirs.forEach(d => {
const seg = d.split('/').filter(Boolean).slice(-2, -1)[0] || 'general'
counts[seg] = (counts[seg] || 0) + 1
})
return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'general'
}
const techEntry = {
title: summary.slice(0, 60),
sub_feature: detectSubFeature(gitStat),
date: new Date().toISOString().split('T')[0],
description: summary.slice(0, 100),
status: 'completed',
session_id: sessionContext ? sessionFolders[sessionFolders.length - 1].match(/lite-plan\/([^/]+)/)?.[1] : null
}
```
### Step 3: Preview & Confirm
```javascript
// Show preview
console.log(`
-- Sync Preview --
Guidelines (${guidelineUpdates.length} items):
${guidelineUpdates.map(g => ` [${g.type}/${g.category}] ${g.text}`).join('\n') || ' (none)'}
Tech [${detectCategory(summary)}]:
${techEntry.title}
Target files:
.ccw/specs/*.md
.workflow/project-tech.json
`)
if (!AUTO_YES) {
const approved = CONFIRM("Apply these updates? (modify/skip items if needed)") // BLOCKS (wait for user response)
if (!approved) {
console.log('Sync cancelled.')
return
}
}
```
### Step 4: Write
```javascript
// -- Update specs/*.md --
// Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)
if (guidelineUpdates.length > 0) {
// Map guideline types to spec files
const specFileMap = {
convention: '.ccw/specs/coding-conventions.md',
constraint: '.ccw/specs/architecture-constraints.md',
learning: '.ccw/specs/coding-conventions.md' // learnings appended to conventions
}
for (const g of guidelineUpdates) {
const targetFile = specFileMap[g.type]
const existing = Read(targetFile)
const ruleText = g.type === 'learning'
? `- [${g.category}] ${g.text} (learned: ${new Date().toISOString().split('T')[0]})`
: `- [${g.category}] ${g.text}`
// Deduplicate: skip if text already in file
if (!existing.includes(g.text)) {
const newContent = existing.trimEnd() + '\n' + ruleText + '\n'
Write(targetFile, newContent)
}
}
// Rebuild spec index after writing
Bash('ccw spec rebuild')
}
// -- Update project-tech.json --
const techPath = '.workflow/project-tech.json'
const tech = JSON.parse(Read(techPath))
if (!tech.development_index) {
tech.development_index = { feature: [], enhancement: [], bugfix: [], refactor: [], docs: [] }
}
const category = detectCategory(summary)
tech.development_index[category].push(techEntry)
tech._metadata.last_updated = new Date().toISOString()
Write(techPath, JSON.stringify(tech, null, 2))
```
### Step 5: Confirm
```
Synced: ${guidelineUpdates.length} guidelines + 1 tech entry [${category}]
```
## Error Handling
| Error | Resolution |
|-------|------------|
| File missing | Create scaffold (same as $spec-setup Step 4) |
| No git history | Use user summary or session context only |
| No meaningful updates | Skip guidelines, still add tech entry |
| Duplicate entry | Skip silently (dedup check in Step 4) |
## Related Commands
- `$spec-setup` - Initialize project with specs scaffold
- `$spec-add` - Interactive wizard to create individual specs with scope selection
- `$workflow-plan` - Start planning with initialized project context

View File

@@ -0,0 +1,613 @@
---
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: AskUserQuestion, 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
1. **During Session**: Capture important decisions as they're made
2. **After Session**: Reflect on lessons learned before archiving
3. **Proactive**: Add team conventions or architectural rules
4. **Interactive**: Guided wizard for adding rules with full control over dimension, scope, and category
## Usage
```bash
$spec-add # Interactive wizard (all prompts)
$spec-add --interactive # Explicit interactive wizard
$spec-add "Use async/await instead of callbacks" # Direct mode (auto-detect type)
$spec-add -y "No direct DB access" --type constraint # Auto-confirm, skip confirmation
$spec-add --scope global --dimension personal # Create global personal spec (interactive)
$spec-add --dimension specs --category exploration # Project spec in exploration category (interactive)
$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
```javascript
// Parse arguments
const args = "$ARGUMENTS"
const argsLower = args.toLowerCase()
// Extract flags
const AUTO_YES = argsLower.includes('--yes') || argsLower.includes('-y')
const isInteractive = argsLower.includes('--interactive')
// Extract named parameters
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(/--scope\s+(\w+)/i)?.[1]?.toLowerCase() : null
// Extract rule text (everything before flags, or quoted string)
let ruleText = args
.replace(/--type\s+\w+/gi, '')
.replace(/--category\s+\w+/gi, '')
.replace(/--dimension\s+\w+/gi, '')
.replace(/--scope\s+\w+/gi, '')
.replace(/--interactive/gi, '')
.replace(/--yes/gi, '')
.replace(/-y\b/gi, '')
.replace(/^["']|["']$/g, '')
.trim()
// Validate values
if (scope && !['global', 'project'].includes(scope)) {
console.log("Invalid scope. Use 'global' or 'project'.")
return
}
if (dimension && !['specs', 'personal'].includes(dimension)) {
console.log("Invalid dimension. Use 'specs' or 'personal'.")
return
}
if (type && !['convention', 'constraint', 'learning'].includes(type)) {
console.log("Invalid type. Use 'convention', 'constraint', or 'learning'.")
return
}
if (category) {
const validCategories = [
'general', 'exploration', 'planning', 'execution',
'coding_style', 'naming_patterns', 'file_structure', 'documentation',
'architecture', 'tech_stack', 'performance', 'security',
'testing', 'process', 'other'
]
if (!validCategories.includes(category)) {
console.log(`Invalid category. Valid categories: ${validCategories.join(', ')}`)
return
}
}
```
### Step 2: Determine Mode
```javascript
const useInteractiveWizard = isInteractive || !ruleText
```
### Path A: Interactive Wizard
```javascript
if (useInteractiveWizard) {
// --- Step A1: Ask dimension (if not specified) ---
if (!dimension) {
if (AUTO_YES) {
dimension = 'specs' // Default to project specs in auto mode
} else {
const dimensionAnswer = ASK_USER([
{
id: "dimension", type: "select",
prompt: "What type of spec do you want to create?",
options: [
{ label: "Project Spec", 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/)" }
]
}
]) // BLOCKS (wait for user response)
dimension = dimensionAnswer.dimension === "Project Spec" ? "specs" : "personal"
}
}
// --- Step A2: Ask scope (if personal + scope not specified) ---
if (dimension === 'personal' && !scope) {
if (AUTO_YES) {
scope = 'project' // Default to project scope in auto mode
} else {
const scopeAnswer = ASK_USER([
{
id: "scope", type: "select",
prompt: "Where should this personal spec be stored?",
options: [
{ label: "Global (Recommended)", description: "Apply to ALL projects (~/.ccw/specs/personal/)" },
{ label: "Project-only", description: "Apply only to this project (.ccw/specs/personal/)" }
]
}
]) // BLOCKS (wait for user response)
scope = scopeAnswer.scope.includes("Global") ? "global" : "project"
}
}
// --- Step A3: Ask category (if not specified) ---
if (!category) {
if (AUTO_YES) {
category = 'general' // Default to general in auto mode
} else {
const categoryAnswer = ASK_USER([
{
id: "category", type: "select",
prompt: "Which workflow stage does this spec apply to?",
options: [
{ label: "General (Recommended)", description: "Applies to all stages (default)" },
{ label: "Exploration", description: "Code exploration, analysis, debugging" },
{ label: "Planning", description: "Task planning, requirements gathering" },
{ label: "Execution", description: "Implementation, testing, deployment" }
]
}
]) // BLOCKS (wait for user response)
const categoryLabel = categoryAnswer.category
category = categoryLabel.includes("General") ? "general"
: categoryLabel.includes("Exploration") ? "exploration"
: categoryLabel.includes("Planning") ? "planning"
: "execution"
}
}
// --- Step A4: Ask type (if not specified) ---
if (!type) {
if (AUTO_YES) {
type = 'convention' // Default to convention in auto mode
} else {
const typeAnswer = ASK_USER([
{
id: "type", type: "select",
prompt: "What type of rule is this?",
options: [
{ label: "Convention", description: "Coding style preference (e.g., use functional components)" },
{ label: "Constraint", description: "Hard rule that must not be violated (e.g., no direct DB access)" },
{ label: "Learning", description: "Insight or lesson learned (e.g., cache invalidation needs events)" }
]
}
]) // BLOCKS (wait for user response)
const typeLabel = typeAnswer.type
type = typeLabel.includes("Convention") ? "convention"
: typeLabel.includes("Constraint") ? "constraint"
: "learning"
}
}
// --- Step A5: Ask content (rule text) ---
if (!ruleText) {
if (AUTO_YES) {
console.log("Error: Rule text is required in auto mode. Provide rule text as argument.")
return
}
const contentAnswer = ASK_USER([
{
id: "content", type: "text",
prompt: "Enter the rule or guideline text:"
}
]) // BLOCKS (wait for user response)
ruleText = contentAnswer.content
}
}
```
### Path B: Direct Mode
**Auto-detect type if not specified**:
```javascript
function detectType(ruleText) {
const text = ruleText.toLowerCase();
// Constraint indicators
if (/\b(no|never|must not|forbidden|prohibited|always must)\b/.test(text)) {
return 'constraint';
}
// Learning indicators
if (/\b(learned|discovered|realized|found that|turns out)\b/.test(text)) {
return 'learning';
}
// Default to convention
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 'testing';
}
if (type === 'convention') {
if (/\b(name|naming|prefix|suffix|camel|pascal)\b/.test(text)) return 'naming_patterns';
if (/\b(file|folder|directory|structure|organize)\b/.test(text)) return 'file_structure';
if (/\b(doc|comment|jsdoc|readme)\b/.test(text)) return 'documentation';
return 'coding_style';
}
return type === 'constraint' ? 'tech_stack' : 'other';
}
if (!useInteractiveWizard) {
if (!type) {
type = detectType(ruleText)
}
if (!category) {
category = detectCategory(ruleText, type)
}
if (!dimension) {
dimension = 'specs' // Default to project specs in direct mode
}
}
```
### Step 3: Ensure Guidelines File Exists
**Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)**
```bash
bash(test -f .ccw/specs/coding-conventions.md && echo "EXISTS" || echo "NOT_FOUND")
```
**If NOT_FOUND**, initialize spec system:
```bash
Bash('ccw spec init')
Bash('ccw spec rebuild')
```
### Step 4: Determine Target File
```javascript
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') {
// Project specs - use .ccw/specs/ (same as frontend/backend spec-index-builder)
targetDir = '.ccw/specs'
if (isConstraint) {
targetFile = path.join(targetDir, 'architecture-constraints.md')
} else {
targetFile = path.join(targetDir, 'coding-conventions.md')
}
} else {
// Personal specs - use .ccw/personal/ (same as backend spec-index-builder)
if (scope === 'global') {
targetDir = path.join(os.homedir(), '.ccw', 'personal')
} else {
targetDir = path.join('.ccw', 'personal')
}
// Create type-based filename
const typePrefix = isConstraint ? 'constraints' : isLearning ? 'learnings' : 'conventions'
targetFile = path.join(targetDir, `${typePrefix}.md`)
}
```
### Step 5: Build Entry
```javascript
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
};
}
// For conventions and constraints, just return the rule string
return rule;
}
```
### Step 6: Write Spec
```javascript
const fs = require('fs')
// Ensure directory exists
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true })
}
// Check if file exists
const fileExists = fs.existsSync(targetFile)
if (!fileExists) {
// Create new file with frontmatter
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')
}
// Read existing content
let content = fs.readFileSync(targetFile, 'utf8')
// Deduplicate: skip if rule text already exists in the file
if (content.includes(ruleText)) {
console.log(`
Rule already exists in ${targetFile}
Text: "${ruleText}"
`)
return
}
// Format the new rule based on type
let newRule
if (isLearning) {
const entry = buildEntry(ruleText, type, category)
newRule = `- [learning/${category}] ${entry.insight} (${entry.date})`
} else {
newRule = `- [${category}] ${ruleText}`
}
// Append the rule
content = content.trimEnd() + '\n' + newRule + '\n'
fs.writeFileSync(targetFile, content, 'utf8')
// Rebuild spec index
Bash('ccw spec rebuild')
```
### 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
```bash
$spec-add --interactive
# Prompts for: dimension -> scope (if personal) -> category -> type -> content
```
### Add a Convention (Direct)
```bash
$spec-add "Use async/await instead of callbacks" --type convention --category coding_style
```
Result in `.ccw/specs/coding-conventions.md`:
```markdown
- [coding_style] Use async/await instead of callbacks
```
### Add an Architectural Constraint (Direct)
```bash
$spec-add "No direct DB access from controllers" --type constraint --category architecture
```
Result in `.ccw/specs/architecture-constraints.md`:
```markdown
- [architecture] No direct DB access from controllers
```
### Capture a Learning (Direct, Auto-detect)
```bash
$spec-add "Cache invalidation requires event sourcing for consistency" --type learning
```
Result in `.ccw/specs/coding-conventions.md`:
```markdown
- [learning/architecture] Cache invalidation requires event sourcing for consistency (2026-03-06)
```
### Auto-confirm Mode
```bash
$spec-add -y "No direct DB access from controllers" --type constraint
# Auto-detects category as 'architecture', writes without confirmation prompt
```
### Personal Spec (Global)
```bash
$spec-add --scope global --dimension personal --type convention "Prefer descriptive variable names"
```
Result in `~/.ccw/personal/conventions.md`:
```markdown
- [general] Prefer descriptive variable names
```
### Personal Spec (Project)
```bash
$spec-add --scope project --dimension personal --type constraint "No ORM in this project"
```
Result in `.ccw/personal/constraints.md`:
```markdown
- [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

View File

@@ -0,0 +1,657 @@
---
name: spec-setup
description: Initialize project-level state and configure specs via interactive questionnaire using cli-explore-agent
argument-hint: "[--regenerate] [--skip-specs] [--reset]"
allowed-tools: spawn_agent, wait, send_input, close_agent, AskUserQuestion, Read, Write, Edit, Bash, Glob, Grep
---
# Workflow Spec Setup Command
## Overview
Initialize `.workflow/project-tech.json` and `.ccw/specs/*.md` with comprehensive project understanding by delegating analysis to **cli-explore-agent**, then interactively configure project guidelines through a multi-round questionnaire.
**Dual File System**:
- `project-tech.json`: Auto-generated technical analysis (stack, architecture, components)
- `specs/*.md`: User-maintained rules and constraints (created and populated interactively)
**Design Principle**: Questions are dynamically generated based on the project's tech stack, architecture, and patterns -- not generic boilerplate.
**Note**: This command may be called by other workflow commands. Upon completion, return immediately to continue the calling workflow without interrupting the task flow.
## Usage
```bash
$spec-setup # Initialize (skip if exists)
$spec-setup --regenerate # Force regeneration of project-tech.json
$spec-setup --skip-specs # Initialize project-tech only, skip spec initialization and questionnaire
$spec-setup --reset # Reset specs content before questionnaire
```
## Execution Process
```
Input Parsing:
|- Parse --regenerate flag -> regenerate = true | false
|- Parse --skip-specs flag -> skipSpecs = true | false
+- Parse --reset flag -> reset = true | false
Decision:
|- BOTH_EXIST + no --regenerate + no --reset -> Exit: "Already initialized"
|- EXISTS + --regenerate -> Backup existing -> Continue analysis
|- EXISTS + --reset -> Reset specs, keep project-tech -> Skip to questionnaire
+- NOT_FOUND -> Continue full flow
Full Flow:
|- Step 1: Parse input and check existing state
|- Step 2: Get project metadata (name, root)
|- Step 3: Invoke cli-explore-agent (subagent)
| |- Structural scan (get_modules_by_depth.sh, find, wc)
| |- Semantic analysis (Gemini CLI)
| |- Synthesis and merge
| +- Write .workflow/project-tech.json
|- Step 4: Initialize Spec System (if not --skip-specs)
| |- Check if specs/*.md exist
| |- If NOT_FOUND -> Run ccw spec init
| +- Run ccw spec rebuild
|- Step 5: Multi-Round Interactive Questionnaire (if not --skip-specs)
| |- Check if guidelines already populated -> Ask: "Append / Reset / Cancel"
| |- Load project context from project-tech.json
| |- Round 1: Coding Conventions (coding_style, naming_patterns)
| |- Round 2: File & Documentation Conventions (file_structure, documentation)
| |- Round 3: Architecture & Tech Constraints (architecture, tech_stack)
| |- Round 4: Performance & Security Constraints (performance, security)
| +- Round 5: Quality Rules (quality_rules)
|- Step 6: Write specs/*.md (if not --skip-specs)
+- Step 7: Display Summary
Output:
|- .workflow/project-tech.json (+ .backup if regenerate)
+- .ccw/specs/*.md (scaffold or configured, unless --skip-specs)
```
## Implementation
### Step 1: Parse Input and Check Existing State
**Parse flags**:
```javascript
const regenerate = $ARGUMENTS.includes('--regenerate')
const skipSpecs = $ARGUMENTS.includes('--skip-specs')
const reset = $ARGUMENTS.includes('--reset')
```
**Check existing state**:
```bash
bash(test -f .workflow/project-tech.json && echo "TECH_EXISTS" || echo "TECH_NOT_FOUND")
bash(test -f .ccw/specs/coding-conventions.md && echo "SPECS_EXISTS" || echo "SPECS_NOT_FOUND")
```
**If BOTH_EXIST and no --regenerate and no --reset**: Exit early
```
Project already initialized:
- Tech analysis: .workflow/project-tech.json
- Guidelines: .ccw/specs/*.md
Use $spec-setup --regenerate to rebuild tech analysis
Use $spec-setup --reset to reconfigure guidelines
Use $spec-add to add individual rules
Use $workflow-status --project to view state
```
### Step 2: Get Project Metadata
```bash
bash(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
bash(git rev-parse --show-toplevel 2>/dev/null || pwd)
bash(mkdir -p .workflow)
```
### Step 3: Invoke cli-explore-agent (Subagent)
**For --regenerate**: Backup and preserve existing data
```bash
bash(cp .workflow/project-tech.json .workflow/project-tech.json.backup)
```
**Delegate analysis to subagent**:
```javascript
let exploreAgent = null
try {
exploreAgent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first)
2. Read: .workflow/project-tech.json (if exists, for --regenerate)
---
Analyze project for workflow initialization and generate .workflow/project-tech.json.
## MANDATORY FIRST STEPS
1. Execute: cat ~/.ccw/workflows/cli-templates/schemas/project-tech-schema.json (get schema reference)
2. Execute: ccw tool exec get_modules_by_depth '{}' (get project structure)
## Task
Generate complete project-tech.json following the schema structure:
- project_name: "${projectName}"
- initialized_at: ISO 8601 timestamp
- overview: {
description: "Brief project description",
technology_stack: {
languages: [{name, file_count, primary}],
frameworks: ["string"],
build_tools: ["string"],
test_frameworks: ["string"]
},
architecture: {style, layers: [], patterns: []},
key_components: [{name, path, description, importance}]
}
- features: []
- development_index: ${regenerate ? 'preserve from backup' : '{feature: [], enhancement: [], bugfix: [], refactor: [], docs: []}'}
- statistics: ${regenerate ? 'preserve from backup' : '{total_features: 0, total_sessions: 0, last_updated: ISO timestamp}'}
- _metadata: {initialized_by: "cli-explore-agent", analysis_timestamp: ISO timestamp, analysis_mode: "deep-scan"}
## Analysis Requirements
**Technology Stack**:
- Languages: File counts, mark primary
- Frameworks: From package.json, requirements.txt, go.mod, etc.
- Build tools: npm, cargo, maven, webpack, vite
- Test frameworks: jest, pytest, go test, junit
**Architecture**:
- Style: MVC, microservices, layered (from structure & imports)
- Layers: presentation, business-logic, data-access
- Patterns: singleton, factory, repository
- Key components: 5-10 modules {name, path, description, importance}
## Execution
1. Structural scan: get_modules_by_depth.sh, find, wc -l
2. Semantic analysis: Gemini for patterns/architecture
3. Synthesis: Merge findings
4. ${regenerate ? 'Merge with preserved development_index and statistics from .workflow/project-tech.json.backup' : ''}
5. Write JSON: Write('.workflow/project-tech.json', jsonContent)
6. Report: Return brief completion summary
Project root: ${projectRoot}
`
})
// Wait for completion
const result = wait({ ids: [exploreAgent], timeout_ms: 600000 })
if (result.timed_out) {
send_input({ id: exploreAgent, message: 'Complete analysis now and write project-tech.json.' })
const retry = wait({ ids: [exploreAgent], timeout_ms: 300000 })
if (retry.timed_out) throw new Error('Agent timeout')
}
} finally {
if (exploreAgent) close_agent({ id: exploreAgent })
}
```
### Step 4: Initialize Spec System (if not --skip-specs)
```javascript
// Skip spec initialization if --skip-specs flag is provided
if (!skipSpecs) {
// Initialize spec system if not already initialized
const specsCheck = Bash('test -f .ccw/specs/coding-conventions.md && echo EXISTS || echo NOT_FOUND')
if (specsCheck.includes('NOT_FOUND')) {
console.log('Initializing spec system...')
Bash('ccw spec init')
Bash('ccw spec rebuild')
}
} else {
console.log('Skipping spec initialization and questionnaire (--skip-specs)')
}
```
If `--skip-specs` is provided, skip directly to Step 7 (Display Summary) with limited output.
### Step 5: Multi-Round Interactive Questionnaire (if not --skip-specs)
#### Step 5.0: Check Existing Guidelines
If guidelines already have content, ask the user how to proceed:
```javascript
// Check if specs already have content via ccw spec list
const specsList = Bash('ccw spec list --json 2>/dev/null || echo "{}"')
const specsData = JSON.parse(specsList)
const isPopulated = (specsData.total || 0) > 5 // More than seed docs
if (isPopulated && !reset) {
const mode = ASK_USER([
{
id: "mode", type: "select",
prompt: "Project guidelines already contain entries. How would you like to proceed?",
options: [
{ label: "Append", description: "Keep existing entries and add new ones from the wizard" },
{ label: "Reset", description: "Clear all existing entries and start fresh" },
{ label: "Cancel", description: "Exit without changes" }
],
default: "Append"
}
]) // BLOCKS (wait for user response)
// If Cancel -> exit
// If Reset -> clear all arrays before proceeding
// If Append -> keep existing, wizard adds to them
}
// If --reset flag was provided, clear existing entries before proceeding
if (reset) {
// Reset specs content
console.log('Resetting existing guidelines...')
}
```
#### Step 5.1: Load Project Context
```javascript
// Load project context via ccw spec load for planning context
const projectContext = Bash('ccw spec load --category planning 2>/dev/null || echo "{}"')
const specData = JSON.parse(projectContext)
// Extract key info from loaded specs for generating smart questions
const languages = specData.overview?.technology_stack?.languages || []
const primaryLang = languages.find(l => l.primary)?.name || languages[0]?.name || 'Unknown'
const frameworks = specData.overview?.technology_stack?.frameworks || []
const testFrameworks = specData.overview?.technology_stack?.test_frameworks || []
const archStyle = specData.overview?.architecture?.style || 'Unknown'
const archPatterns = specData.overview?.architecture?.patterns || []
const buildTools = specData.overview?.technology_stack?.build_tools || []
```
#### Step 5.2: Multi-Round Questionnaire
Each round uses `ASK_USER` with project-aware options. The user can always select "Other" to provide custom input.
**CRITICAL**: After each round, collect the user's answers and convert them into guideline entries. Do NOT batch all rounds -- process each round's answers before proceeding to the next.
---
##### Round 1: Coding Conventions
Generate options dynamically based on detected language/framework:
```javascript
// Build language-specific coding style options
const codingStyleOptions = []
if (['TypeScript', 'JavaScript'].includes(primaryLang)) {
codingStyleOptions.push(
{ label: "Strict TypeScript", description: "Use strict mode, no 'any' type, explicit return types for public APIs" },
{ label: "Functional style", description: "Prefer pure functions, immutability, avoid class-based patterns where possible" },
{ label: "Const over let", description: "Always use const; only use let when reassignment is truly needed" }
)
} else if (primaryLang === 'Python') {
codingStyleOptions.push(
{ label: "Type hints", description: "Use type hints for all function signatures and class attributes" },
{ label: "Functional style", description: "Prefer pure functions, list comprehensions, avoid mutable state" },
{ label: "PEP 8 strict", description: "Strict PEP 8 compliance with max line length 88 (Black formatter)" }
)
} else if (primaryLang === 'Go') {
codingStyleOptions.push(
{ label: "Error wrapping", description: "Always wrap errors with context using fmt.Errorf with %w" },
{ label: "Interface first", description: "Define interfaces at the consumer side, not the provider" },
{ label: "Table-driven tests", description: "Use table-driven test pattern for all unit tests" }
)
}
// Add universal options
codingStyleOptions.push(
{ label: "Early returns", description: "Prefer early returns / guard clauses over deep nesting" }
)
// Round 1: Coding Conventions
const round1 = ASK_USER([
{
id: "coding_style", type: "multi-select",
prompt: `Your project uses ${primaryLang}. Which coding style conventions do you follow?`,
options: codingStyleOptions.slice(0, 4) // Max 4 options
},
{
id: "naming", type: "multi-select",
prompt: `What naming conventions does your ${primaryLang} project use?`,
options: [
{ label: "camelCase variables", description: "Variables and functions use camelCase (e.g., getUserName)" },
{ label: "PascalCase types", description: "Classes, interfaces, type aliases use PascalCase (e.g., UserService)" },
{ label: "UPPER_SNAKE constants", description: "Constants use UPPER_SNAKE_CASE (e.g., MAX_RETRIES)" },
{ label: "Prefix interfaces", description: "Prefix interfaces with 'I' (e.g., IUserService)" }
]
}
]) // BLOCKS (wait for user response)
```
**Process Round 1 answers** -> add to `conventions.coding_style` and `conventions.naming_patterns` arrays.
---
##### Round 2: File Structure & Documentation
```javascript
// Round 2: File Structure & Documentation
const round2 = ASK_USER([
{
id: "file_structure", type: "multi-select",
prompt: `Your project has a ${archStyle} architecture. What file organization rules apply?`,
options: [
{ label: "Co-located tests", description: "Test files live next to source files (e.g., foo.ts + foo.test.ts)" },
{ label: "Separate test dir", description: "Tests in a dedicated __tests__ or tests/ directory" },
{ label: "One export per file", description: "Each file exports a single main component/class/function" },
{ label: "Index barrels", description: "Use index.ts barrel files for clean imports from directories" }
]
},
{
id: "documentation", type: "multi-select",
prompt: "What documentation standards does your project follow?",
options: [
{ label: "JSDoc/docstring public APIs", description: "All public functions and classes must have JSDoc/docstrings" },
{ label: "README per module", description: "Each major module/package has its own README" },
{ label: "Inline comments for why", description: "Comments explain 'why', not 'what' -- code should be self-documenting" },
{ label: "No comment requirement", description: "Code should be self-explanatory; comments only for non-obvious logic" }
]
}
]) // BLOCKS (wait for user response)
```
**Process Round 2 answers** -> add to `conventions.file_structure` and `conventions.documentation`.
---
##### Round 3: Architecture & Tech Stack Constraints
```javascript
// Build architecture-specific options
const archOptions = []
if (archStyle.toLowerCase().includes('monolith')) {
archOptions.push(
{ label: "No circular deps", description: "Modules must not have circular dependencies" },
{ label: "Layer boundaries", description: "Strict layer separation: UI -> Service -> Data (no skipping layers)" }
)
} else if (archStyle.toLowerCase().includes('microservice')) {
archOptions.push(
{ label: "Service isolation", description: "Services must not share databases or internal state" },
{ label: "API contracts", description: "All inter-service communication through versioned API contracts" }
)
}
archOptions.push(
{ label: "Stateless services", description: "Service/business logic must be stateless (state in DB/cache only)" },
{ label: "Dependency injection", description: "Use dependency injection for testability, no hardcoded dependencies" }
)
// Round 3: Architecture & Tech Stack Constraints
const round3 = ASK_USER([
{
id: "architecture", type: "multi-select",
prompt: `Your ${archStyle} architecture uses ${archPatterns.join(', ') || 'various'} patterns. What architecture constraints apply?`,
options: archOptions.slice(0, 4)
},
{
id: "tech_stack", type: "multi-select",
prompt: `Tech stack: ${frameworks.join(', ')}. What technology constraints apply?`,
options: [
{ label: "No new deps without review", description: "Adding new dependencies requires explicit justification and review" },
{ label: "Pin dependency versions", description: "All dependencies must use exact versions, not ranges" },
{ label: "Prefer native APIs", description: "Use built-in/native APIs over third-party libraries when possible" },
{ label: "Framework conventions", description: `Follow official ${frameworks[0] || 'framework'} conventions and best practices` }
]
}
]) // BLOCKS (wait for user response)
```
**Process Round 3 answers** -> add to `constraints.architecture` and `constraints.tech_stack`.
---
##### Round 4: Performance & Security Constraints
```javascript
// Round 4: Performance & Security Constraints
const round4 = ASK_USER([
{
id: "performance", type: "multi-select",
prompt: "What performance requirements does your project have?",
options: [
{ label: "API response time", description: "API endpoints must respond within 200ms (p95)" },
{ label: "Bundle size limit", description: "Frontend bundle size must stay under 500KB gzipped" },
{ label: "Lazy loading", description: "Large modules/routes must use lazy loading / code splitting" },
{ label: "No N+1 queries", description: "Database access must avoid N+1 query patterns" }
]
},
{
id: "security", type: "multi-select",
prompt: "What security requirements does your project enforce?",
options: [
{ label: "Input sanitization", description: "All user input must be validated and sanitized before use" },
{ label: "No secrets in code", description: "No API keys, passwords, or tokens in source code -- use env vars" },
{ label: "Auth on all endpoints", description: "All API endpoints require authentication unless explicitly public" },
{ label: "Parameterized queries", description: "All database queries must use parameterized/prepared statements" }
]
}
]) // BLOCKS (wait for user response)
```
**Process Round 4 answers** -> add to `constraints.performance` and `constraints.security`.
---
##### Round 5: Quality Rules
```javascript
// Round 5: Quality Rules
const round5 = ASK_USER([
{
id: "quality", type: "multi-select",
prompt: `Testing with ${testFrameworks.join(', ') || 'your test framework'}. What quality rules apply?`,
options: [
{ label: "Min test coverage", description: "Minimum 80% code coverage for new code; no merging below threshold" },
{ label: "No skipped tests", description: "Tests must not be skipped (.skip/.only) in committed code" },
{ label: "Lint must pass", description: "All code must pass linter checks before commit (enforced by pre-commit)" },
{ label: "Type check must pass", description: "Full type checking (tsc --noEmit) must pass with zero errors" }
]
}
]) // BLOCKS (wait for user response)
```
**Process Round 5 answers** -> add to `quality_rules` array as `{ rule, scope, enforced_by }` objects.
### Step 6: Write specs/*.md (if not --skip-specs)
For each category of collected answers, append rules to the corresponding spec MD file. Each spec file uses YAML frontmatter with `readMode`, `priority`, `category`, and `keywords`.
**Category Assignment**: Based on the round and question type:
- Round 1-2 (conventions): `category: general` (applies to all stages)
- Round 3 (architecture/tech): `category: planning` (planning phase)
- Round 4 (performance/security): `category: execution` (implementation phase)
- Round 5 (quality): `category: execution` (testing phase)
```javascript
// Helper: append rules to a spec MD file with category support
// Uses .ccw/specs/ directory (same as frontend/backend spec-index-builder)
function appendRulesToSpecFile(filePath, rules, defaultCategory = 'general') {
if (rules.length === 0) return
// Ensure .ccw/specs/ directory exists
const specDir = path.dirname(filePath)
if (!fs.existsSync(specDir)) {
fs.mkdirSync(specDir, { recursive: true })
}
// Check if file exists
if (!file_exists(filePath)) {
// Create file with frontmatter including category
const frontmatter = `---
title: ${filePath.includes('conventions') ? 'Coding Conventions' : filePath.includes('constraints') ? 'Architecture Constraints' : 'Quality Rules'}
readMode: optional
priority: medium
category: ${defaultCategory}
scope: project
dimension: specs
keywords: [${defaultCategory}, ${filePath.includes('conventions') ? 'convention' : filePath.includes('constraints') ? 'constraint' : 'quality'}]
---
# ${filePath.includes('conventions') ? 'Coding Conventions' : filePath.includes('constraints') ? 'Architecture Constraints' : 'Quality Rules'}
`
Write(filePath, frontmatter)
}
const existing = Read(filePath)
// Append new rules as markdown list items after existing content
const newContent = existing.trimEnd() + '\n' + rules.map(r => `- ${r}`).join('\n') + '\n'
Write(filePath, newContent)
}
// Write conventions (general category) - use .ccw/specs/ (same as frontend/backend)
appendRulesToSpecFile('.ccw/specs/coding-conventions.md',
[...newCodingStyle, ...newNamingPatterns, ...newFileStructure, ...newDocumentation],
'general')
// Write constraints (planning category)
appendRulesToSpecFile('.ccw/specs/architecture-constraints.md',
[...newArchitecture, ...newTechStack, ...newPerformance, ...newSecurity],
'planning')
// Write quality rules (execution category)
if (newQualityRules.length > 0) {
const qualityPath = '.ccw/specs/quality-rules.md'
if (!file_exists(qualityPath)) {
Write(qualityPath, `---
title: Quality Rules
readMode: required
priority: high
category: execution
scope: project
dimension: specs
keywords: [execution, quality, testing, coverage, lint]
---
# Quality Rules
`)
}
appendRulesToSpecFile(qualityPath,
newQualityRules.map(q => `${q.rule} (scope: ${q.scope}, enforced by: ${q.enforced_by})`),
'execution')
}
// Rebuild spec index after writing
Bash('ccw spec rebuild')
```
#### Answer Processing Rules
When converting user selections to guideline entries:
1. **Selected option** -> Use the option's `description` as the guideline string (it's more precise than the label)
2. **"Other" with custom text** -> Use the user's text directly as the guideline string
3. **Deduplication** -> Skip entries that already exist in the guidelines (exact string match)
4. **Quality rules** -> Convert to `{ rule: description, scope: "all", enforced_by: "code-review" }` format
### Step 7: Display Summary
```javascript
const projectTech = JSON.parse(Read('.workflow/project-tech.json'));
if (skipSpecs) {
// Minimal summary for --skip-specs mode
console.log(`
Project initialized successfully (tech analysis only)
## Project Overview
Name: ${projectTech.project_name}
Description: ${projectTech.overview.description}
### Technology Stack
Languages: ${projectTech.overview.technology_stack.languages.map(l => l.name).join(', ')}
Frameworks: ${projectTech.overview.technology_stack.frameworks.join(', ')}
### Architecture
Style: ${projectTech.overview.architecture.style}
Components: ${projectTech.overview.key_components.length} core modules
---
Files created:
- Tech analysis: .workflow/project-tech.json
- Specs: (skipped via --skip-specs)
${regenerate ? '- Backup: .workflow/project-tech.json.backup' : ''}
Next steps:
- Use $spec-setup (without --skip-specs) to configure guidelines
- Use $spec-add to create individual specs
- Use $workflow-plan to start planning
`);
} else {
// Full summary with guidelines stats
const countConventions = newCodingStyle.length + newNamingPatterns.length
+ newFileStructure.length + newDocumentation.length
const countConstraints = newArchitecture.length + newTechStack.length
+ newPerformance.length + newSecurity.length
const countQuality = newQualityRules.length
// Get updated spec list
const specsList = Bash('ccw spec list --json 2>/dev/null || echo "{}"')
console.log(`
Project initialized and guidelines configured
## Project Overview
Name: ${projectTech.project_name}
Description: ${projectTech.overview.description}
### Technology Stack
Languages: ${projectTech.overview.technology_stack.languages.map(l => l.name).join(', ')}
Frameworks: ${projectTech.overview.technology_stack.frameworks.join(', ')}
### Architecture
Style: ${projectTech.overview.architecture.style}
Components: ${projectTech.overview.key_components.length} core modules
### Guidelines Summary
- Conventions: ${countConventions} rules added to coding-conventions.md
- Constraints: ${countConstraints} rules added to architecture-constraints.md
- Quality rules: ${countQuality} rules added to quality-rules.md
Spec index rebuilt. Use \`ccw spec list\` to view all specs.
---
Files created:
- Tech analysis: .workflow/project-tech.json
- Specs: .ccw/specs/ (configured)
${regenerate ? '- Backup: .workflow/project-tech.json.backup' : ''}
Next steps:
- Use $spec-add to add individual rules later
- Specs are auto-loaded via hook on each prompt
- Use $workflow-plan to start planning
`);
}
```
## Error Handling
| Situation | Action |
|-----------|--------|
| **Agent Failure** | Fall back to basic initialization with placeholder overview |
| **Missing Tools** | Agent uses Qwen fallback or bash-only |
| **Empty Project** | Create minimal JSON with all gaps identified |
| **No project-tech.json** (when --reset without prior init) | Run full flow from Step 2 |
| **User cancels mid-wizard** | Save whatever was collected so far (partial is better than nothing) |
| **File write failure** | Report error, suggest manual edit |
## Related Commands
- `$spec-add` - Interactive wizard to create individual specs with scope selection
- `$session-sync` - Quick-sync session work to specs and project-tech
- `$workflow-plan` - Start planning with initialized project context
- `$workflow-status --project` - View project state and guidelines

View File

@@ -717,7 +717,17 @@ AskUserQuestion({
| Retry Failed | Filter tasks with `_execution.status === 'failed'`, re-execute, append `[RETRY]` events |
| View Events | Display execution-events.md content |
| Create Issue | `Skill(skill="issue:new", args="...")` from failed task details |
| Done | Display artifact paths, end workflow |
| Done | Display artifact paths, sync session state, end workflow |
### Step 4.5: Sync Session State
After completion (regardless of user selection), unless `--dry-run`:
```bash
$session-sync -y "Execution complete: {completed}/{total} tasks succeeded"
```
Updates specs/*.md with execution learnings and project-tech.json with development index entry.
---

View File

@@ -208,7 +208,7 @@ Phase 2: Test-Cycle Execution (phases/02-test-cycle-execute.md)
│ ├─ spawn_agent(@cli-planning-agent) → IMPL-fix-N.json
│ ├─ spawn_agent(@test-fix-agent) → Apply fix & re-test
│ └─ Re-test → Back to decision
└─ Completion: Final validation → Summary → Auto-complete session
└─ Completion: Final validation → Summary → Sync session state → Auto-complete session
```
## Core Rules
@@ -387,5 +387,6 @@ try {
- `test-fix-agent` (~/.codex/agents/test-fix-agent.md) - Test execution, code fixes, criticality assignment
**Follow-up**:
- Session sync: `$session-sync -y "Test-fix cycle complete: {pass_rate}% pass rate"`
- Session auto-complete on success
- Issue creation for follow-up work (post-completion expansion)