Files
Claude-Code-Workflow/.claude/commands/workflow/init-specs.md
catlog22 99a3561f71 feat(workflow): add unified workflow spec command system
- Add /workflow:init-specs command for interactive spec creation with scope selection (global/project)
- Update /workflow:init to chain solidify and add --skip-specs flag
- Add category field support to generated specs frontmatter
- Add GET /api/project-tech/stats endpoint for development progress stats
- Add devProgressInjection settings to system configuration
- Add development progress injection control card to GlobalSettingsTab
- Add i18n keys for new settings in en/zh locales
2026-02-27 12:25:26 +08:00

12 KiB

name, description, argument-hint, examples
name description argument-hint examples
init-specs Interactive wizard to create individual specs or personal constraints with scope selection [--scope <global|project>] [--dimension <specs|personal>] [--category <general|exploration|planning|execution>]
/workflow:init-specs
/workflow:init-specs --scope global --dimension personal
/workflow:init-specs --scope project --dimension specs

Workflow Init Specs Command (/workflow:init-specs)

Overview

Interactive wizard for creating individual specs or personal constraints with scope selection. This command provides a guided experience for adding new rules to the spec system.

Key Features:

  • Supports both project specs and personal specs
  • Scope selection (global vs project) for personal specs
  • Category-based organization for workflow stages
  • Interactive mode with smart defaults

Usage

/workflow:init-specs                              # Interactive mode (all prompts)
/workflow:init-specs --scope global               # Create global personal spec
/workflow:init-specs --scope project              # Create project spec (default)
/workflow:init-specs --dimension specs            # Project conventions/constraints
/workflow:init-specs --dimension personal         # Personal preferences
/workflow:init-specs --category exploration       # Workflow stage category

Parameters

Parameter Values Default Description
--scope global, project project Where to store the spec (only for personal dimension)
--dimension specs, personal Interactive Type of spec to create
--category general, exploration, planning, execution general Workflow stage category

Execution Process

Input Parsing:
   ├─ Parse --scope (global | project)
   ├─ Parse --dimension (specs | personal)
   └─ Parse --category (general | exploration | planning | execution)

Step 1: Gather Requirements (Interactive)
   ├─ If dimension not specified → Ask dimension
   ├─ If personal + scope not specified → Ask scope
   ├─ If category not specified → Ask category
   ├─ Ask type (convention | constraint | learning)
   └─ Ask content (rule text)

Step 2: Determine Target File
   ├─ specs dimension → .workflow/specs/coding-conventions.md or architecture-constraints.md
   └─ personal dimension → ~/.ccw/specs/personal/ or .ccw/specs/personal/

Step 3: Write Spec
   ├─ Check if file exists, create if needed with proper frontmatter
   ├─ Append rule to appropriate section
   └─ Run ccw spec rebuild

Step 4: Display Confirmation

Implementation

Step 1: Parse Input and Gather Requirements

// Parse arguments
const args = $ARGUMENTS.toLowerCase()
const hasScope = args.includes('--scope')
const hasDimension = args.includes('--dimension')
const hasCategory = args.includes('--category')

// Extract values from arguments
let scope = hasScope ? args.match(/--scope\s+(\w+)/)?.[1] : null
let dimension = hasDimension ? args.match(/--dimension\s+(\w+)/)?.[1] : null
let category = hasCategory ? args.match(/--category\s+(\w+)/)?.[1] : null

// 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 (category && !['general', 'exploration', 'planning', 'execution'].includes(category)) {
  console.log("Invalid category. Use 'general', 'exploration', 'planning', or 'execution'.")
  return
}

Step 2: Interactive Questions

If dimension not specified:

