mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
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:
@@ -54,14 +54,11 @@ Phase 4: Output Generation
|
|||||||
- Other schemas as specified in prompt
|
- Other schemas as specified in prompt
|
||||||
Read and memorize schema requirements BEFORE any analysis begins (feeds Phase 3 validation).
|
Read and memorize schema requirements BEFORE any analysis begins (feeds Phase 3 validation).
|
||||||
|
|
||||||
3. **Project Context Loading** (from init.md products):
|
3. **Project Context Loading** (from spec system):
|
||||||
- Read `.workflow/project-tech.json` (if exists):
|
- Load exploration specs using: `ccw spec load --keywords exploration`
|
||||||
- Extract: `tech_stack`, `architecture`, `key_components`, `overview`
|
- Extract: `tech_stack`, `architecture`, `key_components`, `overview`
|
||||||
- Usage: Align analysis scope and patterns with actual project technology choices
|
- Usage: Align analysis scope and patterns with actual project technology choices
|
||||||
- Read `.workflow/specs/*.md` (if exists):
|
- If no specs are returned, proceed with fresh analysis (no error).
|
||||||
- Extract: `conventions`, `constraints`, `quality_rules`, `learnings`
|
|
||||||
- Usage: Apply as constraints during pattern analysis, integration point evaluation, and recommendations
|
|
||||||
- If either file does not exist, proceed with fresh analysis (no error).
|
|
||||||
|
|
||||||
4. **Task Keyword Search** (initial file discovery):
|
4. **Task Keyword Search** (initial file discovery):
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -54,9 +54,8 @@ When invoked with `process_docs: true` in input context:
|
|||||||
|
|
||||||
## Input Context
|
## Input Context
|
||||||
|
|
||||||
**Project Context** (read from init.md products at startup):
|
**Project Context** (loaded from spec system at startup):
|
||||||
- `.workflow/project-tech.json` → tech_stack, architecture, key_components
|
- Load specs using: `ccw spec load --keywords "exploration architecture"` → tech_stack, architecture, key_components, conventions, constraints, quality_rules
|
||||||
- `.workflow/specs/*.md` → conventions, constraints, quality_rules
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,14 +77,11 @@ if (file_exists(contextPackagePath)) {
|
|||||||
|
|
||||||
**1.1b Project Context Loading** (MANDATORY):
|
**1.1b Project Context Loading** (MANDATORY):
|
||||||
```javascript
|
```javascript
|
||||||
// Load project-level context (from workflow:init products)
|
// Load project-level context (from spec system)
|
||||||
// These provide foundational constraints for ALL context gathering
|
// These provide foundational constraints for ALL context gathering
|
||||||
const projectTech = file_exists('.workflow/project-tech.json')
|
const projectSpecs = Bash('ccw spec load --keywords "exploration architecture" --stdin');
|
||||||
? JSON.parse(Read('.workflow/project-tech.json')) // tech_stack, architecture_type, key_components, build_system, test_framework
|
const projectTech = projectSpecs?.tech_stack ? projectSpecs : null;
|
||||||
: null;
|
const projectGuidelines = projectSpecs?.coding_conventions ? projectSpecs : null;
|
||||||
const projectGuidelines = file_exists('.workflow/specs/*.md')
|
|
||||||
? JSON.parse(Read('.workflow/specs/*.md')) // coding_conventions, naming_rules, forbidden_patterns, quality_gates
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Usage:
|
// Usage:
|
||||||
// - projectTech → Populate project_context fields (tech_stack, architecture_patterns)
|
// - projectTech → Populate project_context fields (tech_stack, architecture_patterns)
|
||||||
|
|||||||
@@ -35,9 +35,8 @@ Phase 5: Fix & Verification
|
|||||||
|
|
||||||
## Phase 1: Bug Analysis
|
## Phase 1: Bug Analysis
|
||||||
|
|
||||||
**Load Project Context** (from init.md products):
|
**Load Project Context** (from spec system):
|
||||||
- Read `.workflow/project-tech.json` (if exists) for tech stack context
|
- Load exploration specs using: `ccw spec load --keywords exploration` for tech stack context and coding constraints
|
||||||
- Read `.workflow/specs/*.md` (if exists) for coding constraints
|
|
||||||
|
|
||||||
**Session Setup**:
|
**Session Setup**:
|
||||||
```javascript
|
```javascript
|
||||||
|
|||||||
@@ -98,22 +98,18 @@ if (isPopulated) {
|
|||||||
### Step 2: Load Project Context
|
### Step 2: Load Project Context
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const projectTech = JSON.parse(Read('.workflow/project-tech.json'))
|
// Load project context via ccw spec load for planning context
|
||||||
|
const projectContext = Bash('ccw spec load --keywords planning 2>/dev/null || echo "{}"')
|
||||||
|
const specData = JSON.parse(projectContext)
|
||||||
|
|
||||||
// Extract key info for generating smart questions
|
// Extract key info from loaded specs for generating smart questions
|
||||||
const languages = projectTech.technology_analysis?.technology_stack?.languages
|
const languages = specData.overview?.technology_stack?.languages || []
|
||||||
|| projectTech.overview?.technology_stack?.languages || []
|
|
||||||
const primaryLang = languages.find(l => l.primary)?.name || languages[0]?.name || 'Unknown'
|
const primaryLang = languages.find(l => l.primary)?.name || languages[0]?.name || 'Unknown'
|
||||||
const frameworks = projectTech.technology_analysis?.technology_stack?.frameworks
|
const frameworks = specData.overview?.technology_stack?.frameworks || []
|
||||||
|| projectTech.overview?.technology_stack?.frameworks || []
|
const testFrameworks = specData.overview?.technology_stack?.test_frameworks || []
|
||||||
const testFrameworks = projectTech.technology_analysis?.technology_stack?.test_frameworks
|
const archStyle = specData.overview?.architecture?.style || 'Unknown'
|
||||||
|| projectTech.overview?.technology_stack?.test_frameworks || []
|
const archPatterns = specData.overview?.architecture?.patterns || []
|
||||||
const archStyle = projectTech.technology_analysis?.architecture?.style
|
const buildTools = specData.overview?.technology_stack?.build_tools || []
|
||||||
|| projectTech.overview?.architecture?.style || 'Unknown'
|
|
||||||
const archPatterns = projectTech.technology_analysis?.architecture?.patterns
|
|
||||||
|| projectTech.overview?.architecture?.patterns || []
|
|
||||||
const buildTools = projectTech.technology_analysis?.technology_stack?.build_tools
|
|
||||||
|| projectTech.overview?.technology_stack?.build_tools || []
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Multi-Round Interactive Questionnaire
|
### Step 3: Multi-Round Interactive Questionnaire
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ Priority: <issue.priority>
|
|||||||
## MANDATORY FIRST STEPS
|
## MANDATORY FIRST STEPS
|
||||||
1. Run: ccw tool exec get_modules_by_depth '{}'
|
1. Run: ccw tool exec get_modules_by_depth '{}'
|
||||||
2. Execute ACE searches based on issue keywords
|
2. Execute ACE searches based on issue keywords
|
||||||
3. Read: .workflow/project-tech.json (if exists)
|
3. Run: ccw spec load --keywords exploration
|
||||||
|
|
||||||
## Exploration Focus
|
## Exploration Focus
|
||||||
- Identify files directly related to this issue
|
- Identify files directly related to this issue
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ Session: ${sessionFolder}
|
|||||||
## MANDATORY FIRST STEPS
|
## MANDATORY FIRST STEPS
|
||||||
1. Run: ccw tool exec get_modules_by_depth '{}'
|
1. Run: ccw tool exec get_modules_by_depth '{}'
|
||||||
2. Execute searches: ${strategy.searches.map(s => `"${s}"`).join(', ')}
|
2. Execute searches: ${strategy.searches.map(s => `"${s}"`).join(', ')}
|
||||||
3. Read: .workflow/project-tech.json (if exists)
|
3. Run: ccw spec load --keywords exploration
|
||||||
|
|
||||||
## Exploration Focus (${perspective} angle)
|
## Exploration Focus (${perspective} angle)
|
||||||
- **Depth**: ${strategy.depth}
|
- **Depth**: ${strategy.depth}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ Session: <session-folder>
|
|||||||
## MANDATORY FIRST STEPS
|
## MANDATORY FIRST STEPS
|
||||||
1. Run: ccw tool exec get_modules_by_depth '{}'
|
1. Run: ccw tool exec get_modules_by_depth '{}'
|
||||||
2. Execute relevant searches based on topic keywords
|
2. Execute relevant searches based on topic keywords
|
||||||
3. Read: .workflow/project-tech.json (if exists)
|
3. Run: ccw spec load --keywords exploration
|
||||||
|
|
||||||
## Exploration Focus (<perspective> angle)
|
## Exploration Focus (<perspective> angle)
|
||||||
<dimensions map to exploration focus areas>
|
<dimensions map to exploration focus areas>
|
||||||
|
|||||||
@@ -93,10 +93,11 @@ rg "password|token|secret|auth" -g "*.{ts,js,py}"
|
|||||||
rg "eval|exec|innerHTML|dangerouslySetInnerHTML" -g "*.{ts,js,tsx}"
|
rg "eval|exec|innerHTML|dangerouslySetInnerHTML" -g "*.{ts,js,tsx}"
|
||||||
|
|
||||||
# Gemini security analysis
|
# Gemini security analysis
|
||||||
|
ccw spec load --keywords execution
|
||||||
ccw cli -p "
|
ccw cli -p "
|
||||||
PURPOSE: Security audit of completed implementation
|
PURPOSE: Security audit of completed implementation
|
||||||
TASK: Review code for security vulnerabilities, insecure patterns, auth/authz issues
|
TASK: Review code for security vulnerabilities, insecure patterns, auth/authz issues
|
||||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../specs/*.md
|
CONTEXT: @.summaries/IMPL-*.md,../..
|
||||||
EXPECTED: Security findings report with severity levels
|
EXPECTED: Security findings report with severity levels
|
||||||
RULES: Focus on OWASP Top 10, authentication, authorization, data validation, injection risks
|
RULES: Focus on OWASP Top 10, authentication, authorization, data validation, injection risks
|
||||||
" --tool gemini --mode write --cd ${sessionPath}
|
" --tool gemini --mode write --cd ${sessionPath}
|
||||||
@@ -104,10 +105,11 @@ RULES: Focus on OWASP Top 10, authentication, authorization, data validation, in
|
|||||||
|
|
||||||
**Architecture Review** (`architecture`):
|
**Architecture Review** (`architecture`):
|
||||||
```bash
|
```bash
|
||||||
|
ccw spec load --keywords execution
|
||||||
ccw cli -p "
|
ccw cli -p "
|
||||||
PURPOSE: Architecture compliance review
|
PURPOSE: Architecture compliance review
|
||||||
TASK: Evaluate adherence to architectural patterns, identify technical debt, review design decisions
|
TASK: Evaluate adherence to architectural patterns, identify technical debt, review design decisions
|
||||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../specs/*.md
|
CONTEXT: @.summaries/IMPL-*.md,../..
|
||||||
EXPECTED: Architecture assessment with recommendations
|
EXPECTED: Architecture assessment with recommendations
|
||||||
RULES: Check for patterns, separation of concerns, modularity, scalability
|
RULES: Check for patterns, separation of concerns, modularity, scalability
|
||||||
" --tool qwen --mode write --cd ${sessionPath}
|
" --tool qwen --mode write --cd ${sessionPath}
|
||||||
@@ -115,10 +117,11 @@ RULES: Check for patterns, separation of concerns, modularity, scalability
|
|||||||
|
|
||||||
**Quality Review** (`quality`):
|
**Quality Review** (`quality`):
|
||||||
```bash
|
```bash
|
||||||
|
ccw spec load --keywords execution
|
||||||
ccw cli -p "
|
ccw cli -p "
|
||||||
PURPOSE: Code quality and best practices review
|
PURPOSE: Code quality and best practices review
|
||||||
TASK: Assess code readability, maintainability, adherence to best practices
|
TASK: Assess code readability, maintainability, adherence to best practices
|
||||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../specs/*.md
|
CONTEXT: @.summaries/IMPL-*.md,../..
|
||||||
EXPECTED: Quality assessment with improvement suggestions
|
EXPECTED: Quality assessment with improvement suggestions
|
||||||
RULES: Check for code smells, duplication, complexity, naming conventions
|
RULES: Check for code smells, duplication, complexity, naming conventions
|
||||||
" --tool gemini --mode write --cd ${sessionPath}
|
" --tool gemini --mode write --cd ${sessionPath}
|
||||||
@@ -136,10 +139,11 @@ for task_file in ${sessionPath}/.task/*.json; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Cross-check implementation against requirements
|
# Cross-check implementation against requirements
|
||||||
|
ccw spec load --keywords execution
|
||||||
ccw cli -p "
|
ccw cli -p "
|
||||||
PURPOSE: Verify all requirements and acceptance criteria are met
|
PURPOSE: Verify all requirements and acceptance criteria are met
|
||||||
TASK: Cross-check implementation summaries against original requirements
|
TASK: Cross-check implementation summaries against original requirements
|
||||||
CONTEXT: @.task/IMPL-*.json,.summaries/IMPL-*.md,../.. @../../project-tech.json @../../specs/*.md
|
CONTEXT: @.task/IMPL-*.json,.summaries/IMPL-*.md,../..
|
||||||
EXPECTED:
|
EXPECTED:
|
||||||
- Requirements coverage matrix
|
- Requirements coverage matrix
|
||||||
- Acceptance criteria verification
|
- Acceptance criteria verification
|
||||||
|
|||||||
@@ -126,17 +126,11 @@ if (autoYes) {
|
|||||||
After collecting preferences, enhance context and dispatch:
|
After collecting preferences, enhance context and dispatch:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Step 1: Check for project context files
|
// Step 1: Load project context via ccw spec
|
||||||
const hasProjectTech = fileExists('.workflow/project-tech.json')
|
Bash('ccw spec load --keywords planning')
|
||||||
const hasProjectGuidelines = fileExists('.workflow/specs/*.md')
|
|
||||||
|
|
||||||
// Step 2: Log available context
|
// Step 2: Log available context
|
||||||
if (hasProjectTech) {
|
console.log('Project context loaded via: ccw spec load --keywords planning')
|
||||||
console.log('Project tech context available: .workflow/project-tech.json')
|
|
||||||
}
|
|
||||||
if (hasProjectGuidelines) {
|
|
||||||
console.log('Project guidelines available: .workflow/specs/*.md')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Dispatch to phase (workflowPreferences available as context)
|
// Step 3: Dispatch to phase (workflowPreferences available as context)
|
||||||
if (mode === 'plan') {
|
if (mode === 'plan') {
|
||||||
|
|||||||
@@ -494,9 +494,9 @@ Generate implementation plan and write plan.json.
|
|||||||
## Output Schema Reference
|
## Output Schema Reference
|
||||||
Execute: cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json (get schema reference before generating plan)
|
Execute: cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json (get schema reference before generating plan)
|
||||||
|
|
||||||
## Project Context (MANDATORY - Read Both Files)
|
## Project Context (MANDATORY - Load via ccw spec)
|
||||||
1. Read: .workflow/project-tech.json (technology stack, architecture, key components)
|
Execute: ccw spec load --keywords planning
|
||||||
2. Read: .workflow/specs/*.md (user-defined constraints and conventions)
|
This loads technology stack, architecture, key components, and user-defined constraints/conventions.
|
||||||
|
|
||||||
**CRITICAL**: All generated tasks MUST comply with constraints in specs/*.md
|
**CRITICAL**: All generated tasks MUST comply with constraints in specs/*.md
|
||||||
|
|
||||||
|
|||||||
@@ -485,7 +485,8 @@ ${(t.test?.success_metrics || []).length > 0 ? `\n**Success metrics**: ${t.test.
|
|||||||
context.push(`### Artifacts\nPlan: ${executionContext.session.artifacts.plan}`)
|
context.push(`### Artifacts\nPlan: ${executionContext.session.artifacts.plan}`)
|
||||||
}
|
}
|
||||||
// Project guidelines (user-defined constraints from /workflow:session:solidify)
|
// Project guidelines (user-defined constraints from /workflow:session:solidify)
|
||||||
context.push(`### Project Guidelines\n@.workflow/specs/*.md`)
|
// Loaded via: ccw spec load --keywords planning
|
||||||
|
context.push(`### Project Guidelines\n(Loaded via ccw spec load --keywords planning)`)
|
||||||
if (context.length > 0) sections.push(`## Context\n${context.join('\n\n')}`)
|
if (context.length > 0) sections.push(`## Context\n${context.join('\n\n')}`)
|
||||||
|
|
||||||
sections.push(`Complete each task according to its "Done when" checklist.`)
|
sections.push(`Complete each task according to its "Done when" checklist.`)
|
||||||
|
|||||||
@@ -100,17 +100,11 @@ if (autoYes) {
|
|||||||
After collecting preferences, enhance context and dispatch:
|
After collecting preferences, enhance context and dispatch:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Step 1: Check for project context files
|
// Step 1: Load project context via ccw spec
|
||||||
const hasProjectTech = fileExists('.workflow/project-tech.json')
|
Bash('ccw spec load --keywords planning')
|
||||||
const hasProjectGuidelines = fileExists('.workflow/specs/*.md')
|
|
||||||
|
|
||||||
// Step 2: Log available context
|
// Step 2: Log available context
|
||||||
if (hasProjectTech) {
|
console.log('Project context loaded via: ccw spec load --keywords planning')
|
||||||
console.log('Project tech context available: .workflow/project-tech.json')
|
|
||||||
}
|
|
||||||
if (hasProjectGuidelines) {
|
|
||||||
console.log('Project guidelines available: .workflow/specs/*.md')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Dispatch to phase (workflowPreferences available as context)
|
// Step 3: Dispatch to phase (workflowPreferences available as context)
|
||||||
if (mode === 'plan') {
|
if (mode === 'plan') {
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ This is the PRIMARY context source - all subsequent analysis must align with use
|
|||||||
Execute complete context-search-agent workflow (Phase 1-3) for implementation planning.
|
Execute complete context-search-agent workflow (Phase 1-3) for implementation planning.
|
||||||
|
|
||||||
Key emphasis:
|
Key emphasis:
|
||||||
- Load project-tech.json and specs/*.md FIRST (per your spec Phase 1.1b)
|
- Run: ccw spec load --keywords exploration FIRST (per your spec Phase 1.1b)
|
||||||
- Synthesize exploration results with project context
|
- Synthesize exploration results with project context
|
||||||
- Generate prioritized_context with user_intent alignment
|
- Generate prioritized_context with user_intent alignment
|
||||||
- Apply specs/*.md constraints during conflict detection
|
- Apply specs/*.md constraints during conflict detection
|
||||||
|
|||||||
@@ -170,21 +170,21 @@ Output:
|
|||||||
Session ID: ${sessionId}
|
Session ID: ${sessionId}
|
||||||
MCP Capabilities: {exa_code, exa_web, code_index}
|
MCP Capabilities: {exa_code, exa_web, code_index}
|
||||||
|
|
||||||
## PROJECT CONTEXT (MANDATORY - load before planning-notes)
|
## PROJECT CONTEXT (MANDATORY - load via ccw spec)
|
||||||
These files provide project-level constraints that apply to ALL tasks:
|
Execute: ccw spec load --keywords planning
|
||||||
|
|
||||||
1. **.workflow/project-tech.json** (auto-generated tech analysis)
|
This loads:
|
||||||
- Contains: tech_stack, architecture_type, key_components, build_system, test_framework
|
- Technology stack, architecture, key components, build system, test framework
|
||||||
- Usage: Populate plan.json shared_context, align task tech choices, set correct test commands
|
- User-maintained rules and constraints (coding_conventions, naming_rules, forbidden_patterns, quality_gates)
|
||||||
- If missing: Fall back to context-package.project_context
|
|
||||||
|
|
||||||
2. **.workflow/specs/*.md** (user-maintained rules and constraints)
|
Usage:
|
||||||
- Contains: coding_conventions, naming_rules, forbidden_patterns, quality_gates, custom_constraints
|
- Populate plan.json shared_context, align task tech choices, set correct test commands
|
||||||
- Usage: Apply as HARD CONSTRAINTS on all generated tasks — task implementation steps,
|
- Apply as HARD CONSTRAINTS on all generated tasks — task implementation steps,
|
||||||
acceptance criteria, and convergence.verification MUST respect these guidelines
|
acceptance criteria, and convergence.verification MUST respect these guidelines
|
||||||
- If empty/missing: No additional constraints (proceed normally)
|
|
||||||
|
|
||||||
Loading order: project-tech.json → specs/*.md → planning-notes.md → context-package.json
|
If spec load returns empty: Proceed normally with context-package.project_context
|
||||||
|
|
||||||
|
Loading order: ccw spec load → planning-notes.md → context-package.json
|
||||||
|
|
||||||
## USER CONFIGURATION (from Step 4.0)
|
## USER CONFIGURATION (from Step 4.0)
|
||||||
Execution Method: ${userConfig.executionMethod} // agent|hybrid|cli
|
Execution Method: ${userConfig.executionMethod} // agent|hybrid|cli
|
||||||
|
|||||||
@@ -221,8 +221,7 @@ Execute complete context-search-agent workflow for TDD implementation planning:
|
|||||||
|
|
||||||
### Phase 1: Initialization & Pre-Analysis
|
### Phase 1: Initialization & Pre-Analysis
|
||||||
1. **Project State Loading**:
|
1. **Project State Loading**:
|
||||||
- Read and parse .workflow/project-tech.json. Use its overview section as the foundational project_context.
|
- Run: \`ccw spec load --keywords execution\` to load project context, tech stack, and guidelines.
|
||||||
- Read and parse .workflow/specs/*.md. Load conventions, constraints, and learnings into a project_guidelines section.
|
|
||||||
- If files don't exist, proceed with fresh analysis.
|
- If files don't exist, proceed with fresh analysis.
|
||||||
2. **Detection**: Check for existing context-package (early exit if valid)
|
2. **Detection**: Check for existing context-package (early exit if valid)
|
||||||
3. **Foundation**: Initialize CodexLens, get project structure, load docs
|
3. **Foundation**: Initialize CodexLens, get project structure, load docs
|
||||||
|
|||||||
@@ -231,18 +231,14 @@ MCP Capabilities: {exa_code, exa_web, code_index}
|
|||||||
## PROJECT CONTEXT (MANDATORY - load before planning-notes)
|
## PROJECT CONTEXT (MANDATORY - load before planning-notes)
|
||||||
These files provide project-level constraints that apply to ALL tasks:
|
These files provide project-level constraints that apply to ALL tasks:
|
||||||
|
|
||||||
1. **.workflow/project-tech.json** (auto-generated tech analysis)
|
1. **ccw spec load --keywords execution** (project specs and tech analysis)
|
||||||
- Contains: tech_stack, architecture_type, key_components, build_system, test_framework
|
- Contains: tech_stack, architecture_type, key_components, build_system, test_framework, coding_conventions, naming_rules, forbidden_patterns, quality_gates, custom_constraints
|
||||||
- Usage: Populate plan.json shared_context, align task tech choices, set correct test commands
|
- Usage: Populate plan.json shared_context, align task tech choices, set correct test commands
|
||||||
- If missing: Fall back to context-package.project_context
|
- Apply as HARD CONSTRAINTS on all generated tasks — task implementation steps,
|
||||||
|
|
||||||
2. **.workflow/specs/*.md** (user-maintained rules and constraints)
|
|
||||||
- Contains: coding_conventions, naming_rules, forbidden_patterns, quality_gates, custom_constraints
|
|
||||||
- Usage: Apply as HARD CONSTRAINTS on all generated tasks — task implementation steps,
|
|
||||||
acceptance criteria, and convergence.verification MUST respect these guidelines
|
acceptance criteria, and convergence.verification MUST respect these guidelines
|
||||||
- If empty/missing: No additional constraints (proceed normally)
|
- If empty/missing: No additional constraints (proceed normally)
|
||||||
|
|
||||||
Loading order: project-tech.json → specs/*.md → planning-notes.md → context-package.json
|
Loading order: \`ccw spec load --keywords execution\` → planning-notes.md → context-package.json
|
||||||
|
|
||||||
## USER CONFIGURATION (from Phase 0)
|
## USER CONFIGURATION (from Phase 0)
|
||||||
Execution Method: ${userConfig.executionMethod} // agent|hybrid|cli
|
Execution Method: ${userConfig.executionMethod} // agent|hybrid|cli
|
||||||
|
|||||||
@@ -345,8 +345,7 @@ Execute complete context-search-agent workflow for implementation planning:
|
|||||||
|
|
||||||
### Phase 1: Initialization & Pre-Analysis
|
### Phase 1: Initialization & Pre-Analysis
|
||||||
1. **Project State Loading**:
|
1. **Project State Loading**:
|
||||||
- Read and parse .workflow/project-tech.json. Use its overview section as the foundational project_context.
|
- Run: \`ccw spec load --keywords execution\` to load project context, tech stack, and guidelines.
|
||||||
- Read and parse .workflow/specs/*.md. Load conventions, constraints, and learnings into a project_guidelines section.
|
|
||||||
- If files don't exist, proceed with fresh analysis.
|
- If files don't exist, proceed with fresh analysis.
|
||||||
2. **Detection**: Check for existing context-package (early exit if valid)
|
2. **Detection**: Check for existing context-package (early exit if valid)
|
||||||
3. **Foundation**: Initialize CodexLens, get project structure, load docs
|
3. **Foundation**: Initialize CodexLens, get project structure, load docs
|
||||||
|
|||||||
@@ -244,8 +244,7 @@ Task(
|
|||||||
${selectedStrategy} - ${strategyDescription}
|
${selectedStrategy} - ${strategyDescription}
|
||||||
|
|
||||||
## PROJECT CONTEXT (MANDATORY)
|
## PROJECT CONTEXT (MANDATORY)
|
||||||
1. Read: .workflow/project-tech.json (tech stack, test framework, build system)
|
1. Run: \`ccw spec load --keywords execution\` (tech stack, test framework, build system, constraints)
|
||||||
2. Read: .workflow/specs/*.md (constraints — apply as HARD CONSTRAINTS on fixes)
|
|
||||||
|
|
||||||
## MANDATORY FIRST STEPS
|
## MANDATORY FIRST STEPS
|
||||||
1. Read test results: ${session.test_results_path}
|
1. Read test results: ${session.test_results_path}
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ Check these items. Report results as a checklist.
|
|||||||
|
|
||||||
### 1.2 Strongly Recommended (warn if missing)
|
### 1.2 Strongly Recommended (warn if missing)
|
||||||
|
|
||||||
- **project-tech.json**: Check `{projectRoot}/.workflow/project-tech.json`
|
- **Project specs**: Run `ccw spec load --keywords execution` to load project context
|
||||||
- If missing: Read `package.json` / `tsconfig.json` / `pyproject.toml` and generate a minimal version. Ask user: "检测到项目使用 [tech stack], 是否正确?需要补充什么?"
|
- If spec system unavailable: Read `package.json` / `tsconfig.json` / `pyproject.toml` and generate a minimal version. Ask user: "检测到项目使用 [tech stack], 是否正确?需要补充什么?"
|
||||||
- **specs/*.md**: Check `{projectRoot}/.workflow/specs/*.md`
|
|
||||||
- If missing: Scan for `.eslintrc`, `.prettierrc`, `ruff.toml` etc. Ask user: "未找到 specs/*.md, 是否有特定的编码规范需要遵循?"
|
|
||||||
- **Test framework**: Detect from config files (jest.config, vitest.config, pytest.ini, etc.)
|
- **Test framework**: Detect from config files (jest.config, vitest.config, pytest.ini, etc.)
|
||||||
- If missing: Ask user: "未检测到测试框架配置,请指定测试命令(如 `npm test`, `pytest`),或输入 'skip' 跳过测试验证"
|
- If missing: Ask user: "未检测到测试框架配置,请指定测试命令(如 `npm test`, `pytest`),或输入 'skip' 跳过测试验证"
|
||||||
|
|
||||||
@@ -38,8 +36,8 @@ Print formatted checklist:
|
|||||||
✓ 项目根目录: D:\myproject
|
✓ 项目根目录: D:\myproject
|
||||||
✓ 工作空间: .workflow/.cycle/ 就绪
|
✓ 工作空间: .workflow/.cycle/ 就绪
|
||||||
⚠ Git: 3 个未提交变更
|
⚠ Git: 3 个未提交变更
|
||||||
✓ project-tech.json: 已检测 (Express + TypeORM + PostgreSQL)
|
✓ Project specs: 已加载 (ccw spec load --keywords execution)
|
||||||
⚠ specs/*.md: 未找到 (已跳过)
|
⚠ specs: 未找到 (已跳过)
|
||||||
✓ 测试框架: jest (npm test)
|
✓ 测试框架: jest (npm test)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -166,13 +164,13 @@ Read the user's `$TASK` and score each dimension:
|
|||||||
> 请选择适用的或添加自定义约束"
|
> 请选择适用的或添加自定义约束"
|
||||||
|
|
||||||
**上下文不足 (score 0-1)**:
|
**上下文不足 (score 0-1)**:
|
||||||
> "我从项目中检测到: [tech stack from project-tech.json]。还有其他需要知道的技术细节吗?例如现有的认证机制、相关的工具库、数据模型等"
|
> "我从项目中检测到: [tech stack from loaded specs]。还有其他需要知道的技术细节吗?例如现有的认证机制、相关的工具库、数据模型等"
|
||||||
|
|
||||||
### 2.4 Auto-Enhancement
|
### 2.4 Auto-Enhancement
|
||||||
|
|
||||||
For dimensions still at score 1 after Q&A, auto-enhance from codebase:
|
For dimensions still at score 1 after Q&A, auto-enhance from codebase:
|
||||||
- **Scope**: Use `Glob` and `Grep` to find related files, list them
|
- **Scope**: Use `Glob` and `Grep` to find related files, list them
|
||||||
- **Context**: Read `project-tech.json` and key config files
|
- **Context**: Run `ccw spec load --keywords execution` to load project context
|
||||||
- **Constraints**: Infer from `specs/*.md` and existing patterns
|
- **Constraints**: Infer from `specs/*.md` and existing patterns
|
||||||
|
|
||||||
### 2.5 Assemble Refined Task
|
### 2.5 Assemble Refined Task
|
||||||
|
|||||||
@@ -1,464 +0,0 @@
|
|||||||
---
|
|
||||||
description: "Interactive pre-flight checklist for ccw-loop. Discovers .task/*.json from collaborative-plan-with-file, analyze-with-file, brainstorm-to-cycle sessions; validates, transforms to ccw-loop task format, writes prep-package.json + .task/*.json, then launches the loop."
|
|
||||||
argument-hint: '[SOURCE="<path-to-.task/-dir-or-session-folder>"] [MAX_ITER=10]'
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pre-Flight Checklist for CCW Loop
|
|
||||||
|
|
||||||
You are an interactive preparation assistant. Your job is to discover and consume task artifacts from upstream planning/analysis/brainstorm skills, validate them, transform into ccw-loop's task format, and launch an **unattended** development loop. Follow each step sequentially. **Ask the user questions when information is missing.**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 1: Source Discovery
|
|
||||||
|
|
||||||
### 1.1 Auto-Detect Available Sessions
|
|
||||||
|
|
||||||
Scan for upstream artifacts from the three supported source skills:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const projectRoot = Bash('git rev-parse --show-toplevel 2>/dev/null || pwd').trim()
|
|
||||||
|
|
||||||
// Source 1: collaborative-plan-with-file
|
|
||||||
const cplanSessions = Glob(`${projectRoot}/.workflow/.planning/CPLAN-*/.task/*.json`)
|
|
||||||
.map(p => ({
|
|
||||||
path: p.replace(/\/\.task\/[^/]+$/, '/.task'),
|
|
||||||
source: 'collaborative-plan-with-file',
|
|
||||||
type: 'task-dir',
|
|
||||||
session: p.match(/CPLAN-[^/]+/)?.[0],
|
|
||||||
mtime: fs.statSync(p).mtime
|
|
||||||
}))
|
|
||||||
// Deduplicate by session
|
|
||||||
.filter((v, i, a) => a.findIndex(x => x.session === v.session) === i)
|
|
||||||
|
|
||||||
// Source 2: analyze-with-file
|
|
||||||
const anlSessions = Glob(`${projectRoot}/.workflow/.analysis/ANL-*/.task/*.json`)
|
|
||||||
.map(p => ({
|
|
||||||
path: p.replace(/\/\.task\/[^/]+$/, '/.task'),
|
|
||||||
source: 'analyze-with-file',
|
|
||||||
type: 'task-dir',
|
|
||||||
session: p.match(/ANL-[^/]+/)?.[0],
|
|
||||||
mtime: fs.statSync(p).mtime
|
|
||||||
}))
|
|
||||||
.filter((v, i, a) => a.findIndex(x => x.session === v.session) === i)
|
|
||||||
|
|
||||||
// Source 3: brainstorm-to-cycle
|
|
||||||
const bsSessions = Glob(`${projectRoot}/.workflow/.brainstorm/*/cycle-task.md`)
|
|
||||||
.map(p => ({
|
|
||||||
path: p,
|
|
||||||
source: 'brainstorm-to-cycle',
|
|
||||||
type: 'markdown',
|
|
||||||
session: p.match(/\.brainstorm\/([^/]+)/)?.[1],
|
|
||||||
mtime: fs.statSync(p).mtime
|
|
||||||
}))
|
|
||||||
|
|
||||||
const allSources = [...cplanSessions, ...anlSessions, ...bsSessions]
|
|
||||||
.sort((a, b) => b.mtime - a.mtime) // Most recent first
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.2 Display Discovered Sources
|
|
||||||
|
|
||||||
```
|
|
||||||
可用的上游任务源
|
|
||||||
════════════════
|
|
||||||
|
|
||||||
collaborative-plan-with-file:
|
|
||||||
1. CPLAN-auth-redesign-20260208 .task/ (5 tasks, 2h ago)
|
|
||||||
2. CPLAN-api-cleanup-20260205 .task/ (3 days ago)
|
|
||||||
|
|
||||||
analyze-with-file:
|
|
||||||
3. ANL-perf-audit-20260207 .task/ (8 tasks, 1d ago)
|
|
||||||
|
|
||||||
brainstorm-to-cycle:
|
|
||||||
4. BS-notification-system cycle-task.md (1d ago)
|
|
||||||
|
|
||||||
手动输入:
|
|
||||||
5. 自定义路径 (输入 .task/ 目录路径或任务描述)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 User Selection
|
|
||||||
|
|
||||||
Ask the user to select a source:
|
|
||||||
|
|
||||||
> "请选择任务来源(输入编号),或输入 .task/ 目录的完整路径:
|
|
||||||
> 也可以输入 'manual' 手动输入任务描述(不使用上游任务文件)"
|
|
||||||
|
|
||||||
**If `$SOURCE` argument provided**, skip discovery and use directly:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
if (options.SOURCE) {
|
|
||||||
// Validate path exists
|
|
||||||
if (!fs.existsSync(options.SOURCE)) {
|
|
||||||
console.error(`Path not found: ${options.SOURCE}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
selectedSource = {
|
|
||||||
path: options.SOURCE,
|
|
||||||
source: inferSource(options.SOURCE),
|
|
||||||
type: fs.statSync(options.SOURCE).isDirectory() ? 'task-dir' : 'markdown'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 2: Source Validation & Task Loading
|
|
||||||
|
|
||||||
### 2.1 For .task/ Sources (collaborative-plan / analyze-with-file)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function validateAndLoadTaskDir(taskDirPath) {
|
|
||||||
const taskFiles = Glob(`${taskDirPath}/*.json`).sort()
|
|
||||||
const tasks = []
|
|
||||||
const errors = []
|
|
||||||
|
|
||||||
for (let i = 0; i < taskFiles.length; i++) {
|
|
||||||
try {
|
|
||||||
const content = Read(taskFiles[i])
|
|
||||||
const task = JSON.parse(content)
|
|
||||||
|
|
||||||
// Required fields check (task-schema.json: id, title, description, depends_on, convergence)
|
|
||||||
const requiredFields = ['id', 'title', 'description']
|
|
||||||
const missing = requiredFields.filter(f => !task[f])
|
|
||||||
if (missing.length > 0) {
|
|
||||||
errors.push(`${taskFiles[i]}: missing fields: ${missing.join(', ')}`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate task structure
|
|
||||||
if (task.id && task.title && task.description) {
|
|
||||||
tasks.push(task)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
errors.push(`${taskFiles[i]}: invalid JSON: ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { tasks, errors, total_files: taskFiles.length }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Display validation results:
|
|
||||||
|
|
||||||
```
|
|
||||||
JSONL 验证
|
|
||||||
══════════
|
|
||||||
目录: .workflow/.planning/CPLAN-auth-redesign-20260208/.task/
|
|
||||||
来源: collaborative-plan-with-file
|
|
||||||
|
|
||||||
✓ 5/5 任务文件解析成功
|
|
||||||
✓ 必需字段完整 (id, title, description)
|
|
||||||
✓ 3 个任务含收敛标准 (convergence)
|
|
||||||
⚠ 2 个任务缺少收敛标准 (将使用默认)
|
|
||||||
|
|
||||||
任务列表:
|
|
||||||
TASK-001 [high] Implement JWT token service (feature, 3 files)
|
|
||||||
TASK-002 [high] Add OAuth2 Google strategy (feature, 2 files)
|
|
||||||
TASK-003 [medium] Create user session middleware (feature, 4 files)
|
|
||||||
TASK-004 [low] Add rate limiting to auth endpoints (enhancement, 2 files)
|
|
||||||
TASK-005 [low] Write integration tests (testing, 5 files)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 For Markdown Sources (brainstorm-to-cycle)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function loadBrainstormTask(mdPath) {
|
|
||||||
const content = Read(mdPath)
|
|
||||||
|
|
||||||
// Extract enriched task description from cycle-task.md
|
|
||||||
// Format: # Generated Task \n\n **Idea**: ... \n\n --- \n\n {enrichedTask}
|
|
||||||
const taskMatch = content.match(/---\s*\n([\s\S]+)$/)
|
|
||||||
const enrichedTask = taskMatch ? taskMatch[1].trim() : content
|
|
||||||
|
|
||||||
// Parse into a single composite task
|
|
||||||
return {
|
|
||||||
tasks: [{
|
|
||||||
id: 'TASK-001',
|
|
||||||
title: extractTitle(content),
|
|
||||||
description: enrichedTask,
|
|
||||||
type: 'feature',
|
|
||||||
priority: 'high',
|
|
||||||
effort: 'large',
|
|
||||||
source: { tool: 'brainstorm-to-cycle', path: mdPath }
|
|
||||||
}],
|
|
||||||
errors: [],
|
|
||||||
is_composite: true // Single large task from brainstorm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Display:
|
|
||||||
|
|
||||||
```
|
|
||||||
Brainstorm 任务加载
|
|
||||||
══════════════════
|
|
||||||
文件: .workflow/.brainstorm/notification-system/cycle-task.md
|
|
||||||
来源: brainstorm-to-cycle
|
|
||||||
|
|
||||||
ℹ 脑暴输出为复合任务描述(非结构化 JSONL)
|
|
||||||
标题: Build real-time notification system
|
|
||||||
类型: feature (composite)
|
|
||||||
|
|
||||||
是否需要将其拆分为多个子任务?(Y/n)
|
|
||||||
```
|
|
||||||
|
|
||||||
If user selects **Y** (split), analyze the task description and generate sub-tasks:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Analyze and decompose the composite task into 3-7 sub-tasks
|
|
||||||
// Use mcp__ace-tool__search_context to find relevant patterns
|
|
||||||
// Generate structured tasks with convergence criteria
|
|
||||||
```
|
|
||||||
|
|
||||||
If user selects **n** (keep as single), use as-is.
|
|
||||||
|
|
||||||
### 2.3 Validation Gate
|
|
||||||
|
|
||||||
If validation has errors:
|
|
||||||
|
|
||||||
```
|
|
||||||
⚠ 验证发现 {N} 个问题:
|
|
||||||
Line 3: missing fields: description
|
|
||||||
Line 7: invalid JSON
|
|
||||||
|
|
||||||
选项:
|
|
||||||
1. 跳过有问题的行,继续 ({valid_count} 个有效任务)
|
|
||||||
2. 取消,手动修复后重试
|
|
||||||
```
|
|
||||||
|
|
||||||
**Block if 0 valid tasks.** Warn and continue if some tasks invalid.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 3: Task Transformation
|
|
||||||
|
|
||||||
Transform unified task JSON files -> ccw-loop `develop.tasks[]` format.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function transformToCcwLoopTasks(sourceTasks) {
|
|
||||||
const now = getUtc8ISOString()
|
|
||||||
|
|
||||||
return sourceTasks.map((task, index) => ({
|
|
||||||
// Core fields (ccw-loop native)
|
|
||||||
id: task.id || `task-${String(index + 1).padStart(3, '0')}`,
|
|
||||||
description: task.title
|
|
||||||
? `${task.title}: ${task.description}`
|
|
||||||
: task.description,
|
|
||||||
tool: inferTool(task), // 'gemini' | 'qwen' | 'codex'
|
|
||||||
mode: 'write',
|
|
||||||
status: 'pending',
|
|
||||||
priority: mapPriority(task.priority), // 1 (high) | 2 (medium) | 3 (low)
|
|
||||||
files_changed: (task.files || []).map(f => f.path || f),
|
|
||||||
created_at: now,
|
|
||||||
completed_at: null,
|
|
||||||
|
|
||||||
// Extended fields (preserved from source for agent reference)
|
|
||||||
_source: task.source || { tool: 'manual' },
|
|
||||||
_convergence: task.convergence || null,
|
|
||||||
_type: task.type || 'feature',
|
|
||||||
_effort: task.effort || 'medium',
|
|
||||||
_depends_on: task.depends_on || []
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function inferTool(task) {
|
|
||||||
// Default to gemini for write tasks
|
|
||||||
return 'gemini'
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapPriority(priority) {
|
|
||||||
switch (priority) {
|
|
||||||
case 'high': case 'critical': return 1
|
|
||||||
case 'medium': return 2
|
|
||||||
case 'low': return 3
|
|
||||||
default: return 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Display transformed tasks:
|
|
||||||
|
|
||||||
```
|
|
||||||
任务转换
|
|
||||||
════════
|
|
||||||
源格式: .task/*.json (collaborative-plan-with-file)
|
|
||||||
目标格式: ccw-loop develop.tasks
|
|
||||||
|
|
||||||
task-001 [P1] Implement JWT token service: Create JWT service... gemini/write pending
|
|
||||||
task-002 [P1] Add OAuth2 Google strategy: Implement passport... gemini/write pending
|
|
||||||
task-003 [P2] Create user session middleware: Add Express... gemini/write pending
|
|
||||||
task-004 [P3] Add rate limiting to auth endpoints: Implement... gemini/write pending
|
|
||||||
task-005 [P3] Write integration tests: Create test suite... gemini/write pending
|
|
||||||
|
|
||||||
共 5 个任务 (2 high, 1 medium, 2 low)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.1 Task Reordering (Optional)
|
|
||||||
|
|
||||||
Ask: "是否需要调整任务顺序或移除某些任务?(输入编号排列如 '1,3,2,5' 或回车保持当前顺序)"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 4: Auto-Loop Configuration
|
|
||||||
|
|
||||||
### 4.1 Present Defaults
|
|
||||||
|
|
||||||
```
|
|
||||||
自动循环配置
|
|
||||||
════════════
|
|
||||||
模式: 全自动 (develop → debug → validate → complete)
|
|
||||||
最大迭代: $MAX_ITER (默认 10)
|
|
||||||
超时: 10 分钟/action
|
|
||||||
|
|
||||||
收敛标准 (从源任务汇总):
|
|
||||||
${tasksWithConvergence} 个任务含收敛标准 → 自动验证
|
|
||||||
${tasksWithoutConvergence} 个任务无收敛标准 → 使用默认 (测试通过)
|
|
||||||
|
|
||||||
需要调整参数吗?(直接回车使用默认值)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 Customization (if requested)
|
|
||||||
|
|
||||||
> "请选择要调整的项目:
|
|
||||||
> 1. 最大迭代次数 (当前: 10)
|
|
||||||
> 2. 每个 action 超时 (当前: 10 分钟)
|
|
||||||
> 3. 全部使用默认值"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 5: Final Confirmation
|
|
||||||
|
|
||||||
```
|
|
||||||
══════════════════════════════════════════════
|
|
||||||
Pre-Flight 检查完成
|
|
||||||
══════════════════════════════════════════════
|
|
||||||
|
|
||||||
来源: collaborative-plan-with-file (CPLAN-auth-redesign-20260208)
|
|
||||||
任务数: 5 个 (2 high, 1 medium, 2 low)
|
|
||||||
验证: ✓ 5/5 任务格式正确
|
|
||||||
收敛: 3/5 任务含收敛标准
|
|
||||||
自动模式: ON (最多 10 次迭代)
|
|
||||||
|
|
||||||
任务摘要:
|
|
||||||
1. [P1] Implement JWT token service
|
|
||||||
2. [P1] Add OAuth2 Google strategy
|
|
||||||
3. [P2] Create user session middleware
|
|
||||||
4. [P3] Add rate limiting to auth endpoints
|
|
||||||
5. [P3] Write integration tests
|
|
||||||
|
|
||||||
══════════════════════════════════════════════
|
|
||||||
```
|
|
||||||
|
|
||||||
Ask: "确认启动?(Y/n)"
|
|
||||||
- If **Y** → proceed to Step 6
|
|
||||||
- If **n** → ask which part to revise
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 6: Write Artifacts
|
|
||||||
|
|
||||||
### 6.1 Write prep-package.json
|
|
||||||
|
|
||||||
Write to `{projectRoot}/.workflow/.loop/prep-package.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": "1.0.0",
|
|
||||||
"generated_at": "{ISO8601_UTC+8}",
|
|
||||||
"prep_status": "ready",
|
|
||||||
"target_skill": "ccw-loop",
|
|
||||||
|
|
||||||
"environment": {
|
|
||||||
"project_root": "{projectRoot}",
|
|
||||||
"tech_stack": "{detected tech stack}",
|
|
||||||
"test_framework": "{detected test framework}"
|
|
||||||
},
|
|
||||||
|
|
||||||
"source": {
|
|
||||||
"tool": "collaborative-plan-with-file",
|
|
||||||
"session_id": "CPLAN-auth-redesign-20260208",
|
|
||||||
"task_dir": "{projectRoot}/.workflow/.planning/CPLAN-auth-redesign-20260208/.task",
|
|
||||||
"task_count": 5,
|
|
||||||
"tasks_with_convergence": 3
|
|
||||||
},
|
|
||||||
|
|
||||||
"tasks": {
|
|
||||||
"total": 5,
|
|
||||||
"by_priority": { "high": 2, "medium": 1, "low": 2 },
|
|
||||||
"by_type": { "feature": 3, "enhancement": 1, "testing": 1 }
|
|
||||||
},
|
|
||||||
|
|
||||||
"auto_loop": {
|
|
||||||
"enabled": true,
|
|
||||||
"no_confirmation": true,
|
|
||||||
"max_iterations": 10,
|
|
||||||
"timeout_per_action_ms": 600000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.2 Write .task/*.json
|
|
||||||
|
|
||||||
Write transformed tasks to `{projectRoot}/.workflow/.loop/.task/` directory (one file per task, following task-schema.json):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const taskDir = `${projectRoot}/.workflow/.loop/.task`
|
|
||||||
Bash(`mkdir -p ${taskDir}`)
|
|
||||||
|
|
||||||
for (const task of transformedTasks) {
|
|
||||||
const fileName = `TASK-${task.id.replace(/^task-/, '')}.json`
|
|
||||||
Write(`${taskDir}/${fileName}`, JSON.stringify(task, null, 2))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Confirm:
|
|
||||||
|
|
||||||
```
|
|
||||||
ok prep-package.json -> .workflow/.loop/prep-package.json
|
|
||||||
ok .task/ directory -> .workflow/.loop/.task/ (5 task files)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 7: Launch Loop
|
|
||||||
|
|
||||||
Invoke the skill:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ccw-loop --auto TASK="Execute tasks from {source.tool} session {source.session_id}"
|
|
||||||
```
|
|
||||||
|
|
||||||
其中:
|
|
||||||
- `$ccw-loop` — 展开为 skill 调用
|
|
||||||
- `--auto` — 启用全自动模式
|
|
||||||
- Skill 端会检测 `prep-package.json` 并加载 `.task/*.json`
|
|
||||||
|
|
||||||
**Skill 端会做以下检查**(见 Phase 1 Step 1.1):
|
|
||||||
1. 检测 `prep-package.json` 是否存在
|
|
||||||
2. 验证 `prep_status === "ready"`
|
|
||||||
3. 验证 `target_skill === "ccw-loop"`
|
|
||||||
4. 校验 `project_root` 与当前项目一致
|
|
||||||
5. 校验文件时效(24h 内生成)
|
|
||||||
6. 验证 `.task/` 目录存在且含有效任务文件
|
|
||||||
7. 全部通过 -> 加载预构建任务列表;任一失败 -> 回退到默认 INIT 行为
|
|
||||||
|
|
||||||
Print:
|
|
||||||
|
|
||||||
```
|
|
||||||
启动 ccw-loop (自动模式)...
|
|
||||||
prep-package.json → Phase 1 自动加载并校验
|
|
||||||
.task/*.json → 5 个预构建任务加载到 develop.tasks
|
|
||||||
循环: develop → validate → complete (最多 10 次迭代)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| 情况 | 处理 |
|
|
||||||
|------|------|
|
|
||||||
| 无可用上游会话 | 提示用户先运行 collaborative-plan / analyze-with-file / brainstorm,或选择手动输入 |
|
|
||||||
| JSONL 格式全部无效 | 报告错误,**不启动 loop** |
|
|
||||||
| JSONL 部分无效 | 警告无效文件,用有效任务继续 |
|
|
||||||
| brainstorm cycle-task.md 为空 | 报告错误,建议完成 brainstorm 流程 |
|
|
||||||
| 用户取消确认 | 保存 prep-package.json (prep_status="cancelled"),提示可修改后重新运行 |
|
|
||||||
| Skill 端 prep-package 校验失败 | Skill 打印警告,回退到无 prep 的默认 INIT 行为(不阻塞执行) |
|
|
||||||
@@ -21,10 +21,8 @@ Check these items. Report results as a checklist.
|
|||||||
|
|
||||||
### 1.2 Strongly Recommended (warn if missing)
|
### 1.2 Strongly Recommended (warn if missing)
|
||||||
|
|
||||||
- **project-tech.json**: Check `{projectRoot}/.workflow/project-tech.json`
|
- **Project specs**: Run `ccw spec load --keywords planning` to load project context
|
||||||
- If missing: WARN — Phase 1 will call `workflow:init` to generate it. Ask user: "检测到项目使用 [tech stack from package.json], 是否正确?需要补充什么?"
|
- If spec system unavailable: WARN — Phase 1 will call `workflow:init` to initialize. Ask user: "检测到项目使用 [tech stack from package.json], 是否正确?需要补充什么?"
|
||||||
- **specs/*.md**: Check `{projectRoot}/.workflow/specs/*.md`
|
|
||||||
- If missing: WARN — will be generated as empty scaffold. Ask: "有特定的编码规范需要遵循吗?"
|
|
||||||
- **Test framework**: Detect from config files (jest.config, vitest.config, pytest.ini, etc.)
|
- **Test framework**: Detect from config files (jest.config, vitest.config, pytest.ini, etc.)
|
||||||
- If missing: Ask: "未检测到测试框架,请指定测试命令(如 `npm test`),或输入 'skip' 跳过"
|
- If missing: Ask: "未检测到测试框架,请指定测试命令(如 `npm test`),或输入 'skip' 跳过"
|
||||||
|
|
||||||
@@ -38,8 +36,8 @@ Print formatted checklist:
|
|||||||
✓ 项目根目录: D:\myproject
|
✓ 项目根目录: D:\myproject
|
||||||
✓ .workflow/ 目录就绪
|
✓ .workflow/ 目录就绪
|
||||||
⚠ Git: 3 个未提交变更
|
⚠ Git: 3 个未提交变更
|
||||||
✓ project-tech.json: 已检测 (Express + TypeORM + PostgreSQL)
|
✓ Project specs: 已加载 (ccw spec load --keywords planning)
|
||||||
⚠ specs/*.md: 未找到 (Phase 1 将生成空模板)
|
⚠ specs: 未找到 (Phase 1 将初始化)
|
||||||
✓ 测试框架: jest (npm test)
|
✓ 测试框架: jest (npm test)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -156,13 +154,13 @@ Each dimension scores 0-2 (0=missing, 1=vague, 2=clear). **Total minimum: 6/10 t
|
|||||||
> "有哪些限制条件?常见约束:不破坏现有 API / 使用现有数据库 / 不引入新依赖 / 保持现有模式。请选择或自定义"
|
> "有哪些限制条件?常见约束:不破坏现有 API / 使用现有数据库 / 不引入新依赖 / 保持现有模式。请选择或自定义"
|
||||||
|
|
||||||
**上下文不足 (score 0-1)**:
|
**上下文不足 (score 0-1)**:
|
||||||
> "我从项目中检测到: [tech stack from project-tech.json]。还有需要知道的技术细节吗?"
|
> "我从项目中检测到: [tech stack from loaded specs]。还有需要知道的技术细节吗?"
|
||||||
|
|
||||||
### 2.4 Auto-Enhancement
|
### 2.4 Auto-Enhancement
|
||||||
|
|
||||||
For dimensions still at score 1 after Q&A, auto-enhance from codebase:
|
For dimensions still at score 1 after Q&A, auto-enhance from codebase:
|
||||||
- **Scope**: Use `Glob` and `Grep` to find related files
|
- **Scope**: Use `Glob` and `Grep` to find related files
|
||||||
- **Context**: Read `project-tech.json` and key config files
|
- **Context**: Run `ccw spec load --keywords planning` to load project context
|
||||||
- **Constraints**: Infer from `specs/*.md`
|
- **Constraints**: Infer from `specs/*.md`
|
||||||
|
|
||||||
### 2.5 Assemble Structured Description
|
### 2.5 Assemble Structured Description
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ Step 1: Topic Understanding
|
|||||||
|
|
||||||
Step 2: Exploration (Inline, No Agents)
|
Step 2: Exploration (Inline, No Agents)
|
||||||
├─ Detect codebase → search relevant modules, patterns
|
├─ Detect codebase → search relevant modules, patterns
|
||||||
│ ├─ Read project-tech.json / specs/*.md (if exists)
|
│ ├─ Run `ccw spec load --keywords exploration` (if spec system available)
|
||||||
│ └─ Use Grep, Glob, Read, mcp__ace-tool__search_context
|
│ └─ Use Grep, Glob, Read, mcp__ace-tool__search_context
|
||||||
├─ Multi-perspective analysis (if selected, serial)
|
├─ Multi-perspective analysis (if selected, serial)
|
||||||
│ ├─ Single: Comprehensive analysis
|
│ ├─ Single: Comprehensive analysis
|
||||||
@@ -297,7 +297,7 @@ const hasCodebase = Bash(`
|
|||||||
|
|
||||||
if (hasCodebase !== 'none') {
|
if (hasCodebase !== 'none') {
|
||||||
// 1. Read project metadata (if exists)
|
// 1. Read project metadata (if exists)
|
||||||
// - .workflow/project-tech.json (tech stack info)
|
// - Run `ccw spec load --keywords exploration` (load project specs)
|
||||||
// - .workflow/specs/*.md (project conventions)
|
// - .workflow/specs/*.md (project conventions)
|
||||||
|
|
||||||
// 2. Search codebase for relevant content
|
// 2. Search codebase for relevant content
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ Use built-in tools to understand the codebase structure before spawning perspect
|
|||||||
**Context Gathering Activities**:
|
**Context Gathering Activities**:
|
||||||
1. **Get project structure** - Execute `ccw tool exec get_modules_by_depth '{}'`
|
1. **Get project structure** - Execute `ccw tool exec get_modules_by_depth '{}'`
|
||||||
2. **Search for related code** - Use Grep/Glob to find files matching topic keywords
|
2. **Search for related code** - Use Grep/Glob to find files matching topic keywords
|
||||||
3. **Read project tech context** - Load `{projectRoot}/.workflow/project-tech.json` if available
|
3. **Read project tech context** - Run `ccw spec load --keywords "exploration planning"` if spec system available
|
||||||
4. **Analyze patterns** - Identify common code patterns and architecture decisions
|
4. **Analyze patterns** - Identify common code patterns and architecture decisions
|
||||||
|
|
||||||
**exploration-codebase.json Structure**:
|
**exploration-codebase.json Structure**:
|
||||||
@@ -358,8 +358,8 @@ const agentIds = perspectives.map(perspective => {
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords "exploration planning"`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
3. Read project tech context from loaded specs
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -566,7 +566,7 @@ const deepDiveAgent = spawn_agent({
|
|||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first)
|
||||||
2. Read: ${sessionFolder}/perspectives.json (prior findings)
|
2. Read: ${sessionFolder}/perspectives.json (prior findings)
|
||||||
3. Read: ${projectRoot}/.workflow/project-tech.json
|
3. Run: `ccw spec load --keywords "exploration planning"`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ Use built-in tools directly to understand the task scope and identify sub-domain
|
|||||||
**Analysis Activities**:
|
**Analysis Activities**:
|
||||||
1. **Search for references** — Find related documentation, README files, and architecture guides
|
1. **Search for references** — Find related documentation, README files, and architecture guides
|
||||||
- Use: `mcp__ace-tool__search_context`, Grep, Glob, Read
|
- Use: `mcp__ace-tool__search_context`, Grep, Glob, Read
|
||||||
- Read: `.workflow/project-tech.json`, `.workflow/specs/*.md` (if exists)
|
- Run: `ccw spec load --keywords planning` (if spec system available)
|
||||||
2. **Extract task keywords** — Identify key terms and concepts from the task description
|
2. **Extract task keywords** — Identify key terms and concepts from the task description
|
||||||
3. **Identify ambiguities** — List any unclear points or multiple possible interpretations
|
3. **Identify ambiguities** — List any unclear points or multiple possible interpretations
|
||||||
4. **Clarify with user** — If ambiguities found, use AskUserQuestion for clarification
|
4. **Clarify with user** — If ambiguities found, use AskUserQuestion for clarification
|
||||||
|
|||||||
@@ -231,8 +231,7 @@ const agentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
||||||
2. Read: {projectRoot}/.workflow/project-tech.json
|
2. Execute: ccw spec load --keywords exploration
|
||||||
3. Read: {projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
## TASK CONTEXT
|
## TASK CONTEXT
|
||||||
${taskContext}
|
${taskContext}
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ const hasCodebase = bash(`
|
|||||||
// 2. Codebase Exploration (only when hasCodebase !== 'none')
|
// 2. Codebase Exploration (only when hasCodebase !== 'none')
|
||||||
if (hasCodebase !== 'none') {
|
if (hasCodebase !== 'none') {
|
||||||
// Read project metadata (if exists)
|
// Read project metadata (if exists)
|
||||||
// .workflow/project-tech.json, .workflow/specs/*.md
|
// Run `ccw spec load --keywords planning`
|
||||||
|
|
||||||
// Search codebase for requirement-relevant context
|
// Search codebase for requirement-relevant context
|
||||||
// Use: mcp__ace-tool__search_context, Grep, Glob, Read
|
// Use: mcp__ace-tool__search_context, Grep, Glob, Read
|
||||||
|
|||||||
@@ -303,8 +303,7 @@ const agentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Execute: ccw spec load --keywords "exploration execution"
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -94,8 +94,7 @@ dimensions.forEach(dimension => {
|
|||||||
3. Get target files: Read resolved_files from review-state.json
|
3. Get target files: Read resolved_files from review-state.json
|
||||||
4. Validate file access: bash(ls -la ${targetFiles.join(' ')})
|
4. Validate file access: bash(ls -la ${targetFiles.join(' ')})
|
||||||
5. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference)
|
5. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference)
|
||||||
6. Read: ${projectRoot}/.workflow/project-tech.json (technology stack and architecture context)
|
6. Execute: ccw spec load --keywords "exploration execution" (technology stack and constraints)
|
||||||
7. Read: ${projectRoot}/.workflow/specs/*.md (user-defined constraints and conventions to validate against)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -217,8 +216,7 @@ dimensions.forEach(dimension => {
|
|||||||
4. Get changed files: bash(cd ${workflowDir} && git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u)
|
4. Get changed files: bash(cd ${workflowDir} && git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u)
|
||||||
5. Read review state: ${reviewStateJsonPath}
|
5. Read review state: ${reviewStateJsonPath}
|
||||||
6. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference)
|
6. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference)
|
||||||
7. Read: ${projectRoot}/.workflow/project-tech.json (technology stack and architecture context)
|
7. Execute: ccw spec load --keywords "exploration execution" (technology stack and constraints)
|
||||||
8. Read: ${projectRoot}/.workflow/specs/*.md (user-defined constraints and conventions to validate against)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -336,8 +334,7 @@ const deepDiveAgentId = spawn_agent({
|
|||||||
4. Identify related code: bash(grep -r "import.*${basename(file)}" ${projectDir}/src --include="*.ts")
|
4. Identify related code: bash(grep -r "import.*${basename(file)}" ${projectDir}/src --include="*.ts")
|
||||||
5. Read test files: bash(find ${projectDir}/tests -name "*${basename(file, '.ts')}*" -type f)
|
5. Read test files: bash(find ${projectDir}/tests -name "*${basename(file, '.ts')}*" -type f)
|
||||||
6. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference)
|
6. Execute: cat ~/.ccw/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference)
|
||||||
7. Read: ${projectRoot}/.workflow/project-tech.json (technology stack and architecture context)
|
7. Execute: ccw spec load --keywords "exploration execution" (technology stack and constraints for remediation)
|
||||||
8. Read: ${projectRoot}/.workflow/specs/*.md (user-defined constraints for remediation compliance)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ const agentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-planning-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-planning-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Execute: ccw spec load --keywords planning
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,7 @@ const execAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-execution-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-execution-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Execute: ccw spec load --keywords execution
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ completion report.
|
|||||||
### Step 1: Load Context
|
### Step 1: Load Context
|
||||||
|
|
||||||
After reading role definition:
|
After reading role definition:
|
||||||
- Read: `.workflow/project-tech.json`
|
- Run: `ccw spec load --keywords execution`
|
||||||
- Read: `.workflow/specs/*.md`
|
|
||||||
- Extract issue ID, solution file path, session dir from task message
|
- Extract issue ID, solution file path, session dir from task message
|
||||||
|
|
||||||
### Step 2: Load Solution
|
### Step 2: Load Solution
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ Outputs `ISSUE_READY:{issueId}` after each solution and waits for orchestrator t
|
|||||||
### Step 1: Load Context
|
### Step 1: Load Context
|
||||||
|
|
||||||
After reading role definition, load project context:
|
After reading role definition, load project context:
|
||||||
- Read: `.workflow/project-tech.json`
|
- Run: `ccw spec load --keywords planning`
|
||||||
- Read: `.workflow/specs/*.md`
|
|
||||||
- Extract session directory and artifacts directory from task message
|
- Extract session directory and artifacts directory from task message
|
||||||
|
|
||||||
### Step 2: Parse Input
|
### Step 2: Parse Input
|
||||||
@@ -82,7 +81,7 @@ spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/issue-plan-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/issue-plan-agent.md (MUST read first)
|
||||||
2. Read: .workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ const plannerAgent = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/planex-planner.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/planex-planner.md (MUST read first)
|
||||||
2. Read: .workflow/project-tech.json
|
2. Run: `ccw spec load --keywords "planning execution"`
|
||||||
3. Read: .workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -156,8 +155,7 @@ while (true) {
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/planex-executor.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/planex-executor.md (MUST read first)
|
||||||
2. Read: .workflow/project-tech.json
|
2. Run: `ccw spec load --keywords "planning execution"`
|
||||||
3. Read: .workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -88,8 +88,7 @@ const agentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords "planning execution"`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
## TASK CONTEXT
|
## TASK CONTEXT
|
||||||
${taskContext}
|
${taskContext}
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ const contextAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/test-context-search-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/test-context-search-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -102,8 +101,7 @@ const contextAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/context-search-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/context-search-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -176,8 +174,7 @@ const analysisAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-execution-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-execution-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -245,8 +242,7 @@ const taskGenAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/action-planning-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/action-planning-agent.md (MUST read first)
|
||||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
3. Read: ${projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -90,8 +90,7 @@ const analysisAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/cli-planning-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/cli-planning-agent.md (MUST read first)
|
||||||
2. Read: {projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords planning`
|
||||||
3. Read: {projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -157,8 +156,7 @@ const fixAgentId = spawn_agent({
|
|||||||
|
|
||||||
### MANDATORY FIRST STEPS (Agent Execute)
|
### MANDATORY FIRST STEPS (Agent Execute)
|
||||||
1. **Read role definition**: ~/.codex/agents/test-fix-agent.md (MUST read first)
|
1. **Read role definition**: ~/.codex/agents/test-fix-agent.md (MUST read first)
|
||||||
2. Read: {projectRoot}/.workflow/project-tech.json
|
2. Run: `ccw spec load --keywords execution`
|
||||||
3. Read: {projectRoot}/.workflow/specs/*.md
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -111,19 +111,19 @@ export function HookDialog({
|
|||||||
const newErrors: Record<string, string> = {};
|
const newErrors: Record<string, string> = {};
|
||||||
|
|
||||||
if (!formData.name.trim()) {
|
if (!formData.name.trim()) {
|
||||||
newErrors.name = formatMessage({ id: 'hooks.validation.nameRequired' });
|
newErrors.name = formatMessage({ id: 'specs.hooks.validation.nameRequired', defaultMessage: 'Name is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!formData.command.trim()) {
|
if (!formData.command.trim()) {
|
||||||
newErrors.command = formatMessage({ id: 'hooks.validation.commandRequired' });
|
newErrors.command = formatMessage({ id: 'specs.hooks.validation.commandRequired', defaultMessage: 'Command is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.timeout && formData.timeout < 1000) {
|
if (formData.timeout && formData.timeout < 1000) {
|
||||||
newErrors.timeout = formatMessage({ id: 'hooks.validation.timeoutMin' });
|
newErrors.timeout = formatMessage({ id: 'specs.hooks.validation.timeoutMin', defaultMessage: 'Minimum timeout is 1000ms' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formData.timeout && formData.timeout > 300000) {
|
if (formData.timeout && formData.timeout > 300000) {
|
||||||
newErrors.timeout = formatMessage({ id: 'hooks.validation.timeoutMax' });
|
newErrors.timeout = formatMessage({ id: 'specs.hooks.validation.timeoutMax', defaultMessage: 'Maximum timeout is 300000ms' });
|
||||||
}
|
}
|
||||||
|
|
||||||
setErrors(newErrors);
|
setErrors(newErrors);
|
||||||
@@ -153,11 +153,11 @@ export function HookDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{isEditing
|
{isEditing
|
||||||
? formatMessage({ id: 'hooks.dialog.editTitle' })
|
? formatMessage({ id: 'specs.hooks.dialog.editTitle', defaultMessage: 'Edit Hook' })
|
||||||
: formatMessage({ id: 'hooks.dialog.createTitle' })}
|
: formatMessage({ id: 'specs.hooks.dialog.createTitle', defaultMessage: 'Create Hook' })}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{formatMessage({ id: 'hooks.dialog.description' })}
|
{formatMessage({ id: 'specs.hooks.dialog.description', defaultMessage: 'Configure the hook trigger event, command, and other settings.' })}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -165,13 +165,13 @@ export function HookDialog({
|
|||||||
{/* Name field */}
|
{/* Name field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="name" className="required">
|
<Label htmlFor="name" className="required">
|
||||||
{formatMessage({ id: 'hooks.fields.name' })}
|
{formatMessage({ id: 'specs.hooks.fields.name', defaultMessage: 'Hook Name' })}
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="name"
|
id="name"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) => updateField('name', e.target.value)}
|
onChange={(e) => updateField('name', e.target.value)}
|
||||||
placeholder={formatMessage({ id: 'hooks.placeholders.name' })}
|
placeholder={formatMessage({ id: 'specs.hooks.placeholders.name', defaultMessage: 'Enter hook name' })}
|
||||||
className={errors.name ? 'border-destructive' : ''}
|
className={errors.name ? 'border-destructive' : ''}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
@@ -183,7 +183,7 @@ export function HookDialog({
|
|||||||
{/* Event type field */}
|
{/* Event type field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="event" className="required">
|
<Label htmlFor="event" className="required">
|
||||||
{formatMessage({ id: 'hooks.fields.event' })}
|
{formatMessage({ id: 'specs.hooks.fields.event', defaultMessage: 'Trigger Event' })}
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
value={formData.event}
|
value={formData.event}
|
||||||
@@ -191,29 +191,29 @@ export function HookDialog({
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
<SelectTrigger id="event">
|
<SelectTrigger id="event">
|
||||||
<SelectValue placeholder={formatMessage({ id: 'hooks.placeholders.event' })} />
|
<SelectValue placeholder={formatMessage({ id: 'specs.hooks.placeholders.event', defaultMessage: 'Select event' })} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="SessionStart">
|
<SelectItem value="SessionStart">
|
||||||
{formatMessage({ id: 'hooks.events.sessionStart' })}
|
{formatMessage({ id: 'specs.hooks.events.sessionStart', defaultMessage: 'Session Start' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="UserPromptSubmit">
|
<SelectItem value="UserPromptSubmit">
|
||||||
{formatMessage({ id: 'hooks.events.userPromptSubmit' })}
|
{formatMessage({ id: 'specs.hooks.events.userPromptSubmit', defaultMessage: 'User Prompt Submit' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="SessionEnd">
|
<SelectItem value="SessionEnd">
|
||||||
{formatMessage({ id: 'hooks.events.sessionEnd' })}
|
{formatMessage({ id: 'specs.hooks.events.sessionEnd', defaultMessage: 'Session End' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatMessage({ id: 'hints.hookEvents' })}
|
{formatMessage({ id: 'specs.hints.hookEvents', defaultMessage: 'Select when this hook should be triggered' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Scope field */}
|
{/* Scope field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="required">
|
<Label className="required">
|
||||||
{formatMessage({ id: 'hooks.fields.scope' })}
|
{formatMessage({ id: 'specs.hooks.fields.scope', defaultMessage: 'Scope' })}
|
||||||
</Label>
|
</Label>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={formData.scope}
|
value={formData.scope}
|
||||||
@@ -225,32 +225,32 @@ export function HookDialog({
|
|||||||
<RadioGroupItem value="global" id="scope-global" />
|
<RadioGroupItem value="global" id="scope-global" />
|
||||||
<Label htmlFor="scope-global" className="flex items-center gap-1.5 cursor-pointer">
|
<Label htmlFor="scope-global" className="flex items-center gap-1.5 cursor-pointer">
|
||||||
<Globe className="h-4 w-4" />
|
<Globe className="h-4 w-4" />
|
||||||
{formatMessage({ id: 'hooks.scope.global' })}
|
{formatMessage({ id: 'specs.hooks.scope.global', defaultMessage: 'Global' })}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<RadioGroupItem value="project" id="scope-project" />
|
<RadioGroupItem value="project" id="scope-project" />
|
||||||
<Label htmlFor="scope-project" className="flex items-center gap-1.5 cursor-pointer">
|
<Label htmlFor="scope-project" className="flex items-center gap-1.5 cursor-pointer">
|
||||||
<Folder className="h-4 w-4" />
|
<Folder className="h-4 w-4" />
|
||||||
{formatMessage({ id: 'hooks.scope.project' })}
|
{formatMessage({ id: 'specs.hooks.scope.project', defaultMessage: 'Project' })}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatMessage({ id: 'hints.hookScope' })}
|
{formatMessage({ id: 'specs.hints.hookScope', defaultMessage: 'Global hooks apply to all projects, project hooks only to current project' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Command field */}
|
{/* Command field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="command" className="required">
|
<Label htmlFor="command" className="required">
|
||||||
{formatMessage({ id: 'hooks.fields.command' })}
|
{formatMessage({ id: 'specs.hooks.fields.command', defaultMessage: 'Command' })}
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="command"
|
id="command"
|
||||||
value={formData.command}
|
value={formData.command}
|
||||||
onChange={(e) => updateField('command', e.target.value)}
|
onChange={(e) => updateField('command', e.target.value)}
|
||||||
placeholder={formatMessage({ id: 'hooks.placeholders.command' })}
|
placeholder={formatMessage({ id: 'specs.hooks.placeholders.command', defaultMessage: 'Enter command to execute' })}
|
||||||
className={cn('font-mono', errors.command ? 'border-destructive' : '')}
|
className={cn('font-mono', errors.command ? 'border-destructive' : '')}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
@@ -258,20 +258,20 @@ export function HookDialog({
|
|||||||
<p className="text-xs text-destructive">{errors.command}</p>
|
<p className="text-xs text-destructive">{errors.command}</p>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatMessage({ id: 'hints.hookCommand' })}
|
{formatMessage({ id: 'specs.hints.hookCommand', defaultMessage: 'Command to execute, can use environment variables' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description field */}
|
{/* Description field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="description">
|
<Label htmlFor="description">
|
||||||
{formatMessage({ id: 'hooks.fields.description' })}
|
{formatMessage({ id: 'specs.hooks.fields.description', defaultMessage: 'Description' })}
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="description"
|
id="description"
|
||||||
value={formData.description || ''}
|
value={formData.description || ''}
|
||||||
onChange={(e) => updateField('description', e.target.value)}
|
onChange={(e) => updateField('description', e.target.value)}
|
||||||
placeholder={formatMessage({ id: 'hooks.placeholders.description' })}
|
placeholder={formatMessage({ id: 'specs.hooks.placeholders.description', defaultMessage: 'Enter description (optional)' })}
|
||||||
rows={2}
|
rows={2}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
@@ -280,9 +280,9 @@ export function HookDialog({
|
|||||||
{/* Timeout field */}
|
{/* Timeout field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="timeout" className="flex items-center gap-1">
|
<Label htmlFor="timeout" className="flex items-center gap-1">
|
||||||
{formatMessage({ id: 'hooks.fields.timeout' })}
|
{formatMessage({ id: 'specs.hooks.fields.timeout', defaultMessage: 'Timeout' })}
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
({formatMessage({ id: 'hooks.fields.timeoutUnit' })})
|
({formatMessage({ id: 'specs.hooks.fields.timeoutUnit', defaultMessage: 'ms' })})
|
||||||
</span>
|
</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -300,14 +300,14 @@ export function HookDialog({
|
|||||||
<p className="text-xs text-destructive">{errors.timeout}</p>
|
<p className="text-xs text-destructive">{errors.timeout}</p>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatMessage({ id: 'hints.hookTimeout' })}
|
{formatMessage({ id: 'specs.hints.hookTimeout', defaultMessage: 'Timeout for command execution' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Fail mode field */}
|
{/* Fail mode field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="failMode" className="flex items-center gap-1">
|
<Label htmlFor="failMode" className="flex items-center gap-1">
|
||||||
{formatMessage({ id: 'hooks.fields.failMode' })}
|
{formatMessage({ id: 'specs.hooks.fields.failMode', defaultMessage: 'Failure Mode' })}
|
||||||
<HelpCircle className="h-3.5 w-3.5 text-muted-foreground" />
|
<HelpCircle className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
@@ -320,30 +320,30 @@ export function HookDialog({
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="continue">
|
<SelectItem value="continue">
|
||||||
{formatMessage({ id: 'hooks.failModes.continue' })}
|
{formatMessage({ id: 'specs.hooks.failModes.continue', defaultMessage: 'Continue' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="warn">
|
<SelectItem value="warn">
|
||||||
{formatMessage({ id: 'hooks.failModes.warn' })}
|
{formatMessage({ id: 'specs.hooks.failModes.warn', defaultMessage: 'Warn' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="block">
|
<SelectItem value="block">
|
||||||
{formatMessage({ id: 'hooks.failModes.block' })}
|
{formatMessage({ id: 'specs.hooks.failModes.block', defaultMessage: 'Block' })}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatMessage({ id: 'hints.hookFailMode' })}
|
{formatMessage({ id: 'specs.hints.hookFailMode', defaultMessage: 'How to handle command execution failure' })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={handleCancel} disabled={isLoading}>
|
<Button variant="outline" onClick={handleCancel} disabled={isLoading}>
|
||||||
{formatMessage({ id: 'common.cancel' })}
|
{formatMessage({ id: 'specs.common.cancel', defaultMessage: 'Cancel' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSave} disabled={isLoading}>
|
<Button onClick={handleSave} disabled={isLoading}>
|
||||||
{isLoading
|
{isLoading
|
||||||
? formatMessage({ id: 'common.saving' })
|
? formatMessage({ id: 'specs.common.saving', defaultMessage: 'Saving...' })
|
||||||
: formatMessage({ id: 'common.save' })}
|
: formatMessage({ id: 'specs.common.save', defaultMessage: 'Save' })}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|||||||
@@ -336,10 +336,13 @@ import {
|
|||||||
updateSystemSettings,
|
updateSystemSettings,
|
||||||
installRecommendedHooks,
|
installRecommendedHooks,
|
||||||
getSpecStats,
|
getSpecStats,
|
||||||
|
getSpecsList,
|
||||||
|
rebuildSpecIndex,
|
||||||
|
updateSpecFrontmatter,
|
||||||
type SystemSettings,
|
type SystemSettings,
|
||||||
type UpdateSystemSettingsInput,
|
type UpdateSystemSettingsInput,
|
||||||
type InstallRecommendedHooksResponse,
|
|
||||||
type SpecStats,
|
type SpecStats,
|
||||||
|
type SpecsListResponse,
|
||||||
} from '../lib/api';
|
} from '../lib/api';
|
||||||
|
|
||||||
// Query keys for specs settings
|
// Query keys for specs settings
|
||||||
@@ -463,3 +466,110 @@ export function useSpecStats(options: UseSpecStatsOptions = {}): UseSpecStatsRet
|
|||||||
refetch: () => { query.refetch(); },
|
refetch: () => { query.refetch(); },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Specs List Hook
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
export interface UseSpecsListOptions {
|
||||||
|
projectPath?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
staleTime?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseSpecsListReturn {
|
||||||
|
data: SpecsListResponse | undefined;
|
||||||
|
isLoading: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
refetch: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to fetch specs list for all dimensions
|
||||||
|
* @param options - Options including projectPath for workspace isolation
|
||||||
|
*/
|
||||||
|
export function useSpecsList(options: UseSpecsListOptions = {}): UseSpecsListReturn {
|
||||||
|
const { projectPath, enabled = true, staleTime = STALE_TIME } = options;
|
||||||
|
|
||||||
|
const query = useQuery({
|
||||||
|
queryKey: specsSettingsKeys.specStats(projectPath), // Reuse for specs list
|
||||||
|
queryFn: () => getSpecsList(projectPath),
|
||||||
|
staleTime,
|
||||||
|
enabled,
|
||||||
|
retry: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: query.data,
|
||||||
|
isLoading: query.isLoading,
|
||||||
|
error: query.error,
|
||||||
|
refetch: () => { query.refetch(); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Rebuild Spec Index Mutation Hook
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
export interface UseRebuildSpecIndexOptions {
|
||||||
|
projectPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to rebuild spec index
|
||||||
|
* @param options - Options including projectPath for workspace isolation
|
||||||
|
*/
|
||||||
|
export function useRebuildSpecIndex(options: UseRebuildSpecIndexOptions = {}) {
|
||||||
|
const { projectPath } = options;
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: () => rebuildSpecIndex(projectPath),
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate specs list and stats queries to refresh data
|
||||||
|
queryClient.invalidateQueries({ queryKey: specsSettingsKeys.specStats(projectPath) });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: mutation.mutate,
|
||||||
|
mutateAsync: mutation.mutateAsync,
|
||||||
|
isPending: mutation.isPending,
|
||||||
|
error: mutation.error,
|
||||||
|
data: mutation.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Update Spec Frontmatter Mutation Hook
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
export interface UseUpdateSpecFrontmatterOptions {
|
||||||
|
projectPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to update spec frontmatter (e.g., toggle readMode)
|
||||||
|
* @param options - Options including projectPath for workspace isolation
|
||||||
|
*/
|
||||||
|
export function useUpdateSpecFrontmatter(options: UseUpdateSpecFrontmatterOptions = {}) {
|
||||||
|
const { projectPath } = options;
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: ({ file, readMode }: { file: string; readMode: string }) =>
|
||||||
|
updateSpecFrontmatter(file, readMode, projectPath),
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate specs list to refresh data
|
||||||
|
queryClient.invalidateQueries({ queryKey: specsSettingsKeys.specStats(projectPath) });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
mutate: mutation.mutate,
|
||||||
|
mutateAsync: mutation.mutateAsync,
|
||||||
|
isPending: mutation.isPending,
|
||||||
|
error: mutation.error,
|
||||||
|
data: mutation.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -7261,6 +7261,65 @@ export async function getSpecStats(projectPath?: string): Promise<SpecStats> {
|
|||||||
return fetchApi<SpecStats>(url);
|
return fetchApi<SpecStats>(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spec entry from index
|
||||||
|
*/
|
||||||
|
export interface SpecEntry {
|
||||||
|
file: string;
|
||||||
|
title: string;
|
||||||
|
dimension: string;
|
||||||
|
readMode: 'required' | 'optional' | 'keywords';
|
||||||
|
priority: 'critical' | 'high' | 'medium' | 'low';
|
||||||
|
keywords: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specs list response from /api/specs/list
|
||||||
|
*/
|
||||||
|
export interface SpecsListResponse {
|
||||||
|
specs: Record<string, SpecEntry[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch specs list for all dimensions
|
||||||
|
* @param projectPath - Optional project path
|
||||||
|
*/
|
||||||
|
export async function getSpecsList(projectPath?: string): Promise<SpecsListResponse> {
|
||||||
|
const url = projectPath
|
||||||
|
? `/api/specs/list?path=${encodeURIComponent(projectPath)}`
|
||||||
|
: '/api/specs/list';
|
||||||
|
return fetchApi<SpecsListResponse>(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild spec index
|
||||||
|
*/
|
||||||
|
export async function rebuildSpecIndex(projectPath?: string): Promise<{ success: boolean; stats?: Record<string, number> }> {
|
||||||
|
const url = projectPath
|
||||||
|
? `/api/specs/rebuild?path=${encodeURIComponent(projectPath)}`
|
||||||
|
: '/api/specs/rebuild';
|
||||||
|
return fetchApi<{ success: boolean; stats?: Record<string, number> }>(url, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update spec frontmatter (toggle readMode)
|
||||||
|
*/
|
||||||
|
export async function updateSpecFrontmatter(
|
||||||
|
file: string,
|
||||||
|
readMode: string,
|
||||||
|
projectPath?: string
|
||||||
|
): Promise<{ success: boolean; readMode?: string }> {
|
||||||
|
const url = projectPath
|
||||||
|
? `/api/specs/update-frontmatter?path=${encodeURIComponent(projectPath)}`
|
||||||
|
: '/api/specs/update-frontmatter';
|
||||||
|
return fetchApi<{ success: boolean; readMode?: string }>(url, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({ file, readMode }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Analysis API ==========
|
// ========== Analysis API ==========
|
||||||
|
|
||||||
import type { AnalysisSessionSummary, AnalysisSessionDetail } from '../types/analysis';
|
import type { AnalysisSessionSummary, AnalysisSessionDetail } from '../types/analysis';
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import team from './team.json';
|
|||||||
import terminalDashboard from './terminal-dashboard.json';
|
import terminalDashboard from './terminal-dashboard.json';
|
||||||
import skillHub from './skill-hub.json';
|
import skillHub from './skill-hub.json';
|
||||||
import nativeSession from './native-session.json';
|
import nativeSession from './native-session.json';
|
||||||
|
import specs from './specs.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens nested JSON object to dot-separated keys
|
* Flattens nested JSON object to dot-separated keys
|
||||||
@@ -107,4 +108,5 @@ export default {
|
|||||||
...flattenMessages(terminalDashboard, 'terminalDashboard'),
|
...flattenMessages(terminalDashboard, 'terminalDashboard'),
|
||||||
...flattenMessages(skillHub, 'skillHub'),
|
...flattenMessages(skillHub, 'skillHub'),
|
||||||
...flattenMessages(nativeSession, 'nativeSession'),
|
...flattenMessages(nativeSession, 'nativeSession'),
|
||||||
|
...flattenMessages(specs, 'specs'),
|
||||||
} as Record<string, string>;
|
} as Record<string, string>;
|
||||||
|
|||||||
93
ccw/frontend/src/locales/en/specs.json
Normal file
93
ccw/frontend/src/locales/en/specs.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"pageTitle": "Spec Settings",
|
||||||
|
"pageDescription": "Manage specification injection, hooks, and system settings",
|
||||||
|
"tabProjectSpecs": "Project Specs",
|
||||||
|
"tabPersonalSpecs": "Personal",
|
||||||
|
"tabHooks": "Hooks",
|
||||||
|
"tabInjection": "Injection",
|
||||||
|
"tabSettings": "Settings",
|
||||||
|
"searchPlaceholder": "Search specs...",
|
||||||
|
"rebuildIndex": "Rebuild Index",
|
||||||
|
"loading": "Loading...",
|
||||||
|
"noSpecs": "No specs found. Create specs in .workflow/ directory.",
|
||||||
|
|
||||||
|
"recommendedHooks": "Recommended Hooks",
|
||||||
|
"recommendedHooksDesc": "One-click install system-preset spec injection hooks",
|
||||||
|
"installAll": "Install All Recommended Hooks",
|
||||||
|
"installedHooks": "Installed Hooks",
|
||||||
|
"installedHooksDesc": "Manage your installed hooks configuration",
|
||||||
|
"searchHooks": "Search hooks...",
|
||||||
|
"noHooks": "No hooks installed. Install recommended hooks above.",
|
||||||
|
|
||||||
|
"spec": {
|
||||||
|
"edit": "Edit",
|
||||||
|
"toggle": "Toggle",
|
||||||
|
"delete": "Delete",
|
||||||
|
"required": "Required",
|
||||||
|
"optional": "Optional",
|
||||||
|
"priority": {
|
||||||
|
"critical": "Critical",
|
||||||
|
"high": "High",
|
||||||
|
"medium": "Medium",
|
||||||
|
"low": "Low"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"hook": {
|
||||||
|
"install": "Install",
|
||||||
|
"edit": "Edit",
|
||||||
|
"toggle": "Toggle",
|
||||||
|
"delete": "Delete",
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"scope": {
|
||||||
|
"global": "Global",
|
||||||
|
"project": "Project"
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"SessionStart": "Session Start",
|
||||||
|
"UserPromptSubmit": "Prompt Submit",
|
||||||
|
"SessionEnd": "Session End"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"injection": {
|
||||||
|
"title": "Injection Control",
|
||||||
|
"description": "Monitor and manage spec injection length",
|
||||||
|
"currentLength": "Current Length",
|
||||||
|
"maxLength": "Max Length",
|
||||||
|
"warnThreshold": "Warn Threshold",
|
||||||
|
"percentage": "Usage",
|
||||||
|
"truncateOnExceed": "Truncate on Exceed",
|
||||||
|
"truncateDescription": "Automatically truncate when injection exceeds max length",
|
||||||
|
"overLimit": "Over Limit",
|
||||||
|
"warning": "Warning",
|
||||||
|
"normal": "Normal"
|
||||||
|
},
|
||||||
|
|
||||||
|
"settings": {
|
||||||
|
"title": "Global Settings",
|
||||||
|
"description": "Configure personal spec defaults and system settings",
|
||||||
|
"personalSpecDefaults": "Personal Spec Defaults",
|
||||||
|
"defaultReadMode": "Default Read Mode",
|
||||||
|
"autoEnable": "Auto Enable",
|
||||||
|
"autoEnableDescription": "Automatically enable newly created personal specs"
|
||||||
|
},
|
||||||
|
|
||||||
|
"dialog": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"save": "Save",
|
||||||
|
"editSpec": "Edit Spec",
|
||||||
|
"editHook": "Edit Hook",
|
||||||
|
"specTitle": "Spec Title",
|
||||||
|
"keywords": "Keywords",
|
||||||
|
"readMode": "Read Mode",
|
||||||
|
"priority": "Priority",
|
||||||
|
"hookName": "Hook Name",
|
||||||
|
"hookEvent": "Event",
|
||||||
|
"hookCommand": "Command",
|
||||||
|
"hookScope": "Scope",
|
||||||
|
"hookTimeout": "Timeout (ms)",
|
||||||
|
"hookFailMode": "Fail Mode"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ import team from './team.json';
|
|||||||
import terminalDashboard from './terminal-dashboard.json';
|
import terminalDashboard from './terminal-dashboard.json';
|
||||||
import skillHub from './skill-hub.json';
|
import skillHub from './skill-hub.json';
|
||||||
import nativeSession from './native-session.json';
|
import nativeSession from './native-session.json';
|
||||||
|
import specs from './specs.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens nested JSON object to dot-separated keys
|
* Flattens nested JSON object to dot-separated keys
|
||||||
@@ -107,4 +108,5 @@ export default {
|
|||||||
...flattenMessages(terminalDashboard, 'terminalDashboard'),
|
...flattenMessages(terminalDashboard, 'terminalDashboard'),
|
||||||
...flattenMessages(skillHub, 'skillHub'),
|
...flattenMessages(skillHub, 'skillHub'),
|
||||||
...flattenMessages(nativeSession, 'nativeSession'),
|
...flattenMessages(nativeSession, 'nativeSession'),
|
||||||
|
...flattenMessages(specs, 'specs'),
|
||||||
} as Record<string, string>;
|
} as Record<string, string>;
|
||||||
|
|||||||
202
ccw/frontend/src/locales/zh/specs.json
Normal file
202
ccw/frontend/src/locales/zh/specs.json
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
{
|
||||||
|
"pageTitle": "规范设置",
|
||||||
|
"pageDescription": "管理规范注入、钩子和系统设置",
|
||||||
|
"tabProjectSpecs": "项目规范",
|
||||||
|
"tabPersonalSpecs": "个人",
|
||||||
|
"tabHooks": "钩子",
|
||||||
|
"tabInjection": "注入控制",
|
||||||
|
"tabSettings": "设置",
|
||||||
|
"searchPlaceholder": "搜索规范...",
|
||||||
|
"rebuildIndex": "重建索引",
|
||||||
|
"loading": "加载中...",
|
||||||
|
"noSpecs": "未找到规范。请在 .workflow/ 目录中创建规范文件。",
|
||||||
|
|
||||||
|
"recommendedHooks": "推荐钩子",
|
||||||
|
"recommendedHooksDesc": "一键安装系统预设的规范注入钩子",
|
||||||
|
"installAll": "安装所有推荐钩子",
|
||||||
|
"installedHooks": "已安装钩子",
|
||||||
|
"installedHooksDesc": "管理已安装的钩子配置",
|
||||||
|
"searchHooks": "搜索钩子...",
|
||||||
|
"noHooks": "未安装钩子。请安装上方的推荐钩子。",
|
||||||
|
|
||||||
|
"actions": {
|
||||||
|
"edit": "编辑",
|
||||||
|
"delete": "删除",
|
||||||
|
"reset": "重置",
|
||||||
|
"save": "保存",
|
||||||
|
"saving": "保存中..."
|
||||||
|
},
|
||||||
|
|
||||||
|
"status": {
|
||||||
|
"enabled": "已启用",
|
||||||
|
"disabled": "已禁用"
|
||||||
|
},
|
||||||
|
|
||||||
|
"readMode": {
|
||||||
|
"required": "必读",
|
||||||
|
"optional": "选读"
|
||||||
|
},
|
||||||
|
|
||||||
|
"priority": {
|
||||||
|
"critical": "关键",
|
||||||
|
"high": "高",
|
||||||
|
"medium": "中",
|
||||||
|
"low": "低"
|
||||||
|
},
|
||||||
|
|
||||||
|
"spec": {
|
||||||
|
"edit": "编辑规范",
|
||||||
|
"toggle": "切换状态",
|
||||||
|
"delete": "删除规范",
|
||||||
|
"deleteConfirm": "确定要删除此规范吗?",
|
||||||
|
"title": "规范标题",
|
||||||
|
"keywords": "关键词",
|
||||||
|
"keywordsPlaceholder": "输入关键词,用逗号分隔",
|
||||||
|
"readMode": "读取模式",
|
||||||
|
"priority": "优先级",
|
||||||
|
"file": "文件路径"
|
||||||
|
},
|
||||||
|
|
||||||
|
"hook": {
|
||||||
|
"install": "安装",
|
||||||
|
"uninstall": "卸载",
|
||||||
|
"edit": "编辑钩子",
|
||||||
|
"toggle": "切换状态",
|
||||||
|
"delete": "删除钩子",
|
||||||
|
"enabled": "已启用",
|
||||||
|
"disabled": "已禁用",
|
||||||
|
"installed": "已安装",
|
||||||
|
"notInstalled": "未安装",
|
||||||
|
"scope": {
|
||||||
|
"global": "全局",
|
||||||
|
"project": "项目"
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"SessionStart": "会话开始",
|
||||||
|
"UserPromptSubmit": "提示词提交",
|
||||||
|
"SessionEnd": "会话结束"
|
||||||
|
},
|
||||||
|
"name": "钩子名称",
|
||||||
|
"eventLabel": "触发事件",
|
||||||
|
"command": "执行命令",
|
||||||
|
"scopeLabel": "作用域",
|
||||||
|
"timeout": "超时时间(ms)",
|
||||||
|
"failMode": "失败模式",
|
||||||
|
"failModeContinue": "继续",
|
||||||
|
"failModeBlock": "阻止",
|
||||||
|
"failModeWarn": "警告"
|
||||||
|
},
|
||||||
|
|
||||||
|
"hooks": {
|
||||||
|
"dialog": {
|
||||||
|
"createTitle": "创建钩子",
|
||||||
|
"editTitle": "编辑钩子",
|
||||||
|
"description": "配置钩子的触发事件、执行命令和其他参数。"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"name": "钩子名称",
|
||||||
|
"event": "触发事件",
|
||||||
|
"scope": "作用域",
|
||||||
|
"command": "执行命令",
|
||||||
|
"description": "描述",
|
||||||
|
"timeout": "超时时间",
|
||||||
|
"timeoutUnit": "毫秒",
|
||||||
|
"failMode": "失败处理模式"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"name": "输入钩子名称",
|
||||||
|
"event": "选择触发事件",
|
||||||
|
"command": "输入要执行的命令",
|
||||||
|
"description": "输入钩子描述(可选)"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"sessionStart": "会话开始",
|
||||||
|
"userPromptSubmit": "提示词提交",
|
||||||
|
"sessionEnd": "会话结束"
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"global": "全局",
|
||||||
|
"project": "项目"
|
||||||
|
},
|
||||||
|
"failModes": {
|
||||||
|
"continue": "继续执行",
|
||||||
|
"warn": "显示警告",
|
||||||
|
"block": "阻止操作"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"hints": {
|
||||||
|
"hookEvents": "选择钩子触发的事件类型",
|
||||||
|
"hookScope": "全局钩子应用于所有项目,项目钩子仅当前项目",
|
||||||
|
"hookCommand": "执行的命令,可使用环境变量",
|
||||||
|
"hookTimeout": "命令执行的超时时间",
|
||||||
|
"hookFailMode": "命令执行失败时的处理方式"
|
||||||
|
},
|
||||||
|
|
||||||
|
"common": {
|
||||||
|
"cancel": "取消",
|
||||||
|
"save": "保存",
|
||||||
|
"delete": "删除",
|
||||||
|
"edit": "编辑",
|
||||||
|
"reset": "重置",
|
||||||
|
"confirm": "确认"
|
||||||
|
},
|
||||||
|
|
||||||
|
"injection": {
|
||||||
|
"title": "注入控制",
|
||||||
|
"description": "监控和管理规范注入长度",
|
||||||
|
"statusTitle": "当前注入状态",
|
||||||
|
"settingsTitle": "注入控制设置",
|
||||||
|
"settingsDescription": "配置如何将规范内容注入到 AI 上下文中。",
|
||||||
|
"currentLength": "当前长度",
|
||||||
|
"maxLength": "最大注入长度(字符)",
|
||||||
|
"maxLengthHelp": "推荐值:4000-10000。过大会消耗过多上下文,过小可能截断重要规范。",
|
||||||
|
"warnThreshold": "警告阈值",
|
||||||
|
"warnThresholdLabel": "警告阈值(字符)",
|
||||||
|
"warnThresholdHelp": "当注入长度超过此值时显示警告。",
|
||||||
|
"percentage": "使用率",
|
||||||
|
"truncateOnExceed": "超出时截断",
|
||||||
|
"truncateHelp": "当内容超出最大长度时自动截断。",
|
||||||
|
"overLimit": "已超出限制",
|
||||||
|
"overLimitDescription": "当前注入内容已超出最大长度限制 {max} 字符,超出部分将被截断。",
|
||||||
|
"warning": "接近限制",
|
||||||
|
"normal": "正常",
|
||||||
|
"characters": "字符",
|
||||||
|
"statsInfo": "统计信息",
|
||||||
|
"requiredLength": "必读规范长度:",
|
||||||
|
"matchedLength": "关键词匹配长度:",
|
||||||
|
"remaining": "剩余空间:",
|
||||||
|
"loadError": "加载统计数据失败",
|
||||||
|
"saveSuccess": "设置已保存",
|
||||||
|
"saveError": "保存设置失败"
|
||||||
|
},
|
||||||
|
|
||||||
|
"settings": {
|
||||||
|
"title": "全局设置",
|
||||||
|
"description": "配置个人规范默认值和系统设置",
|
||||||
|
"personalSpecDefaults": "个人规范默认值",
|
||||||
|
"defaultReadMode": "默认读取模式",
|
||||||
|
"defaultReadModeHelp": "新创建的个人规范的默认读取模式",
|
||||||
|
"autoEnable": "自动启用",
|
||||||
|
"autoEnableDescription": "新创建的个人规范自动启用"
|
||||||
|
},
|
||||||
|
|
||||||
|
"dialog": {
|
||||||
|
"cancel": "取消",
|
||||||
|
"save": "保存",
|
||||||
|
"close": "关闭",
|
||||||
|
"editSpec": "编辑规范",
|
||||||
|
"editHook": "编辑钩子",
|
||||||
|
"confirmDelete": "确认删除",
|
||||||
|
"specTitle": "规范标题",
|
||||||
|
"keywords": "关键词",
|
||||||
|
"readMode": "读取模式",
|
||||||
|
"priority": "优先级",
|
||||||
|
"hookName": "钩子名称",
|
||||||
|
"hookEvent": "触发事件",
|
||||||
|
"hookCommand": "执行命令",
|
||||||
|
"hookScope": "作用域",
|
||||||
|
"hookTimeout": "超时时间(ms)",
|
||||||
|
"hookFailMode": "失败模式"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,24 +4,40 @@
|
|||||||
* Main page for managing spec settings, hooks, injection control, and global settings.
|
* Main page for managing spec settings, hooks, injection control, and global settings.
|
||||||
* Uses 5 tabs: Project Specs | Personal Specs | Hooks | Injection | Settings
|
* Uses 5 tabs: Project Specs | Personal Specs | Hooks | Injection | Settings
|
||||||
*/
|
*/
|
||||||
import { useState } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/Tabs';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Input } from '@/components/ui/Input';
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { ScrollText, User, Plug, Gauge, Settings, RefreshCw, Search } from 'lucide-react';
|
import { ScrollText, User, Plug, Gauge, Settings, RefreshCw, Search } from 'lucide-react';
|
||||||
import { SpecCard, SpecDialog, type Spec, type SpecFormData } from '@/components/specs';
|
import { SpecCard, SpecDialog, type Spec, type SpecFormData } from '@/components/specs';
|
||||||
import { HookCard, HookDialog, type HookConfig } from '@/components/specs';
|
import { HookCard, HookDialog, type HookConfig } from '@/components/specs';
|
||||||
import { InjectionControlTab } from '@/components/specs/InjectionControlTab';
|
import { InjectionControlTab } from '@/components/specs/InjectionControlTab';
|
||||||
import { GlobalSettingsTab } from '@/components/specs/GlobalSettingsTab';
|
import { GlobalSettingsTab } from '@/components/specs/GlobalSettingsTab';
|
||||||
import { useSpecStats } from '@/hooks/useSystemSettings';
|
import { useSpecStats, useSpecsList, useSystemSettings, useRebuildSpecIndex } from '@/hooks/useSystemSettings';
|
||||||
|
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||||
|
import type { SpecEntry } from '@/lib/api';
|
||||||
|
|
||||||
type SettingsTab = 'project-specs' | 'personal-specs' | 'hooks' | 'injection' | 'settings';
|
type SettingsTab = 'project-specs' | 'personal-specs' | 'hooks' | 'injection' | 'settings';
|
||||||
|
|
||||||
|
// Convert SpecEntry to Spec for display
|
||||||
|
function specEntryToSpec(entry: SpecEntry, dimension: string): Spec {
|
||||||
|
return {
|
||||||
|
id: entry.file,
|
||||||
|
title: entry.title,
|
||||||
|
dimension: dimension as Spec['dimension'],
|
||||||
|
keywords: entry.keywords,
|
||||||
|
readMode: entry.readMode as Spec['readMode'],
|
||||||
|
priority: entry.priority as Spec['priority'],
|
||||||
|
file: entry.file,
|
||||||
|
enabled: true, // Default to enabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function SpecsSettingsPage() {
|
export function SpecsSettingsPage() {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const projectPath = useWorkflowStore(selectProjectPath);
|
||||||
const [activeTab, setActiveTab] = useState<SettingsTab>('project-specs');
|
const [activeTab, setActiveTab] = useState<SettingsTab>('project-specs');
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||||
@@ -29,13 +45,50 @@ export function SpecsSettingsPage() {
|
|||||||
const [editingSpec, setEditingSpec] = useState<Spec | null>(null);
|
const [editingSpec, setEditingSpec] = useState<Spec | null>(null);
|
||||||
const [editingHook, setEditingHook] = useState<HookConfig | null>(null);
|
const [editingHook, setEditingHook] = useState<HookConfig | null>(null);
|
||||||
|
|
||||||
// Mock data for demonstration - will be replaced with real API calls
|
// Fetch real data
|
||||||
const [projectSpecs] = useState<Spec[]>([]);
|
const { data: specsListData, isLoading: specsLoading, refetch: refetchSpecs } = useSpecsList({ projectPath });
|
||||||
const [personalSpecs] = useState<Spec[]>([]);
|
const { data: statsData } = useSpecStats({ projectPath });
|
||||||
const [hooks] = useState<HookConfig[]>([]);
|
const { data: systemSettings } = useSystemSettings();
|
||||||
const [isLoading] = useState(false);
|
const rebuildMutation = useRebuildSpecIndex();
|
||||||
|
|
||||||
const { data: statsData, refetch: refetchStats } = useSpecStats();
|
// Convert specs data to display format
|
||||||
|
const { projectSpecs, personalSpecs } = useMemo(() => {
|
||||||
|
if (!specsListData?.specs) {
|
||||||
|
return { projectSpecs: [], personalSpecs: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const specs: Spec[] = [];
|
||||||
|
const personal: Spec[] = [];
|
||||||
|
|
||||||
|
for (const [dimension, entries] of Object.entries(specsListData.specs)) {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const spec = specEntryToSpec(entry, dimension);
|
||||||
|
if (dimension === 'personal') {
|
||||||
|
personal.push(spec);
|
||||||
|
} else {
|
||||||
|
specs.push(spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { projectSpecs: specs, personalSpecs: personal };
|
||||||
|
}, [specsListData]);
|
||||||
|
|
||||||
|
// Get hooks from system settings
|
||||||
|
const hooks: HookConfig[] = useMemo(() => {
|
||||||
|
return systemSettings?.recommendedHooks?.map(h => ({
|
||||||
|
id: h.id,
|
||||||
|
name: h.name,
|
||||||
|
event: h.event as HookConfig['event'],
|
||||||
|
command: h.command,
|
||||||
|
description: h.description,
|
||||||
|
scope: h.scope as HookConfig['scope'],
|
||||||
|
enabled: h.autoInstall ?? false,
|
||||||
|
installed: h.autoInstall ?? false,
|
||||||
|
})) ?? [];
|
||||||
|
}, [systemSettings]);
|
||||||
|
|
||||||
|
const isLoading = specsLoading;
|
||||||
|
|
||||||
const handleSpecEdit = (spec: Spec) => {
|
const handleSpecEdit = (spec: Spec) => {
|
||||||
setEditingSpec(spec);
|
setEditingSpec(spec);
|
||||||
@@ -81,7 +134,11 @@ export function SpecsSettingsPage() {
|
|||||||
|
|
||||||
const handleRebuildIndex = async () => {
|
const handleRebuildIndex = async () => {
|
||||||
console.log('Rebuilding index...');
|
console.log('Rebuilding index...');
|
||||||
// TODO: Implement rebuild logic
|
rebuildMutation.mutate(undefined, {
|
||||||
|
onSuccess: () => {
|
||||||
|
refetchSpecs();
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterSpecs = (specs: Spec[]) => {
|
const filterSpecs = (specs: Spec[]) => {
|
||||||
@@ -117,7 +174,7 @@ export function SpecsSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats Summary */}
|
{/* Stats Summary */}
|
||||||
{statsData && (
|
{statsData?.dimensions && (
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-4 gap-4">
|
||||||
{Object.entries(statsData.dimensions).map(([dim, data]) => (
|
{Object.entries(statsData.dimensions).map(([dim, data]) => (
|
||||||
<Card key={dim}>
|
<Card key={dim}>
|
||||||
@@ -178,7 +235,7 @@ export function SpecsSettingsPage() {
|
|||||||
scope: 'global',
|
scope: 'global',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
failMode: 'silent'
|
failMode: 'continue'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'spec-injection-prompt',
|
id: 'spec-injection-prompt',
|
||||||
@@ -188,7 +245,7 @@ export function SpecsSettingsPage() {
|
|||||||
scope: 'project',
|
scope: 'project',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
failMode: 'silent'
|
failMode: 'continue'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -219,11 +276,11 @@ export function SpecsSettingsPage() {
|
|||||||
<HookCard
|
<HookCard
|
||||||
key={hook.id}
|
key={hook.id}
|
||||||
hook={hook}
|
hook={hook}
|
||||||
isRecommended={true}
|
isRecommendedCard={true}
|
||||||
onInstall={() => console.log('Install:', hook.id)}
|
onInstall={() => console.log('Install:', hook.id)}
|
||||||
onEdit={handleHookEdit}
|
onEdit={handleHookEdit}
|
||||||
onToggle={handleHookToggle}
|
onToggle={handleHookToggle}
|
||||||
onDelete={handleHookDelete}
|
onUninstall={handleHookDelete}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -261,7 +318,7 @@ export function SpecsSettingsPage() {
|
|||||||
hook={hook}
|
hook={hook}
|
||||||
onEdit={handleHookEdit}
|
onEdit={handleHookEdit}
|
||||||
onToggle={handleHookToggle}
|
onToggle={handleHookToggle}
|
||||||
onDelete={handleHookDelete}
|
onUninstall={handleHookDelete}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -273,7 +330,7 @@ export function SpecsSettingsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container py-6 max-w-6xl">
|
<div className="max-w-6xl mx-auto">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h1 className="text-2xl font-bold flex items-center gap-2">
|
<h1 className="text-2xl font-bold flex items-center gap-2">
|
||||||
@@ -343,8 +400,10 @@ export function SpecsSettingsPage() {
|
|||||||
<HookDialog
|
<HookDialog
|
||||||
open={hookDialogOpen}
|
open={hookDialogOpen}
|
||||||
onOpenChange={setHookDialogOpen}
|
onOpenChange={setHookDialogOpen}
|
||||||
hook={editingHook}
|
hook={editingHook ?? undefined}
|
||||||
onSave={handleHookSave}
|
onSave={(hookData) => {
|
||||||
|
handleHookSave(editingHook?.id ?? null, hookData);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import chalk from 'chalk';
|
|||||||
|
|
||||||
interface SpecOptions {
|
interface SpecOptions {
|
||||||
dimension?: string;
|
dimension?: string;
|
||||||
context?: string;
|
keywords?: string;
|
||||||
stdin?: boolean;
|
stdin?: boolean;
|
||||||
json?: boolean;
|
json?: boolean;
|
||||||
}
|
}
|
||||||
@@ -60,11 +60,11 @@ function getProjectPath(hookCwd?: string): string {
|
|||||||
/**
|
/**
|
||||||
* Load action - load specs matching dimension/keywords.
|
* 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}.
|
* Hook mode: --stdin reads JSON {session_id, cwd, user_prompt}, outputs JSON {continue, systemMessage}.
|
||||||
*/
|
*/
|
||||||
async function loadAction(options: SpecOptions): Promise<void> {
|
async function loadAction(options: SpecOptions): Promise<void> {
|
||||||
const { stdin, dimension, context } = options;
|
const { stdin, dimension, keywords: keywordsInput } = options;
|
||||||
let projectPath: string;
|
let projectPath: string;
|
||||||
let stdinData: StdinData | undefined;
|
let stdinData: StdinData | undefined;
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ async function loadAction(options: SpecOptions): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const { loadSpecs } = await import('../tools/spec-loader.js');
|
const { loadSpecs } = await import('../tools/spec-loader.js');
|
||||||
|
|
||||||
const keywords = context
|
const keywords = keywordsInput
|
||||||
? context.split(/[\s,]+/).filter(Boolean)
|
? keywordsInput.split(/[\s,]+/).filter(Boolean)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const result = await loadSpecs({
|
const result = await loadSpecs({
|
||||||
@@ -361,19 +361,31 @@ ${chalk.bold('SUBCOMMANDS')}
|
|||||||
|
|
||||||
${chalk.bold('OPTIONS')}
|
${chalk.bold('OPTIONS')}
|
||||||
--dimension <dim> Target dimension: specs, roadmap, changelog, personal
|
--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)
|
--stdin Read input from stdin (Hook mode)
|
||||||
--json Output as JSON
|
--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.bold('EXAMPLES')}
|
||||||
${chalk.gray('# Initialize spec system:')}
|
${chalk.gray('# Initialize spec system:')}
|
||||||
ccw spec init
|
ccw spec init
|
||||||
|
|
||||||
${chalk.gray('# Load specs for a topic (CLI mode):')}
|
${chalk.gray('# Load exploration-phase specs:')}
|
||||||
ccw spec load --dimension specs --context "auth jwt security"
|
ccw spec load --keywords exploration
|
||||||
|
|
||||||
${chalk.gray('# Load all matching specs:')}
|
${chalk.gray('# Load planning-phase specs with auth topic:')}
|
||||||
ccw spec load --context "implement user authentication"
|
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):')}
|
${chalk.gray('# Use as Claude Code hook (settings.json):')}
|
||||||
ccw spec load --stdin
|
ccw spec load --stdin
|
||||||
@@ -24,6 +24,19 @@ import { join, basename, extname, relative } from 'path';
|
|||||||
// Types
|
// 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.
|
* YAML frontmatter schema for spec MD files.
|
||||||
*/
|
*/
|
||||||
@@ -45,7 +58,7 @@ export interface SpecIndexEntry {
|
|||||||
file: string;
|
file: string;
|
||||||
/** Dimension this spec belongs to */
|
/** Dimension this spec belongs to */
|
||||||
dimension: string;
|
dimension: string;
|
||||||
/** Keywords for matching against user prompts */
|
/** Keywords for matching against user prompts (may include category markers) */
|
||||||
keywords: string[];
|
keywords: string[];
|
||||||
/** Whether this spec is required or optional */
|
/** Whether this spec is required or optional */
|
||||||
readMode: 'required' | '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;
|
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';
|
const SPEC_INDEX_DIR = '.spec-index';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -100,10 +114,10 @@ const SPEC_INDEX_DIR = '.spec-index';
|
|||||||
*
|
*
|
||||||
* @param projectPath - Project root directory
|
* @param projectPath - Project root directory
|
||||||
* @param dimension - The dimension name
|
* @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 {
|
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
|
* @param projectPath - Project root directory
|
||||||
*/
|
*/
|
||||||
export async function buildAllIndices(projectPath: string): Promise<void> {
|
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
|
// Ensure .spec-index directory exists
|
||||||
if (!existsSync(indexDir)) {
|
if (!existsSync(indexDir)) {
|
||||||
@@ -269,7 +283,7 @@ export async function getDimensionIndex(
|
|||||||
// Build fresh and cache
|
// Build fresh and cache
|
||||||
const index = await buildDimensionIndex(projectPath, dimension);
|
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)) {
|
if (!existsSync(indexDir)) {
|
||||||
mkdirSync(indexDir, { recursive: true });
|
mkdirSync(indexDir, { recursive: true });
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
SpecIndexEntry,
|
SpecIndexEntry,
|
||||||
DimensionIndex,
|
DimensionIndex,
|
||||||
SPEC_DIMENSIONS,
|
SPEC_DIMENSIONS,
|
||||||
|
SPEC_CATEGORIES,
|
||||||
type SpecDimension,
|
type SpecDimension,
|
||||||
} from './spec-index-builder.js';
|
} from './spec-index-builder.js';
|
||||||
|
|
||||||
Reference in New Issue
Block a user