From ffe79d28e2b9dc587d83b076720a15894ef13f27 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Thu, 26 Feb 2026 23:13:00 +0800 Subject: [PATCH] refactor(spec): update folder structure and dimensions - Change folder from .workflow/ to .ccw/ - Reduce dimensions from 4 to 2: specs, personal - Remove changelog and roadmap dimensions - Update help text and examples Folder structure: - .ccw/specs/ - Project rules and conventions - .ccw/personal/ - Personal preferences (supports global ~/.ccw/personal/) - .ccw/.spec-index/ - Index cache Keyword categories for workflow stages: - exploration - Code exploration, analysis, debugging - planning - Task planning, requirements - execution - Implementation, testing, deployment --- ccw/src/commands/{rules.ts => spec.ts} | 12 ++-- ...index-builder.ts => spec-index-builder.ts} | 36 ++++++----- ccw/src/tools/{rules-init.ts => spec-init.ts} | 64 +++++-------------- ...extractor.ts => spec-keyword-extractor.ts} | 0 .../tools/{rules-loader.ts => spec-loader.ts} | 4 +- 5 files changed, 41 insertions(+), 75 deletions(-) rename ccw/src/commands/{rules.ts => spec.ts} (96%) rename ccw/src/tools/{rules-index-builder.ts => spec-index-builder.ts} (90%) rename ccw/src/tools/{rules-init.ts => spec-init.ts} (82%) rename ccw/src/tools/{rules-keyword-extractor.ts => spec-keyword-extractor.ts} (100%) rename ccw/src/tools/{rules-loader.ts => spec-loader.ts} (99%) diff --git a/ccw/src/commands/rules.ts b/ccw/src/commands/spec.ts similarity index 96% rename from ccw/src/commands/rules.ts rename to ccw/src/commands/spec.ts index b545a0b8..5ba0770e 100644 --- a/ccw/src/commands/rules.ts +++ b/ccw/src/commands/spec.ts @@ -95,7 +95,7 @@ async function loadAction(options: SpecOptions): Promise { const result = await loadSpecs({ projectPath, - dimension: dimension as 'specs' | 'roadmap' | 'changelog' | 'personal' | undefined, + dimension: dimension as 'specs' | 'personal' | undefined, keywords, outputFormat: stdin ? 'hook' : 'cli', stdinData, @@ -355,12 +355,12 @@ ${chalk.bold('USAGE')} ${chalk.bold('SUBCOMMANDS')} load Load specs matching dimension/keywords (CLI or Hook mode) list List all indexed specs with readMode and keyword info - rebuild Force re-scan of MD files and rebuild .spec-index cache + rebuild Force re-scan of MD files and rebuild .ccw/.spec-index cache status Show per-dimension stats (total, required, optional, freshness) - init Create 4-dimension directory structure with seed MD documents + init Create 2-dimension directory structure with seed MD documents ${chalk.bold('OPTIONS')} - --dimension Target dimension: specs, roadmap, changelog, personal + --dimension Target dimension: specs, personal --keywords Keywords for spec matching (space or comma separated) --stdin Read input from stdin (Hook mode) --json Output as JSON @@ -368,7 +368,7 @@ ${chalk.bold('OPTIONS')} ${chalk.bold('KEYWORD CATEGORIES')} Use these predefined keywords to load specs for specific workflow stages: ${chalk.cyan('exploration')} - Code exploration, analysis, debugging context - ${chalk.cyan('planning')} - Task planning, roadmap, requirements context + ${chalk.cyan('planning')} - Task planning, requirements context ${chalk.cyan('execution')} - Implementation, testing, deployment context ${chalk.bold('EXAMPLES')} @@ -400,7 +400,7 @@ ${chalk.bold('EXAMPLES')} ccw spec rebuild ${chalk.gray('# Rebuild single dimension:')} - ccw spec rebuild --dimension roadmap + ccw spec rebuild --dimension specs ${chalk.gray('# Check system status:')} ccw spec status diff --git a/ccw/src/tools/rules-index-builder.ts b/ccw/src/tools/spec-index-builder.ts similarity index 90% rename from ccw/src/tools/rules-index-builder.ts rename to ccw/src/tools/spec-index-builder.ts index 510ebe46..842a931c 100644 --- a/ccw/src/tools/rules-index-builder.ts +++ b/ccw/src/tools/spec-index-builder.ts @@ -1,10 +1,10 @@ /** * Spec Index Builder * - * Scans .workflow/{dimension}/*.md files, parses YAML frontmatter via - * gray-matter, and writes .spec-index/{dimension}.index.json cache files. + * Scans .ccw/{dimension}/*.md files, parses YAML frontmatter via + * gray-matter, and writes .ccw/.spec-index/{dimension}.index.json cache files. * - * Supports 4 dimensions: specs, roadmap, changelog, personal + * Supports 2 dimensions: specs, personal * * YAML Frontmatter Schema: * --- @@ -83,9 +83,11 @@ export interface DimensionIndex { // ============================================================================ /** - * The 4 supported spec dimensions. + * The 2 supported spec dimensions. + * - specs: Project rules and conventions + * - personal: Personal preferences (supports global ~/.ccw/personal/) */ -export const SPEC_DIMENSIONS = ['specs', 'roadmap', 'changelog', 'personal'] as const; +export const SPEC_DIMENSIONS = ['specs', 'personal'] as const; export type SpecDimension = typeof SPEC_DIMENSIONS[number]; @@ -100,9 +102,9 @@ const VALID_READ_MODES = ['required', 'optional'] as const; const VALID_PRIORITIES = ['critical', 'high', 'medium', 'low'] as const; /** - * Directory name for spec index cache files (inside .workflow/). + * Directory name for spec index cache files (inside .ccw/). */ -const WORKFLOW_DIR = '.workflow'; +const CCW_DIR = '.ccw'; const SPEC_INDEX_DIR = '.spec-index'; // ============================================================================ @@ -114,27 +116,27 @@ const SPEC_INDEX_DIR = '.spec-index'; * * @param projectPath - Project root directory * @param dimension - The dimension name - * @returns Absolute path to .workflow/.spec-index/{dimension}.index.json + * @returns Absolute path to .ccw/.spec-index/{dimension}.index.json */ export function getIndexPath(projectPath: string, dimension: string): string { - return join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR, `${dimension}.index.json`); + return join(projectPath, CCW_DIR, SPEC_INDEX_DIR, `${dimension}.index.json`); } /** - * Get the path to the .workflow/{dimension} directory. + * Get the path to the .ccw/{dimension} directory. * * @param projectPath - Project root directory * @param dimension - The dimension name - * @returns Absolute path to .workflow/{dimension}/ + * @returns Absolute path to .ccw/{dimension}/ */ export function getDimensionDir(projectPath: string, dimension: string): string { - return join(projectPath, '.workflow', dimension); + return join(projectPath, CCW_DIR, dimension); } /** * Build the index for a single dimension. * - * Scans .workflow/{dimension}/*.md files, parses YAML frontmatter, + * Scans .ccw/{dimension}/*.md files, parses YAML frontmatter, * extracts the 5 required fields, and returns a DimensionIndex. * * Files with malformed or missing frontmatter are skipped gracefully. @@ -194,15 +196,15 @@ export async function buildDimensionIndex( } /** - * Build indices for all 4 dimensions and write to .spec-index/. + * Build indices for all dimensions and write to .ccw/.spec-index/. * - * Creates .spec-index/ directory if it doesn't exist. + * Creates .ccw/.spec-index/ directory if it doesn't exist. * Writes {dimension}.index.json for each dimension. * * @param projectPath - Project root directory */ export async function buildAllIndices(projectPath: string): Promise { - const indexDir = join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR); + const indexDir = join(projectPath, CCW_DIR, SPEC_INDEX_DIR); // Ensure .spec-index directory exists if (!existsSync(indexDir)) { @@ -283,7 +285,7 @@ export async function getDimensionIndex( // Build fresh and cache const index = await buildDimensionIndex(projectPath, dimension); - const indexDir = join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR); + const indexDir = join(projectPath, CCW_DIR, SPEC_INDEX_DIR); if (!existsSync(indexDir)) { mkdirSync(indexDir, { recursive: true }); } diff --git a/ccw/src/tools/rules-init.ts b/ccw/src/tools/spec-init.ts similarity index 82% rename from ccw/src/tools/rules-init.ts rename to ccw/src/tools/spec-init.ts index baa7811f..7b0f5e46 100644 --- a/ccw/src/tools/rules-init.ts +++ b/ccw/src/tools/spec-init.ts @@ -1,9 +1,9 @@ /** - * Spec Init - Initialize the 4-dimension spec system + * Spec Init - Initialize the 2-dimension spec system * - * Creates .workflow/specs/, .workflow/roadmap/, .workflow/changelog/, - * .workflow/personal/, and .workflow/.spec-index/ directories with - * seed MD documents containing YAML frontmatter templates. + * Creates .ccw/specs/, .ccw/personal/, + * and .ccw/.spec-index/ directories with seed MD documents + * containing YAML frontmatter templates. * * Idempotent: skips existing files, only creates missing directories/files. */ @@ -39,7 +39,7 @@ export interface InitResult { // Constants // --------------------------------------------------------------------------- -export const DIMENSIONS = ['specs', 'roadmap', 'changelog', 'personal'] as const; +export const DIMENSIONS = ['specs', 'personal'] as const; export const INDEX_DIR = '.spec-index'; // --------------------------------------------------------------------------- @@ -55,7 +55,7 @@ export const SEED_DOCS: Map = new Map([ frontmatter: { title: 'Coding Conventions', dimension: 'specs', - keywords: ['typescript', 'naming', 'style', 'convention'], + keywords: ['typescript', 'naming', 'style', 'convention', 'exploration', 'planning', 'execution'], readMode: 'required', priority: 'high', }, @@ -91,7 +91,7 @@ export const SEED_DOCS: Map = new Map([ frontmatter: { title: 'Architecture Constraints', dimension: 'specs', - keywords: ['architecture', 'module', 'layer', 'pattern'], + keywords: ['architecture', 'module', 'layer', 'pattern', 'exploration', 'planning'], readMode: 'required', priority: 'high', }, @@ -174,40 +174,6 @@ export const SEED_DOCS: Map = new Map([ }, ], ], - [ - 'roadmap', - [ - { - filename: 'current.md', - frontmatter: { - title: 'Current Roadmap', - dimension: 'roadmap', - keywords: ['roadmap', 'plan', 'milestone'], - readMode: 'optional', - priority: 'medium', - }, - body: `# Current Roadmap - -## Active Milestone - -- Milestone name and target date -- Key deliverables - -## Upcoming - -- Next planned features or improvements - -## Completed - -- Recently completed milestones for reference -`, - }, - ], - ], - [ - 'changelog', - [], - ], ]); // --------------------------------------------------------------------------- @@ -246,29 +212,29 @@ export function formatFrontmatter(fm: SpecFrontmatter): string { * @returns InitResult with lists of created/skipped paths */ export function initSpecSystem(projectPath: string): InitResult { - const workflowDir = join(projectPath, '.workflow'); + const ccwDir = join(projectPath, '.ccw'); const result: InitResult = { created: [], skipped: [], directories: [], }; - // Ensure .workflow root exists - if (!existsSync(workflowDir)) { - mkdirSync(workflowDir, { recursive: true }); + // Ensure .ccw root exists + if (!existsSync(ccwDir)) { + mkdirSync(ccwDir, { recursive: true }); } // Create dimension directories for (const dim of DIMENSIONS) { - const dirPath = join(workflowDir, dim); + const dirPath = join(ccwDir, dim); if (!existsSync(dirPath)) { mkdirSync(dirPath, { recursive: true }); result.directories.push(dirPath); } } - // Create index directory at project root (matches spec-index-builder.ts location) - const indexPath = join(projectPath, INDEX_DIR); + // Create index directory inside .ccw (matches spec-index-builder.ts location) + const indexPath = join(ccwDir, INDEX_DIR); if (!existsSync(indexPath)) { mkdirSync(indexPath, { recursive: true }); result.directories.push(indexPath); @@ -276,7 +242,7 @@ export function initSpecSystem(projectPath: string): InitResult { // Write seed documents per dimension for (const [dimension, docs] of SEED_DOCS) { - const dimDir = join(workflowDir, dimension); + const dimDir = join(ccwDir, dimension); for (const doc of docs) { const filePath = join(dimDir, doc.filename); diff --git a/ccw/src/tools/rules-keyword-extractor.ts b/ccw/src/tools/spec-keyword-extractor.ts similarity index 100% rename from ccw/src/tools/rules-keyword-extractor.ts rename to ccw/src/tools/spec-keyword-extractor.ts diff --git a/ccw/src/tools/rules-loader.ts b/ccw/src/tools/spec-loader.ts similarity index 99% rename from ccw/src/tools/rules-loader.ts rename to ccw/src/tools/spec-loader.ts index a8b79de8..030a5ad4 100644 --- a/ccw/src/tools/rules-loader.ts +++ b/ccw/src/tools/spec-loader.ts @@ -105,9 +105,7 @@ interface LoadedSpec { */ const DIMENSION_PRIORITY: Record = { personal: 1, - changelog: 2, - roadmap: 3, - specs: 4, + specs: 2, }; /**