if (!dimension) {
  const dimensionAnswer = AskUserQuestion({
    questions: [{
      question: "What type of spec do you want to create?",
      header: "Dimension",
      multiSelect: false,
      options: [
        {
          label: "Project Spec",
          description: "Coding conventions, constraints, quality rules for this project (stored in .workflow/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"] === "Project Spec" ? "specs" : "personal"
}

If personal dimension and scope not specified:

if (dimension === 'personal' && !scope) {
  const scopeAnswer = AskUserQuestion({
    questions: [{
      question: "Where should this personal spec be stored?",
      header: "Scope",
      multiSelect: false,
      options: [
        {
          label: "Global (Recommended)",
          description: "Apply to ALL projects (~/.ccw/specs/personal/)"
        },
        {
          label: "Project-only",
          description: "Apply only to this project (.ccw/specs/personal/)"
        }
      ]
    }]
  })
  scope = scopeAnswer.answers["Scope"].includes("Global") ? "global" : "project"
}

If category not specified:

if (!category) {
  const categoryAnswer = AskUserQuestion({
    questions: [{
      question: "Which workflow stage does this spec apply to?",
      header: "Category",
      multiSelect: false,
      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"
        }
      ]
    }]
  })
  const categoryLabel = categoryAnswer.answers["Category"]
  category = categoryLabel.includes("General") ? "general"
    : categoryLabel.includes("Exploration") ? "exploration"
    : categoryLabel.includes("Planning") ? "planning"
    : "execution"
}

Ask type:

const typeAnswer = AskUserQuestion({
  questions: [{
    question: "What type of rule is this?",
    header: "Type",
    multiSelect: false,
    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)"
      }
    ]
  }]
})
const type = typeAnswer.answers["Type"]
const isConvention = type.includes("Convention")
const isConstraint = type.includes("Constraint")
const isLearning = type.includes("Learning")

Ask content:

const contentAnswer = AskUserQuestion({
  questions: [{
    question: "Enter the rule or guideline text:",
    header: "Content",
    multiSelect: false,
    options: []
  }]
})
const ruleText = contentAnswer.answers["Content"]

Step 3: Determine Target File

const path = require('path')
const os = require('os')

let targetFile: string
let targetDir: string

if (dimension === 'specs') {
  // Project specs
  targetDir = '.workflow/specs'
  if (isConstraint) {
    targetFile = path.join(targetDir, 'architecture-constraints.md')
  } else {
    targetFile = path.join(targetDir, 'coding-conventions.md')
  }
} else {
  // Personal specs
  if (scope === 'global') {
    targetDir = path.join(os.homedir(), '.ccw', 'specs', 'personal')
  } else {
    targetDir = path.join('.ccw', 'specs', 'personal')
  }

  // Create category-based filename
  const typePrefix = isConstraint ? 'constraints' : isLearning ? 'learnings' : 'conventions'
  targetFile = path.join(targetDir, `${typePrefix}.md`)
}

Step 4: Write Spec

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')

// Format the new rule
const timestamp = new Date().toISOString().split('T')[0]
const rulePrefix = isLearning ? `- [learning] ` : `- [${category}] `
const ruleSuffix = isLearning ? ` (${timestamp})` : ''
const newRule = `${rulePrefix}${ruleText}${ruleSuffix}`

// Check for duplicate
if (content.includes(ruleText)) {
  console.log(`
Rule already exists in ${targetFile}
Text: "${ruleText}"
`)
  return
}

// Append the rule
content = content.trimEnd() + '\n' + newRule + '\n'
fs.writeFileSync(targetFile, content, 'utf8')

// Rebuild spec index
Bash('ccw spec rebuild')

Step 5: Display 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)

.workflow/specs/
├── coding-conventions.md    ← conventions, learnings
├── architecture-constraints.md  ← constraints
└── quality-rules.md         ← quality rules

Personal Specs (dimension: personal)

# Global (~/.ccw/specs/personal/)
~/.ccw/specs/personal/
├── conventions.md           ← personal conventions (all projects)
├── constraints.md           ← personal constraints (all projects)
└── learnings.md             ← personal learnings (all projects)

# Project-local (.ccw/specs/personal/)
.ccw/specs/personal/
├── conventions.md           ← personal conventions (this project only)
├── constraints.md           ← personal constraints (this project only)
└── learnings.md             ← personal learnings (this project only)

Category Field Usage

The category field in frontmatter enables filtered loading:

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"

Error Handling

  • File not writable: Check permissions, suggest manual creation
  • Duplicate rule: Warn and skip (don't add duplicates)
  • Invalid path: Exit with error message
  • /workflow:init - Initialize project with specs scaffold
  • /workflow:init-guidelines - Interactive wizard to fill specs
  • /workflow:session:solidify - Add rules during/after sessions
  • ccw spec list - View all specs
  • ccw spec load --category <cat> - Load filtered specs