feat: Enhance spec management with new hooks and settings features

- Updated test cycle execution steps to streamline agent execution.
- Improved HookDialog component with enhanced validation messages and localization.
- Introduced SpecDialog component for better spec management.
- Added new hooks for fetching and updating specs list and frontmatter.
- Implemented API functions for specs list retrieval and index rebuilding.
- Added localization support for new specs settings and hooks.
- Enhanced SpecsSettingsPage to manage project and personal specs effectively.
- Updated CLI commands to support keyword-based spec loading.
- Improved spec index builder to categorize specs by workflow stages.
This commit is contained in:
catlog22
2026-02-26 22:52:33 +08:00
parent 6155fcc7b8
commit 151b81ee4a
51 changed files with 731 additions and 690 deletions

View File

@@ -11,7 +11,7 @@ import chalk from 'chalk';
interface SpecOptions {
dimension?: string;
context?: string;
keywords?: string;
stdin?: boolean;
json?: boolean;
}
@@ -60,11 +60,11 @@ function getProjectPath(hookCwd?: string): string {
/**
* Load action - load specs matching dimension/keywords.
*
* CLI mode: --dimension and --context options, outputs formatted markdown.
* CLI mode: --dimension and --keywords options, outputs formatted markdown.
* Hook mode: --stdin reads JSON {session_id, cwd, user_prompt}, outputs JSON {continue, systemMessage}.
*/
async function loadAction(options: SpecOptions): Promise<void> {
const { stdin, dimension, context } = options;
const { stdin, dimension, keywords: keywordsInput } = options;
let projectPath: string;
let stdinData: StdinData | undefined;
@@ -89,8 +89,8 @@ async function loadAction(options: SpecOptions): Promise<void> {
try {
const { loadSpecs } = await import('../tools/spec-loader.js');
const keywords = context
? context.split(/[\s,]+/).filter(Boolean)
const keywords = keywordsInput
? keywordsInput.split(/[\s,]+/).filter(Boolean)
: undefined;
const result = await loadSpecs({
@@ -361,19 +361,31 @@ ${chalk.bold('SUBCOMMANDS')}
${chalk.bold('OPTIONS')}
--dimension <dim> Target dimension: specs, roadmap, changelog, personal
--context <text> Context text for keyword extraction (CLI mode)
--keywords <text> Keywords for spec matching (space or comma separated)
--stdin Read input from stdin (Hook mode)
--json Output as JSON
${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('execution')} - Implementation, testing, deployment context
${chalk.bold('EXAMPLES')}
${chalk.gray('# Initialize spec system:')}
ccw spec init
${chalk.gray('# Load specs for a topic (CLI mode):')}
ccw spec load --dimension specs --context "auth jwt security"
${chalk.gray('# Load exploration-phase specs:')}
ccw spec load --keywords exploration
${chalk.gray('# Load all matching specs:')}
ccw spec load --context "implement user authentication"
${chalk.gray('# Load planning-phase specs with auth topic:')}
ccw spec load --keywords "planning auth"
${chalk.gray('# Load execution-phase specs:')}
ccw spec load --keywords execution
${chalk.gray('# Load specs for a topic (CLI mode):')}
ccw spec load --dimension specs --keywords "auth jwt security"
${chalk.gray('# Use as Claude Code hook (settings.json):')}
ccw spec load --stdin

View File

@@ -24,6 +24,19 @@ import { join, basename, extname, relative } from 'path';
// Types
// ============================================================================
/**
* Spec categories for workflow stage-based loading (used as keywords).
* - exploration: Code exploration, analysis, debugging context
* - planning: Task planning, roadmap, requirements context
* - execution: Implementation, testing, deployment context
*
* Usage: Add these as keywords in spec frontmatter, e.g.:
* keywords: [exploration, auth, security]
*/
export const SPEC_CATEGORIES = ['exploration', 'planning', 'execution'] as const;
export type SpecCategory = typeof SPEC_CATEGORIES[number];
/**
* YAML frontmatter schema for spec MD files.
*/
@@ -45,7 +58,7 @@ export interface SpecIndexEntry {
file: string;
/** Dimension this spec belongs to */
dimension: string;
/** Keywords for matching against user prompts */
/** Keywords for matching against user prompts (may include category markers) */
keywords: string[];
/** Whether this spec is required or optional */
readMode: 'required' | 'optional';
@@ -87,8 +100,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.
* Directory name for spec index cache files (inside .workflow/).
*/
const WORKFLOW_DIR = '.workflow';
const SPEC_INDEX_DIR = '.spec-index';
// ============================================================================
@@ -100,10 +114,10 @@ const SPEC_INDEX_DIR = '.spec-index';
*
* @param projectPath - Project root directory
* @param dimension - The dimension name
* @returns Absolute path to .spec-index/{dimension}.index.json
* @returns Absolute path to .workflow/.spec-index/{dimension}.index.json
*/
export function getIndexPath(projectPath: string, dimension: string): string {
return join(projectPath, SPEC_INDEX_DIR, `${dimension}.index.json`);
return join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR, `${dimension}.index.json`);
}
/**
@@ -188,7 +202,7 @@ export async function buildDimensionIndex(
* @param projectPath - Project root directory
*/
export async function buildAllIndices(projectPath: string): Promise<void> {
const indexDir = join(projectPath, SPEC_INDEX_DIR);
const indexDir = join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR);
// Ensure .spec-index directory exists
if (!existsSync(indexDir)) {
@@ -269,7 +283,7 @@ export async function getDimensionIndex(
// Build fresh and cache
const index = await buildDimensionIndex(projectPath, dimension);
const indexDir = join(projectPath, SPEC_INDEX_DIR);
const indexDir = join(projectPath, WORKFLOW_DIR, SPEC_INDEX_DIR);
if (!existsSync(indexDir)) {
mkdirSync(indexDir, { recursive: true });
}

View File

@@ -22,6 +22,7 @@ import {
SpecIndexEntry,
DimensionIndex,
SPEC_DIMENSIONS,
SPEC_CATEGORIES,
type SpecDimension,
} from './spec-index-builder.js';