mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-04 01:40:45 +08:00
feat(cli): add settings file support for builtin Claude
- Enhance CLI status rendering to display settings file information for builtin Claude. - Introduce settings file input in CLI manager for configuring the path to settings.json. - Update Claude CLI tool interface to include settingsFile property. - Implement settings file resolution and validation in CLI executor. - Create a new collaborative planning workflow command with detailed documentation. - Add test scripts for debugging tool configuration and command building.
This commit is contained in:
836
.claude/commands/workflow/collaborative-plan.md
Normal file
836
.claude/commands/workflow/collaborative-plan.md
Normal file
@@ -0,0 +1,836 @@
|
|||||||
|
---
|
||||||
|
name: workflow:collaborative-plan
|
||||||
|
description: Unified collaborative planning with dynamic requirement splitting, parallel sub-agent exploration/understanding/planning, and automatic merge. Each agent maintains process files for full traceability.
|
||||||
|
argument-hint: "[-y|--yes] <task description> [--max-agents=5] [--depth=normal|deep] [--merge-rule=consensus|priority]"
|
||||||
|
allowed-tools: TodoWrite(*), Task(*), AskUserQuestion(*), Read(*), Bash(*), Write(*), Glob(*), Grep(*), mcp__ace-tool__search_context(*)
|
||||||
|
---
|
||||||
|
|
||||||
|
## Auto Mode
|
||||||
|
|
||||||
|
When `--yes` or `-y`: Auto-approve splits, use default merge rule, skip confirmations.
|
||||||
|
|
||||||
|
# Collaborative Planning Command
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic usage
|
||||||
|
/workflow:collaborative-plan "Implement real-time notification system"
|
||||||
|
|
||||||
|
# With options
|
||||||
|
/workflow:collaborative-plan "Refactor authentication module" --max-agents=4
|
||||||
|
/workflow:collaborative-plan "Add payment gateway support" --depth=deep
|
||||||
|
/workflow:collaborative-plan "Migrate to microservices" --merge-rule=priority
|
||||||
|
```
|
||||||
|
|
||||||
|
**Context Source**: ACE semantic search + Per-agent CLI exploration
|
||||||
|
**Output Directory**: `.workflow/.planning/{session-id}/`
|
||||||
|
**Default Max Agents**: 5 (actual count based on requirement complexity)
|
||||||
|
**CLI Tools**: cli-lite-planning-agent (internally calls ccw cli with gemini/codex/qwen)
|
||||||
|
**Schema**: plan-json-schema.json (sub-plans & final plan share same base schema)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Unified collaborative planning workflow that:
|
||||||
|
|
||||||
|
1. **Analyzes** complex requirements and splits into sub-requirements
|
||||||
|
2. **Spawns** parallel sub-agents, each responsible for one sub-requirement
|
||||||
|
3. **Each agent** maintains process files: exploration.md → understanding.md → sub-plan.json
|
||||||
|
4. **Merges** all sub-plans into unified plan.json with conflict resolution
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ COLLABORATIVE PLANNING │
|
||||||
|
├─────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ Phase 1: Requirement Analysis & Splitting │
|
||||||
|
│ ├─ Analyze requirement complexity │
|
||||||
|
│ ├─ Identify 2-5 sub-requirements (focus areas) │
|
||||||
|
│ └─ Write requirement-analysis.json │
|
||||||
|
│ │
|
||||||
|
│ Phase 2: Parallel Sub-Agent Execution │
|
||||||
|
│ ┌──────────────┬──────────────┬──────────────┐ │
|
||||||
|
│ │ Agent 1 │ Agent 2 │ Agent N │ │
|
||||||
|
│ ├──────────────┼──────────────┼──────────────┤ │
|
||||||
|
│ │ exploration │ exploration │ exploration │ → exploration.md │
|
||||||
|
│ │ understanding│ understanding│ understanding│ → understanding.md │
|
||||||
|
│ │ planning │ planning │ planning │ → sub-plan.json │
|
||||||
|
│ └──────────────┴──────────────┴──────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Phase 3: Cross-Verification & Conflict Detection │
|
||||||
|
│ ├─ Load all sub-plan.json files │
|
||||||
|
│ ├─ Detect conflicts (effort, approach, dependencies) │
|
||||||
|
│ └─ Write conflicts.json │
|
||||||
|
│ │
|
||||||
|
│ Phase 4: Merge & Synthesis │
|
||||||
|
│ ├─ Resolve conflicts using merge-rule │
|
||||||
|
│ ├─ Merge all sub-plans into unified plan │
|
||||||
|
│ └─ Write plan.json + plan.md │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.planning/{CPLAN-slug-YYYY-MM-DD}/
|
||||||
|
├── requirement-analysis.json # Phase 1: Requirement breakdown
|
||||||
|
├── agents/ # Phase 2: Per-agent process files
|
||||||
|
│ ├── {focus-area-1}/
|
||||||
|
│ │ ├── exploration.md # What was discovered
|
||||||
|
│ │ ├── understanding.md # Synthesized insights
|
||||||
|
│ │ └── sub-plan.json # Agent's plan for this focus area
|
||||||
|
│ ├── {focus-area-2}/
|
||||||
|
│ │ └── ...
|
||||||
|
│ └── {focus-area-N}/
|
||||||
|
│ └── ...
|
||||||
|
├── conflicts.json # Phase 3: Detected conflicts
|
||||||
|
├── plan.json # Phase 4: Unified merged plan
|
||||||
|
└── plan.md # Phase 4: Human-readable plan
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Session Initialization
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
||||||
|
|
||||||
|
const taskDescription = "$ARGUMENTS"
|
||||||
|
const taskSlug = taskDescription.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
|
||||||
|
.substring(0, 30)
|
||||||
|
|
||||||
|
const sessionId = `CPLAN-${taskSlug}-${getUtc8ISOString().substring(0, 10)}`
|
||||||
|
const sessionFolder = `.workflow/.planning/${sessionId}`
|
||||||
|
|
||||||
|
// Parse options
|
||||||
|
const maxAgents = parseInt($ARGUMENTS.match(/--max-agents=(\d+)/)?.[1] || '5')
|
||||||
|
const depth = $ARGUMENTS.match(/--depth=(normal|deep)/)?.[1] || 'normal'
|
||||||
|
const mergeRule = $ARGUMENTS.match(/--merge-rule=(consensus|priority)/)?.[1] || 'consensus'
|
||||||
|
const autoMode = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
|
||||||
|
|
||||||
|
Bash(`mkdir -p ${sessionFolder}/agents`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 1: Requirement Analysis & Splitting
|
||||||
|
|
||||||
|
Use CLI to analyze and split requirements:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Requirement Analysis", status: "in_progress", activeForm: "Analyzing requirements" },
|
||||||
|
{ content: "Phase 2: Parallel Agent Execution", status: "pending", activeForm: "Running agents" },
|
||||||
|
{ content: "Phase 3: Conflict Detection", status: "pending", activeForm: "Detecting conflicts" },
|
||||||
|
{ content: "Phase 4: Merge & Synthesis", status: "pending", activeForm: "Merging plans" }
|
||||||
|
]})
|
||||||
|
|
||||||
|
// Step 1.1: Use CLI to analyze requirement and propose splits
|
||||||
|
Bash({
|
||||||
|
command: `ccw cli -p "
|
||||||
|
PURPOSE: Analyze requirement and identify distinct sub-requirements/focus areas
|
||||||
|
Success: 2-${maxAgents} clearly separated sub-requirements that can be planned independently
|
||||||
|
|
||||||
|
TASK:
|
||||||
|
• Understand the overall requirement: '${taskDescription}'
|
||||||
|
• Identify major components, features, or concerns
|
||||||
|
• Split into 2-${maxAgents} independent sub-requirements
|
||||||
|
• Each sub-requirement should be:
|
||||||
|
- Self-contained (can be planned independently)
|
||||||
|
- Non-overlapping (minimal dependency on other sub-requirements)
|
||||||
|
- Roughly equal in complexity
|
||||||
|
• For each sub-requirement, provide:
|
||||||
|
- focus_area: Short identifier (e.g., 'auth-backend', 'ui-components')
|
||||||
|
- description: What this sub-requirement covers
|
||||||
|
- key_concerns: Main challenges or considerations
|
||||||
|
- suggested_cli_tool: Which CLI tool is best suited (gemini/codex/qwen)
|
||||||
|
|
||||||
|
MODE: analysis
|
||||||
|
|
||||||
|
CONTEXT: @**/*
|
||||||
|
|
||||||
|
EXPECTED: JSON output with structure:
|
||||||
|
{
|
||||||
|
\"original_requirement\": \"...\",
|
||||||
|
\"complexity\": \"low|medium|high\",
|
||||||
|
\"sub_requirements\": [
|
||||||
|
{
|
||||||
|
\"index\": 1,
|
||||||
|
\"focus_area\": \"...\",
|
||||||
|
\"description\": \"...\",
|
||||||
|
\"key_concerns\": [\"...\"],
|
||||||
|
\"suggested_cli_tool\": \"gemini|codex|qwen\",
|
||||||
|
\"estimated_effort\": \"low|medium|high\"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\"dependencies_between_subs\": [
|
||||||
|
{ \"from\": 1, \"to\": 2, \"reason\": \"...\" }
|
||||||
|
],
|
||||||
|
\"rationale\": \"Why this split was chosen\"
|
||||||
|
}
|
||||||
|
|
||||||
|
CONSTRAINTS: Maximum ${maxAgents} sub-requirements | Ensure clear boundaries
|
||||||
|
" --tool gemini --mode analysis`,
|
||||||
|
run_in_background: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for CLI completion and parse result
|
||||||
|
// ... (hook callback will provide result)
|
||||||
|
```
|
||||||
|
|
||||||
|
**After CLI completes**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Parse CLI output to extract sub-requirements
|
||||||
|
const analysisResult = parseCLIOutput(cliOutput)
|
||||||
|
const subRequirements = analysisResult.sub_requirements
|
||||||
|
|
||||||
|
// Write requirement-analysis.json
|
||||||
|
Write(`${sessionFolder}/requirement-analysis.json`, JSON.stringify({
|
||||||
|
session_id: sessionId,
|
||||||
|
original_requirement: taskDescription,
|
||||||
|
analysis_timestamp: getUtc8ISOString(),
|
||||||
|
complexity: analysisResult.complexity,
|
||||||
|
sub_requirements: subRequirements,
|
||||||
|
dependencies_between_subs: analysisResult.dependencies_between_subs,
|
||||||
|
rationale: analysisResult.rationale,
|
||||||
|
options: { maxAgents, depth, mergeRule }
|
||||||
|
}, null, 2))
|
||||||
|
|
||||||
|
// Create agent folders
|
||||||
|
subRequirements.forEach(sub => {
|
||||||
|
Bash(`mkdir -p ${sessionFolder}/agents/${sub.focus_area}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// User confirmation (unless auto mode)
|
||||||
|
if (!autoMode) {
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: `已识别 ${subRequirements.length} 个子需求:\n${subRequirements.map((s, i) => `${i+1}. ${s.focus_area}: ${s.description}`).join('\n')}\n\n确认开始并行规划?`,
|
||||||
|
header: "Confirm Split",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "开始规划", description: "启动并行sub-agent" },
|
||||||
|
{ label: "调整拆分", description: "修改子需求划分" },
|
||||||
|
{ label: "取消", description: "退出规划" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Parallel Sub-Agent Execution
|
||||||
|
|
||||||
|
Launch one agent per sub-requirement, each maintaining its own process files:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Requirement Analysis", status: "completed", activeForm: "Analyzing requirements" },
|
||||||
|
{ content: "Phase 2: Parallel Agent Execution", status: "in_progress", activeForm: "Running agents" },
|
||||||
|
...subRequirements.map((sub, i) => ({
|
||||||
|
content: ` → Agent ${i+1}: ${sub.focus_area}`,
|
||||||
|
status: "pending",
|
||||||
|
activeForm: `Planning ${sub.focus_area}`
|
||||||
|
})),
|
||||||
|
{ content: "Phase 3: Conflict Detection", status: "pending", activeForm: "Detecting conflicts" },
|
||||||
|
{ content: "Phase 4: Merge & Synthesis", status: "pending", activeForm: "Merging plans" }
|
||||||
|
]})
|
||||||
|
|
||||||
|
// Launch all sub-agents in parallel
|
||||||
|
const agentPromises = subRequirements.map((sub, index) => {
|
||||||
|
return Task({
|
||||||
|
subagent_type: "cli-lite-planning-agent",
|
||||||
|
run_in_background: false,
|
||||||
|
description: `Plan: ${sub.focus_area}`,
|
||||||
|
prompt: `
|
||||||
|
## Sub-Agent Mission
|
||||||
|
|
||||||
|
You are responsible for planning ONE sub-requirement of a larger task.
|
||||||
|
Maintain 3 process files documenting your exploration, understanding, and plan.
|
||||||
|
|
||||||
|
## Your Focus Area
|
||||||
|
|
||||||
|
**Index**: ${index + 1}
|
||||||
|
**Focus Area**: ${sub.focus_area}
|
||||||
|
**Description**: ${sub.description}
|
||||||
|
**Key Concerns**: ${sub.key_concerns.join(', ')}
|
||||||
|
**Suggested Tool**: ${sub.suggested_cli_tool}
|
||||||
|
**Depth**: ${depth}
|
||||||
|
|
||||||
|
## Parent Context
|
||||||
|
|
||||||
|
**Original Requirement**: ${taskDescription}
|
||||||
|
**Session**: ${sessionId}
|
||||||
|
**Your Folder**: ${sessionFolder}/agents/${sub.focus_area}/
|
||||||
|
|
||||||
|
## Schema Reference
|
||||||
|
|
||||||
|
Execute: cat ~/.claude/workflows/cli-templates/schemas/plan-json-schema.json
|
||||||
|
|
||||||
|
## Execution Process (3 Stages)
|
||||||
|
|
||||||
|
### Stage 1: Exploration (→ exploration.md)
|
||||||
|
|
||||||
|
Use ccw cli to explore the codebase for your focus area:
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
ccw cli -p "
|
||||||
|
PURPOSE: Explore codebase for ${sub.focus_area}
|
||||||
|
Success: Identify all relevant files, patterns, and constraints
|
||||||
|
|
||||||
|
TASK:
|
||||||
|
• Find existing code related to ${sub.focus_area}
|
||||||
|
• Identify current architecture and patterns
|
||||||
|
• Discover integration points and dependencies
|
||||||
|
• Note any constraints or limitations
|
||||||
|
|
||||||
|
CONTEXT: @**/*
|
||||||
|
MODE: analysis
|
||||||
|
" --tool ${sub.suggested_cli_tool} --mode analysis
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
After CLI completes, write exploration.md:
|
||||||
|
|
||||||
|
\`\`\`markdown
|
||||||
|
# Exploration: ${sub.focus_area}
|
||||||
|
|
||||||
|
## Discovered Files
|
||||||
|
- [list relevant files with brief descriptions]
|
||||||
|
|
||||||
|
## Current Architecture
|
||||||
|
- [describe existing structure]
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
- [list dependencies and interfaces]
|
||||||
|
|
||||||
|
## Constraints & Limitations
|
||||||
|
- [note any blockers or constraints]
|
||||||
|
|
||||||
|
## Key Findings
|
||||||
|
- [most important discoveries]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Stage 2: Understanding (→ understanding.md)
|
||||||
|
|
||||||
|
Synthesize exploration findings into actionable understanding:
|
||||||
|
|
||||||
|
\`\`\`markdown
|
||||||
|
# Understanding: ${sub.focus_area}
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
[Restate what needs to be done for this focus area]
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
[What exists now, what's missing]
|
||||||
|
|
||||||
|
## Proposed Approach
|
||||||
|
[High-level strategy for implementation]
|
||||||
|
|
||||||
|
## Technical Decisions
|
||||||
|
- Decision 1: [choice] because [rationale]
|
||||||
|
- Decision 2: [choice] because [rationale]
|
||||||
|
|
||||||
|
## Risks & Mitigations
|
||||||
|
- Risk 1: [description] → Mitigation: [approach]
|
||||||
|
|
||||||
|
## Dependencies on Other Sub-Requirements
|
||||||
|
- Depends on: [list any dependencies on other focus areas]
|
||||||
|
- Provides for: [what this provides to other focus areas]
|
||||||
|
|
||||||
|
## Key Insights
|
||||||
|
[Most important understanding gained]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Stage 3: Planning (→ sub-plan.json)
|
||||||
|
|
||||||
|
Generate sub-plan.json following plan-json-schema.json:
|
||||||
|
- 2-5 tasks for this focus area
|
||||||
|
- Clear modification_points with file, target, change
|
||||||
|
- Verification criteria (unit_tests, integration_tests, manual_checks)
|
||||||
|
- Risk assessment per task
|
||||||
|
- Dependencies (internal to this sub-plan)
|
||||||
|
- Add source_agent: "${sub.focus_area}" to _metadata
|
||||||
|
|
||||||
|
## Output Requirements
|
||||||
|
|
||||||
|
Write exactly 3 files:
|
||||||
|
1. \`${sessionFolder}/agents/${sub.focus_area}/exploration.md\`
|
||||||
|
2. \`${sessionFolder}/agents/${sub.focus_area}/understanding.md\`
|
||||||
|
3. \`${sessionFolder}/agents/${sub.focus_area}/sub-plan.json\`
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [ ] Exploration covers all relevant code areas
|
||||||
|
- [ ] Understanding synthesizes findings into clear approach
|
||||||
|
- [ ] sub-plan.json follows plan-json-schema.json with 2-5 tasks
|
||||||
|
- [ ] Each task has modification_points, acceptance, verification
|
||||||
|
- [ ] Dependencies clearly stated
|
||||||
|
- [ ] All 3 files written to correct location
|
||||||
|
`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for all agents to complete
|
||||||
|
const agentResults = await Promise.all(agentPromises)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Cross-Verification & Conflict Detection
|
||||||
|
|
||||||
|
Load all sub-plans and detect conflicts:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Requirement Analysis", status: "completed", activeForm: "Analyzing requirements" },
|
||||||
|
{ content: "Phase 2: Parallel Agent Execution", status: "completed", activeForm: "Running agents" },
|
||||||
|
{ content: "Phase 3: Conflict Detection", status: "in_progress", activeForm: "Detecting conflicts" },
|
||||||
|
{ content: "Phase 4: Merge & Synthesis", status: "pending", activeForm: "Merging plans" }
|
||||||
|
]})
|
||||||
|
|
||||||
|
// Load all sub-plans
|
||||||
|
const subPlans = subRequirements.map(sub => {
|
||||||
|
const planPath = `${sessionFolder}/agents/${sub.focus_area}/sub-plan.json`
|
||||||
|
const content = Read(planPath)
|
||||||
|
return {
|
||||||
|
focus_area: sub.focus_area,
|
||||||
|
index: sub.index,
|
||||||
|
plan: JSON.parse(content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Detect conflicts
|
||||||
|
const conflicts = {
|
||||||
|
detected_at: getUtc8ISOString(),
|
||||||
|
total_sub_plans: subPlans.length,
|
||||||
|
conflicts: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Effort conflicts (same task estimated differently)
|
||||||
|
const effortConflicts = detectEffortConflicts(subPlans)
|
||||||
|
conflicts.conflicts.push(...effortConflicts)
|
||||||
|
|
||||||
|
// 2. File conflicts (multiple agents modifying same file)
|
||||||
|
const fileConflicts = detectFileConflicts(subPlans)
|
||||||
|
conflicts.conflicts.push(...fileConflicts)
|
||||||
|
|
||||||
|
// 3. Approach conflicts (different approaches to same problem)
|
||||||
|
const approachConflicts = detectApproachConflicts(subPlans)
|
||||||
|
conflicts.conflicts.push(...approachConflicts)
|
||||||
|
|
||||||
|
// 4. Dependency conflicts (circular or missing dependencies)
|
||||||
|
const dependencyConflicts = detectDependencyConflicts(subPlans)
|
||||||
|
conflicts.conflicts.push(...dependencyConflicts)
|
||||||
|
|
||||||
|
// Write conflicts.json
|
||||||
|
Write(`${sessionFolder}/conflicts.json`, JSON.stringify(conflicts, null, 2))
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Conflict Detection Complete
|
||||||
|
|
||||||
|
**Total Sub-Plans**: ${subPlans.length}
|
||||||
|
**Conflicts Found**: ${conflicts.conflicts.length}
|
||||||
|
|
||||||
|
${conflicts.conflicts.length > 0 ? `
|
||||||
|
### Conflicts:
|
||||||
|
${conflicts.conflicts.map((c, i) => `
|
||||||
|
${i+1}. **${c.type}** (${c.severity})
|
||||||
|
- Agents: ${c.agents_involved.join(' vs ')}
|
||||||
|
- Issue: ${c.description}
|
||||||
|
- Suggested Resolution: ${c.suggested_resolution}
|
||||||
|
`).join('\n')}
|
||||||
|
` : '✅ No conflicts detected - sub-plans are compatible'}
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Conflict Detection Functions**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function detectFileConflicts(subPlans) {
|
||||||
|
const fileModifications = {}
|
||||||
|
const conflicts = []
|
||||||
|
|
||||||
|
subPlans.forEach(sp => {
|
||||||
|
sp.plan.tasks.forEach(task => {
|
||||||
|
task.modification_points?.forEach(mp => {
|
||||||
|
if (!fileModifications[mp.file]) {
|
||||||
|
fileModifications[mp.file] = []
|
||||||
|
}
|
||||||
|
fileModifications[mp.file].push({
|
||||||
|
focus_area: sp.focus_area,
|
||||||
|
task_id: task.id,
|
||||||
|
target: mp.target,
|
||||||
|
change: mp.change
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.entries(fileModifications).forEach(([file, mods]) => {
|
||||||
|
if (mods.length > 1) {
|
||||||
|
const agents = [...new Set(mods.map(m => m.focus_area))]
|
||||||
|
if (agents.length > 1) {
|
||||||
|
conflicts.push({
|
||||||
|
type: "file_conflict",
|
||||||
|
severity: "high",
|
||||||
|
file: file,
|
||||||
|
agents_involved: agents,
|
||||||
|
modifications: mods,
|
||||||
|
description: `Multiple agents modifying ${file}`,
|
||||||
|
suggested_resolution: "Sequence modifications or consolidate"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return conflicts
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectEffortConflicts(subPlans) {
|
||||||
|
// Compare effort estimates across similar tasks
|
||||||
|
// Return conflicts where estimates differ by >50%
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectApproachConflicts(subPlans) {
|
||||||
|
// Analyze approaches for contradictions
|
||||||
|
// Return conflicts where approaches are incompatible
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectDependencyConflicts(subPlans) {
|
||||||
|
// Check for circular dependencies
|
||||||
|
// Check for missing dependencies
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Merge & Synthesis
|
||||||
|
|
||||||
|
Use cli-lite-planning-agent to merge all sub-plans:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Requirement Analysis", status: "completed", activeForm: "Analyzing requirements" },
|
||||||
|
{ content: "Phase 2: Parallel Agent Execution", status: "completed", activeForm: "Running agents" },
|
||||||
|
{ content: "Phase 3: Conflict Detection", status: "completed", activeForm: "Detecting conflicts" },
|
||||||
|
{ content: "Phase 4: Merge & Synthesis", status: "in_progress", activeForm: "Merging plans" }
|
||||||
|
]})
|
||||||
|
|
||||||
|
// Collect all understanding documents for context
|
||||||
|
const understandingDocs = subRequirements.map(sub => {
|
||||||
|
const path = `${sessionFolder}/agents/${sub.focus_area}/understanding.md`
|
||||||
|
return {
|
||||||
|
focus_area: sub.focus_area,
|
||||||
|
content: Read(path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Invoke planning agent to merge
|
||||||
|
Task({
|
||||||
|
subagent_type: "cli-lite-planning-agent",
|
||||||
|
run_in_background: false,
|
||||||
|
description: "Merge sub-plans into unified plan",
|
||||||
|
prompt: `
|
||||||
|
## Mission: Merge Multiple Sub-Plans
|
||||||
|
|
||||||
|
Merge ${subPlans.length} sub-plans into a single unified plan.
|
||||||
|
|
||||||
|
## Schema Reference
|
||||||
|
|
||||||
|
Execute: cat ~/.claude/workflows/cli-templates/schemas/plan-json-schema.json
|
||||||
|
|
||||||
|
The merged plan follows the SAME schema as lite-plan, with ONE additional field:
|
||||||
|
- \`merge_metadata\`: Object containing merge-specific information
|
||||||
|
|
||||||
|
## Project Context
|
||||||
|
|
||||||
|
1. Read: .workflow/project-tech.json
|
||||||
|
2. Read: .workflow/project-guidelines.json
|
||||||
|
|
||||||
|
## Original Requirement
|
||||||
|
|
||||||
|
${taskDescription}
|
||||||
|
|
||||||
|
## Sub-Plans to Merge
|
||||||
|
|
||||||
|
${subPlans.map(sp => `
|
||||||
|
### Sub-Plan: ${sp.focus_area}
|
||||||
|
\`\`\`json
|
||||||
|
${JSON.stringify(sp.plan, null, 2)}
|
||||||
|
\`\`\`
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Understanding Documents
|
||||||
|
|
||||||
|
${understandingDocs.map(ud => `
|
||||||
|
### Understanding: ${ud.focus_area}
|
||||||
|
${ud.content}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Detected Conflicts
|
||||||
|
|
||||||
|
\`\`\`json
|
||||||
|
${JSON.stringify(conflicts, null, 2)}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Merge Rules
|
||||||
|
|
||||||
|
**Rule**: ${mergeRule}
|
||||||
|
${mergeRule === 'consensus' ? `
|
||||||
|
- Equal weight to all sub-plans
|
||||||
|
- Conflicts resolved by finding middle ground
|
||||||
|
- Combine overlapping tasks
|
||||||
|
` : `
|
||||||
|
- Priority based on sub-requirement index
|
||||||
|
- Earlier agents' decisions take precedence
|
||||||
|
- Later agents adapt to earlier decisions
|
||||||
|
`}
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
1. **Task Consolidation**:
|
||||||
|
- Combine tasks that modify same files
|
||||||
|
- Preserve unique tasks from each sub-plan
|
||||||
|
- Ensure no task duplication
|
||||||
|
- Maintain clear task boundaries
|
||||||
|
|
||||||
|
2. **Dependency Resolution**:
|
||||||
|
- Cross-reference dependencies between sub-plans
|
||||||
|
- Create global task ordering
|
||||||
|
- Handle inter-sub-plan dependencies
|
||||||
|
|
||||||
|
3. **Conflict Resolution**:
|
||||||
|
- Apply ${mergeRule} rule to resolve conflicts
|
||||||
|
- Document resolution rationale
|
||||||
|
- Ensure no contradictions in final plan
|
||||||
|
|
||||||
|
4. **Metadata Preservation**:
|
||||||
|
- Track which sub-plan each task originated from (source_agent field)
|
||||||
|
- Include merge_metadata with:
|
||||||
|
- merged_from: list of sub-plan focus areas
|
||||||
|
- conflicts_resolved: count
|
||||||
|
- merge_rule: ${mergeRule}
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Write to ${sessionFolder}/plan.json following plan-json-schema.json.
|
||||||
|
|
||||||
|
Add ONE extension field for merge tracking:
|
||||||
|
|
||||||
|
\`\`\`json
|
||||||
|
{
|
||||||
|
// ... all standard plan-json-schema fields ...
|
||||||
|
|
||||||
|
"merge_metadata": {
|
||||||
|
"source_session": "${sessionId}",
|
||||||
|
"merged_from": ["focus-area-1", "focus-area-2"],
|
||||||
|
"sub_plan_count": N,
|
||||||
|
"conflicts_detected": N,
|
||||||
|
"conflicts_resolved": N,
|
||||||
|
"merge_rule": "${mergeRule}",
|
||||||
|
"merged_at": "ISO-timestamp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Each task should include \`source_agent\` field indicating which sub-plan it originated from.
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [ ] All sub-plan tasks included (or explicitly merged)
|
||||||
|
- [ ] Conflicts resolved per ${mergeRule} rule
|
||||||
|
- [ ] Dependencies form valid DAG (no cycles)
|
||||||
|
- [ ] merge_metadata present
|
||||||
|
- [ ] Schema compliance verified
|
||||||
|
- [ ] plan.json written to ${sessionFolder}/plan.json
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate human-readable plan.md
|
||||||
|
const plan = JSON.parse(Read(`${sessionFolder}/plan.json`))
|
||||||
|
const planMd = generatePlanMarkdown(plan, subRequirements, conflicts)
|
||||||
|
Write(`${sessionFolder}/plan.md`, planMd)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Markdown Generation**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function generatePlanMarkdown(plan, subRequirements, conflicts) {
|
||||||
|
return `# Collaborative Planning Session
|
||||||
|
|
||||||
|
**Session ID**: ${plan._metadata?.session_id || sessionId}
|
||||||
|
**Original Requirement**: ${taskDescription}
|
||||||
|
**Created**: ${getUtc8ISOString()}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sub-Requirements Analyzed
|
||||||
|
|
||||||
|
${subRequirements.map((sub, i) => `
|
||||||
|
### ${i+1}. ${sub.focus_area}
|
||||||
|
${sub.description}
|
||||||
|
- **Key Concerns**: ${sub.key_concerns.join(', ')}
|
||||||
|
- **Estimated Effort**: ${sub.estimated_effort}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conflict Resolution
|
||||||
|
|
||||||
|
${conflicts.conflicts.length > 0 ? `
|
||||||
|
**Conflicts Detected**: ${conflicts.conflicts.length}
|
||||||
|
**Merge Rule**: ${mergeRule}
|
||||||
|
|
||||||
|
${conflicts.conflicts.map((c, i) => `
|
||||||
|
${i+1}. **${c.type}** - ${c.description}
|
||||||
|
- Resolution: ${c.suggested_resolution}
|
||||||
|
`).join('\n')}
|
||||||
|
` : '✅ No conflicts detected'}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Merged Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
${plan.summary}
|
||||||
|
|
||||||
|
### Approach
|
||||||
|
${plan.approach}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
${plan.tasks.map((task, i) => `
|
||||||
|
### ${task.id}: ${task.title}
|
||||||
|
|
||||||
|
**Source**: ${task.source_agent || 'merged'}
|
||||||
|
**Scope**: ${task.scope}
|
||||||
|
**Action**: ${task.action}
|
||||||
|
**Complexity**: ${task.effort?.complexity || 'medium'}
|
||||||
|
|
||||||
|
${task.description}
|
||||||
|
|
||||||
|
**Modification Points**:
|
||||||
|
${task.modification_points?.map(mp => `- \`${mp.file}\` → ${mp.target}: ${mp.change}`).join('\n') || 'N/A'}
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
${task.implementation?.map((step, idx) => `${idx+1}. ${step}`).join('\n') || 'N/A'}
|
||||||
|
|
||||||
|
**Acceptance Criteria**:
|
||||||
|
${task.acceptance?.map(ac => `- ${ac}`).join('\n') || 'N/A'}
|
||||||
|
|
||||||
|
**Dependencies**: ${task.depends_on?.join(', ') || 'None'}
|
||||||
|
|
||||||
|
---
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
# Execute this plan
|
||||||
|
/workflow:unified-execute-with-file -p ${sessionFolder}/plan.json
|
||||||
|
|
||||||
|
# Or with auto-confirmation
|
||||||
|
/workflow:unified-execute-with-file -y -p ${sessionFolder}/plan.json
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Agent Process Files
|
||||||
|
|
||||||
|
${subRequirements.map(sub => `
|
||||||
|
### ${sub.focus_area}
|
||||||
|
- Exploration: \`${sessionFolder}/agents/${sub.focus_area}/exploration.md\`
|
||||||
|
- Understanding: \`${sessionFolder}/agents/${sub.focus_area}/understanding.md\`
|
||||||
|
- Sub-Plan: \`${sessionFolder}/agents/${sub.focus_area}/sub-plan.json\`
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Generated by**: /workflow:collaborative-plan
|
||||||
|
**Merge Rule**: ${mergeRule}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Completion
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TodoWrite({ todos: [
|
||||||
|
{ content: "Phase 1: Requirement Analysis", status: "completed", activeForm: "Analyzing requirements" },
|
||||||
|
{ content: "Phase 2: Parallel Agent Execution", status: "completed", activeForm: "Running agents" },
|
||||||
|
{ content: "Phase 3: Conflict Detection", status: "completed", activeForm: "Detecting conflicts" },
|
||||||
|
{ content: "Phase 4: Merge & Synthesis", status: "completed", activeForm: "Merging plans" }
|
||||||
|
]})
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
✅ Collaborative Planning Complete
|
||||||
|
|
||||||
|
**Session**: ${sessionId}
|
||||||
|
**Sub-Agents**: ${subRequirements.length}
|
||||||
|
**Conflicts Resolved**: ${conflicts.conflicts.length}
|
||||||
|
|
||||||
|
## Output Files
|
||||||
|
|
||||||
|
📁 ${sessionFolder}/
|
||||||
|
├── requirement-analysis.json # Requirement breakdown
|
||||||
|
├── agents/ # Per-agent process files
|
||||||
|
${subRequirements.map(sub => `│ ├── ${sub.focus_area}/
|
||||||
|
│ │ ├── exploration.md
|
||||||
|
│ │ ├── understanding.md
|
||||||
|
│ │ └── sub-plan.json`).join('\n')}
|
||||||
|
├── conflicts.json # Detected conflicts
|
||||||
|
├── plan.json # Unified plan (execution-ready)
|
||||||
|
└── plan.md # Human-readable plan
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Execute the plan:
|
||||||
|
\`\`\`bash
|
||||||
|
/workflow:unified-execute-with-file -p ${sessionFolder}/plan.json
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Review a specific agent's work:
|
||||||
|
\`\`\`bash
|
||||||
|
cat ${sessionFolder}/agents/{focus-area}/understanding.md
|
||||||
|
\`\`\`
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Flag | Default | Description |
|
||||||
|
|------|---------|-------------|
|
||||||
|
| `--max-agents` | 5 | Maximum sub-agents to spawn |
|
||||||
|
| `--depth` | normal | Exploration depth: normal or deep |
|
||||||
|
| `--merge-rule` | consensus | Conflict resolution: consensus or priority |
|
||||||
|
| `-y, --yes` | false | Auto-confirm all decisions |
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Resolution |
|
||||||
|
|-------|------------|
|
||||||
|
| Requirement too simple | Use single-agent lite-plan instead |
|
||||||
|
| Agent fails | Retry once, then continue with partial results |
|
||||||
|
| Merge conflicts unresolvable | Ask user for manual resolution |
|
||||||
|
| CLI timeout | Use fallback CLI tool |
|
||||||
|
| File write fails | Retry with alternative path |
|
||||||
|
|
||||||
|
## vs Other Planning Commands
|
||||||
|
|
||||||
|
| Command | Use Case |
|
||||||
|
|---------|----------|
|
||||||
|
| **collaborative-plan** | Complex multi-aspect requirements needing parallel exploration |
|
||||||
|
| lite-plan | Simple single-focus tasks |
|
||||||
|
| multi-cli-plan | Iterative cross-verification with convergence |
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Be Specific**: Detailed requirements lead to better splits
|
||||||
|
2. **Review Process Files**: Check exploration.md and understanding.md for insights
|
||||||
|
3. **Trust the Merge**: Conflict resolution follows defined rules
|
||||||
|
4. **Iterate if Needed**: Re-run with different --merge-rule if results unsatisfactory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Now execute collaborative-plan for**: $ARGUMENTS
|
||||||
@@ -1,807 +0,0 @@
|
|||||||
---
|
|
||||||
name: merge-plans-with-file
|
|
||||||
description: Merge multiple planning/brainstorm/analysis outputs, resolve conflicts, and synthesize unified plan. Designed for multi-team input aggregation and final plan crystallization
|
|
||||||
argument-hint: "[-y|--yes] [-r|--rule consensus|priority|hierarchy] \"plan or topic name\""
|
|
||||||
allowed-tools: TodoWrite(*), Task(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*)
|
|
||||||
---
|
|
||||||
|
|
||||||
## Auto Mode
|
|
||||||
|
|
||||||
When `--yes` or `-y`: Auto-resolve conflicts using specified rule (consensus/priority/hierarchy), minimal user prompts.
|
|
||||||
|
|
||||||
# Workflow Merge-Plans-With-File Command (/workflow:merge-plans-with-file)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Plan aggregation and conflict resolution workflow. Takes multiple planning artifacts (brainstorm conclusions, analysis recommendations, quick-plans, implementation plans) and synthesizes them into a unified, conflict-resolved execution plan.
|
|
||||||
|
|
||||||
**Core workflow**: Load Sources → Parse Plans → Conflict Analysis → Arbitration → Unified Plan
|
|
||||||
|
|
||||||
**Key features**:
|
|
||||||
- **Multi-Source Support**: Brainstorm, analysis, quick-plan, IMPL_PLAN, task JSONs
|
|
||||||
- **Parallel Conflict Detection**: Identify all contradictions across input plans
|
|
||||||
- **Conflict Resolution**: Consensus, priority-based, or hierarchical resolution modes
|
|
||||||
- **Unified Synthesis**: Single authoritative plan from multiple perspectives
|
|
||||||
- **Decision Tracking**: Full audit trail of conflicts and resolutions
|
|
||||||
- **Resumable**: Save intermediate states, refine resolutions
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/workflow:merge-plans-with-file [FLAGS] <PLAN_NAME_OR_PATTERN>
|
|
||||||
|
|
||||||
# Flags
|
|
||||||
-y, --yes Auto-resolve conflicts using rule, skip confirmations
|
|
||||||
-r, --rule <rule> Conflict resolution rule: consensus (default) | priority | hierarchy
|
|
||||||
-o, --output <path> Output directory (default: .workflow/.merged/{name})
|
|
||||||
|
|
||||||
# Arguments
|
|
||||||
<plan-name-or-pattern> Plan name or glob pattern to identify input files/sessions
|
|
||||||
Examples: "auth-module", "*.analysis-*.json", "PLAN-*"
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
/workflow:merge-plans-with-file "authentication" # Auto-detect all auth-related plans
|
|
||||||
/workflow:merge-plans-with-file -y -r priority "payment-system" # Auto-resolve with priority rule
|
|
||||||
/workflow:merge-plans-with-file -r hierarchy "feature-complete" # Use hierarchy rule (requires user ranking)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Execution Process
|
|
||||||
|
|
||||||
```
|
|
||||||
Discovery & Loading:
|
|
||||||
├─ Search for planning artifacts matching pattern
|
|
||||||
├─ Load all synthesis.json, conclusions.json, IMPL_PLAN.md
|
|
||||||
├─ Parse each into normalized task/plan structure
|
|
||||||
└─ Validate data completeness
|
|
||||||
|
|
||||||
Session Initialization:
|
|
||||||
├─ Create .workflow/.merged/{sessionId}/
|
|
||||||
├─ Initialize merge.md with plan summary
|
|
||||||
├─ Index all source plans
|
|
||||||
└─ Extract planning metadata and constraints
|
|
||||||
|
|
||||||
Phase 1: Plan Normalization
|
|
||||||
├─ Convert all formats to common task representation
|
|
||||||
├─ Extract tasks, dependencies, effort, risks
|
|
||||||
├─ Identify plan scope and boundaries
|
|
||||||
├─ Validate no duplicate tasks
|
|
||||||
└─ Aggregate recommendations from each plan
|
|
||||||
|
|
||||||
Phase 2: Conflict Detection (Parallel)
|
|
||||||
├─ Architecture conflicts: different design approaches
|
|
||||||
├─ Task conflicts: overlapping responsibilities or different priorities
|
|
||||||
├─ Effort conflicts: vastly different estimates
|
|
||||||
├─ Risk assessment conflicts: different risk levels
|
|
||||||
├─ Scope conflicts: different feature inclusions
|
|
||||||
└─ Generate conflict matrix with severity levels
|
|
||||||
|
|
||||||
Phase 3: Consensus Building / Arbitration
|
|
||||||
├─ For each conflict, analyze source rationale
|
|
||||||
├─ Apply resolution rule (consensus/priority/hierarchy)
|
|
||||||
├─ Escalate unresolvable conflicts to user (unless --yes)
|
|
||||||
├─ Document decision rationale
|
|
||||||
└─ Generate resolutions.json
|
|
||||||
|
|
||||||
Phase 4: Plan Synthesis
|
|
||||||
├─ Merge task lists (remove duplicates, combine insights)
|
|
||||||
├─ Integrate dependencies from all sources
|
|
||||||
├─ Consolidate effort and risk estimates
|
|
||||||
├─ Generate unified execution sequence
|
|
||||||
├─ Create final unified plan
|
|
||||||
└─ Output ready for execution
|
|
||||||
|
|
||||||
Output:
|
|
||||||
├─ .workflow/.merged/{sessionId}/merge.md (merge process & decisions)
|
|
||||||
├─ .workflow/.merged/{sessionId}/source-index.json (input sources)
|
|
||||||
├─ .workflow/.merged/{sessionId}/conflicts.json (conflict matrix)
|
|
||||||
├─ .workflow/.merged/{sessionId}/resolutions.json (how conflicts were resolved)
|
|
||||||
├─ .workflow/.merged/{sessionId}/unified-plan.json (final merged plan)
|
|
||||||
└─ .workflow/.merged/{sessionId}/unified-plan.md (execution-ready markdown)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### Phase 1: Plan Discovery & Loading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
|
||||||
|
|
||||||
// Parse arguments
|
|
||||||
const planPattern = "$PLAN_NAME_OR_PATTERN"
|
|
||||||
const resolutionRule = $ARGUMENTS.match(/--rule\s+(\w+)/)?.[1] || 'consensus'
|
|
||||||
const isAutoMode = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
|
|
||||||
|
|
||||||
// Generate session ID
|
|
||||||
const mergeSlug = planPattern.toLowerCase()
|
|
||||||
.replace(/[*?]/g, '-')
|
|
||||||
.replace(/[^a-z0-9\u4e00-\u9fa5-]+/g, '-')
|
|
||||||
.substring(0, 30)
|
|
||||||
const dateStr = getUtc8ISOString().substring(0, 10)
|
|
||||||
const sessionId = `MERGE-${mergeSlug}-${dateStr}`
|
|
||||||
const sessionFolder = `.workflow/.merged/${sessionId}`
|
|
||||||
|
|
||||||
bash(`mkdir -p ${sessionFolder}`)
|
|
||||||
|
|
||||||
// Discover all relevant planning artifacts
|
|
||||||
const discoveryPaths = [
|
|
||||||
`.workflow/.brainstorm/*/${planPattern}*/synthesis.json`,
|
|
||||||
`.workflow/.analysis/*/${planPattern}*/conclusions.json`,
|
|
||||||
`.workflow/.planning/*/${planPattern}*/synthesis.json`,
|
|
||||||
`.workflow/.plan/${planPattern}*IMPL_PLAN.md`,
|
|
||||||
`.workflow/*/${planPattern}*.json`
|
|
||||||
]
|
|
||||||
|
|
||||||
const sourcePlans = []
|
|
||||||
|
|
||||||
for (const pattern of discoveryPaths) {
|
|
||||||
const matches = glob(pattern)
|
|
||||||
for (const path of matches) {
|
|
||||||
try {
|
|
||||||
const content = Read(path)
|
|
||||||
const plan = parsePlanFile(path, content)
|
|
||||||
if (plan && plan.tasks?.length > 0) {
|
|
||||||
sourcePlans.push({
|
|
||||||
source_path: path,
|
|
||||||
source_type: identifySourceType(path),
|
|
||||||
plan: plan,
|
|
||||||
loaded_at: getUtc8ISOString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(`Failed to load plan from ${path}: ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourcePlans.length === 0) {
|
|
||||||
console.error(`
|
|
||||||
## Error: No Plans Found
|
|
||||||
|
|
||||||
Pattern: ${planPattern}
|
|
||||||
Searched locations:
|
|
||||||
${discoveryPaths.join('\n')}
|
|
||||||
|
|
||||||
Available plans in .workflow/:
|
|
||||||
`)
|
|
||||||
bash(`find .workflow -name "*.json" -o -name "*PLAN.md" | head -20`)
|
|
||||||
return { status: 'error', message: 'No plans found' }
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
## Plans Discovered
|
|
||||||
|
|
||||||
Total: ${sourcePlans.length}
|
|
||||||
${sourcePlans.map(sp => `- ${sp.source_type}: ${sp.source_path}`).join('\n')}
|
|
||||||
`)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 2: Plan Normalization
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Normalize all plans to common format
|
|
||||||
const normalizedPlans = sourcePlans.map((sourcePlan, idx) => {
|
|
||||||
const plan = sourcePlan.plan
|
|
||||||
const tasks = plan.tasks || []
|
|
||||||
|
|
||||||
return {
|
|
||||||
index: idx,
|
|
||||||
source: sourcePlan.source_path,
|
|
||||||
source_type: sourcePlan.source_type,
|
|
||||||
|
|
||||||
metadata: {
|
|
||||||
title: plan.title || `Plan ${idx + 1}`,
|
|
||||||
topic: plan.topic || plan.planning_topic || 'unknown',
|
|
||||||
timestamp: plan.completed || plan.timestamp || sourcePlan.loaded_at,
|
|
||||||
source_ideas: plan.top_ideas?.length || 0,
|
|
||||||
complexity: plan.complexity_level || 'unknown'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Normalized tasks
|
|
||||||
tasks: tasks.map(task => ({
|
|
||||||
id: task.id || `T${idx}-${task.title?.substring(0, 20)}`,
|
|
||||||
title: task.title || task.content,
|
|
||||||
description: task.description || '',
|
|
||||||
type: task.type || inferType(task),
|
|
||||||
priority: task.priority || 'normal',
|
|
||||||
|
|
||||||
// Effort estimation
|
|
||||||
effort: {
|
|
||||||
estimated: task.estimated_duration || task.effort_estimate || 'unknown',
|
|
||||||
from_plan: idx
|
|
||||||
},
|
|
||||||
|
|
||||||
// Risk assessment
|
|
||||||
risk: {
|
|
||||||
level: task.risk_level || 'medium',
|
|
||||||
from_plan: idx
|
|
||||||
},
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
dependencies: task.dependencies || [],
|
|
||||||
|
|
||||||
// Source tracking
|
|
||||||
source_plan_index: idx,
|
|
||||||
original_id: task.id,
|
|
||||||
|
|
||||||
// Quality tracking
|
|
||||||
success_criteria: task.success_criteria || [],
|
|
||||||
challenges: task.challenges || []
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Save source index
|
|
||||||
const sourceIndex = {
|
|
||||||
session_id: sessionId,
|
|
||||||
merge_timestamp: getUtc8ISOString(),
|
|
||||||
pattern: planPattern,
|
|
||||||
total_source_plans: sourcePlans.length,
|
|
||||||
|
|
||||||
sources: normalizedPlans.map(p => ({
|
|
||||||
index: p.index,
|
|
||||||
source_path: p.source,
|
|
||||||
source_type: p.source_type,
|
|
||||||
topic: p.metadata.topic,
|
|
||||||
task_count: p.tasks.length
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/source-index.json`, JSON.stringify(sourceIndex, null, 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 3: Conflict Detection
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Detect conflicts across plans
|
|
||||||
const conflictDetector = {
|
|
||||||
// Architecture conflicts
|
|
||||||
architectureConflicts: [],
|
|
||||||
|
|
||||||
// Task conflicts (duplicates, different scope)
|
|
||||||
taskConflicts: [],
|
|
||||||
|
|
||||||
// Effort conflicts
|
|
||||||
effortConflicts: [],
|
|
||||||
|
|
||||||
// Risk assessment conflicts
|
|
||||||
riskConflicts: [],
|
|
||||||
|
|
||||||
// Scope conflicts
|
|
||||||
scopeConflicts: [],
|
|
||||||
|
|
||||||
// Priority conflicts
|
|
||||||
priorityConflicts: []
|
|
||||||
}
|
|
||||||
|
|
||||||
// Algorithm 1: Detect similar tasks across plans
|
|
||||||
const allTasks = normalizedPlans.flatMap(p => p.tasks)
|
|
||||||
const taskGroups = groupSimilarTasks(allTasks)
|
|
||||||
|
|
||||||
for (const group of taskGroups) {
|
|
||||||
if (group.tasks.length > 1) {
|
|
||||||
// Same task appears in multiple plans
|
|
||||||
const efforts = group.tasks.map(t => t.effort.estimated)
|
|
||||||
const effortVariance = calculateVariance(efforts)
|
|
||||||
|
|
||||||
if (effortVariance > 0.5) {
|
|
||||||
// Significant difference in effort estimates
|
|
||||||
conflictDetector.effortConflicts.push({
|
|
||||||
task_group: group.title,
|
|
||||||
conflicting_tasks: group.tasks.map((t, i) => ({
|
|
||||||
id: t.id,
|
|
||||||
from_plan: t.source_plan_index,
|
|
||||||
effort: t.effort.estimated
|
|
||||||
})),
|
|
||||||
variance: effortVariance,
|
|
||||||
severity: 'high'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for scope differences
|
|
||||||
const scopeDifferences = analyzeScopeDifferences(group.tasks)
|
|
||||||
if (scopeDifferences.length > 0) {
|
|
||||||
conflictDetector.taskConflicts.push({
|
|
||||||
task_group: group.title,
|
|
||||||
scope_differences: scopeDifferences,
|
|
||||||
severity: 'medium'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Algorithm 2: Architecture conflicts
|
|
||||||
const architectures = normalizedPlans.map(p => p.metadata.complexity)
|
|
||||||
if (new Set(architectures).size > 1) {
|
|
||||||
conflictDetector.architectureConflicts.push({
|
|
||||||
different_approaches: true,
|
|
||||||
complexity_levels: architectures.map((a, i) => ({
|
|
||||||
plan: i,
|
|
||||||
complexity: a
|
|
||||||
})),
|
|
||||||
severity: 'high'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Algorithm 3: Risk assessment conflicts
|
|
||||||
const riskLevels = allTasks.map(t => ({ task: t.id, risk: t.risk.level }))
|
|
||||||
const taskRisks = {}
|
|
||||||
for (const tr of riskLevels) {
|
|
||||||
if (!taskRisks[tr.task]) taskRisks[tr.task] = []
|
|
||||||
taskRisks[tr.task].push(tr.risk)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [task, risks] of Object.entries(taskRisks)) {
|
|
||||||
if (new Set(risks).size > 1) {
|
|
||||||
conflictDetector.riskConflicts.push({
|
|
||||||
task: task,
|
|
||||||
conflicting_risk_levels: risks,
|
|
||||||
severity: 'medium'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save conflicts
|
|
||||||
Write(`${sessionFolder}/conflicts.json`, JSON.stringify(conflictDetector, null, 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 4: Conflict Resolution
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Resolve conflicts based on selected rule
|
|
||||||
const resolutions = {
|
|
||||||
resolution_rule: resolutionRule,
|
|
||||||
timestamp: getUtc8ISOString(),
|
|
||||||
|
|
||||||
effort_resolutions: [],
|
|
||||||
architecture_resolutions: [],
|
|
||||||
risk_resolutions: [],
|
|
||||||
scope_resolutions: [],
|
|
||||||
priority_resolutions: []
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolution Strategy 1: Consensus
|
|
||||||
if (resolutionRule === 'consensus') {
|
|
||||||
for (const conflict of conflictDetector.effortConflicts) {
|
|
||||||
// Use median or average
|
|
||||||
const efforts = conflict.conflicting_tasks.map(t => parseEffort(t.effort))
|
|
||||||
const resolved_effort = calculateMedian(efforts)
|
|
||||||
|
|
||||||
resolutions.effort_resolutions.push({
|
|
||||||
conflict: conflict.task_group,
|
|
||||||
original_estimates: efforts,
|
|
||||||
resolved_estimate: resolved_effort,
|
|
||||||
method: 'consensus-median',
|
|
||||||
rationale: 'Used median of all estimates'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolution Strategy 2: Priority-Based
|
|
||||||
else if (resolutionRule === 'priority') {
|
|
||||||
// Use the plan from highest priority source (first or most recent)
|
|
||||||
for (const conflict of conflictDetector.effortConflicts) {
|
|
||||||
const highestPriority = conflict.conflicting_tasks[0] // First plan has priority
|
|
||||||
|
|
||||||
resolutions.effort_resolutions.push({
|
|
||||||
conflict: conflict.task_group,
|
|
||||||
conflicting_estimates: conflict.conflicting_tasks.map(t => t.effort),
|
|
||||||
resolved_estimate: highestPriority.effort,
|
|
||||||
selected_from_plan: highestPriority.from_plan,
|
|
||||||
method: 'priority-based',
|
|
||||||
rationale: `Selected estimate from plan ${highestPriority.from_plan} (highest priority)`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolution Strategy 3: Hierarchy (requires user ranking)
|
|
||||||
else if (resolutionRule === 'hierarchy') {
|
|
||||||
if (!isAutoMode) {
|
|
||||||
// Ask user to rank plan importance
|
|
||||||
const planRanking = AskUserQuestion({
|
|
||||||
questions: [{
|
|
||||||
question: "请按重要性排序这些规划(从最重要到最不重要):",
|
|
||||||
header: "Plan Ranking",
|
|
||||||
multiSelect: false,
|
|
||||||
options: normalizedPlans.slice(0, 5).map(p => ({
|
|
||||||
label: `Plan ${p.index}: ${p.metadata.title.substring(0, 40)}`,
|
|
||||||
description: `${p.tasks.length} tasks, complexity: ${p.metadata.complexity}`
|
|
||||||
}))
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Apply hierarchy
|
|
||||||
const hierarchy = extractHierarchy(planRanking)
|
|
||||||
for (const conflict of conflictDetector.effortConflicts) {
|
|
||||||
const topPriorityTask = conflict.conflicting_tasks
|
|
||||||
.sort((a, b) => hierarchy[a.from_plan] - hierarchy[b.from_plan])[0]
|
|
||||||
|
|
||||||
resolutions.effort_resolutions.push({
|
|
||||||
conflict: conflict.task_group,
|
|
||||||
resolved_estimate: topPriorityTask.effort,
|
|
||||||
selected_from_plan: topPriorityTask.from_plan,
|
|
||||||
method: 'hierarchy-based',
|
|
||||||
rationale: `Selected from highest-ranked plan`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/resolutions.json`, JSON.stringify(resolutions, null, 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 5: Plan Synthesis
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Merge all tasks into unified plan
|
|
||||||
const unifiedTasks = []
|
|
||||||
const processedTaskIds = new Set()
|
|
||||||
|
|
||||||
for (const task of allTasks) {
|
|
||||||
const taskKey = generateTaskKey(task)
|
|
||||||
|
|
||||||
if (processedTaskIds.has(taskKey)) {
|
|
||||||
// Task already added, skip
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
processedTaskIds.add(taskKey)
|
|
||||||
|
|
||||||
// Apply resolution if this task has conflicts
|
|
||||||
let resolvedTask = { ...task }
|
|
||||||
|
|
||||||
const effortResolution = resolutions.effort_resolutions
|
|
||||||
.find(r => r.conflict === taskKey)
|
|
||||||
if (effortResolution) {
|
|
||||||
resolvedTask.effort.estimated = effortResolution.resolved_estimate
|
|
||||||
resolvedTask.effort.resolution_method = effortResolution.method
|
|
||||||
}
|
|
||||||
|
|
||||||
unifiedTasks.push({
|
|
||||||
id: taskKey,
|
|
||||||
title: task.title,
|
|
||||||
description: task.description,
|
|
||||||
type: task.type,
|
|
||||||
priority: task.priority,
|
|
||||||
|
|
||||||
effort: resolvedTask.effort,
|
|
||||||
risk: task.risk,
|
|
||||||
dependencies: task.dependencies,
|
|
||||||
|
|
||||||
success_criteria: [...new Set([
|
|
||||||
...task.success_criteria,
|
|
||||||
...findRelatedTasks(task, allTasks)
|
|
||||||
.flatMap(t => t.success_criteria)
|
|
||||||
])],
|
|
||||||
|
|
||||||
challenges: [...new Set([
|
|
||||||
...task.challenges,
|
|
||||||
...findRelatedTasks(task, allTasks)
|
|
||||||
.flatMap(t => t.challenges)
|
|
||||||
])],
|
|
||||||
|
|
||||||
source_plans: [
|
|
||||||
...new Set(allTasks
|
|
||||||
.filter(t => generateTaskKey(t) === taskKey)
|
|
||||||
.map(t => t.source_plan_index))
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate execution sequence
|
|
||||||
const executionSequence = topologicalSort(unifiedTasks)
|
|
||||||
const criticalPath = identifyCriticalPath(unifiedTasks, executionSequence)
|
|
||||||
|
|
||||||
// Final unified plan
|
|
||||||
const unifiedPlan = {
|
|
||||||
session_id: sessionId,
|
|
||||||
merge_timestamp: getUtc8ISOString(),
|
|
||||||
|
|
||||||
summary: {
|
|
||||||
total_source_plans: normalizedPlans.length,
|
|
||||||
original_tasks_total: allTasks.length,
|
|
||||||
merged_tasks: unifiedTasks.length,
|
|
||||||
conflicts_resolved: Object.values(conflictDetector).flat().length,
|
|
||||||
resolution_rule: resolutionRule
|
|
||||||
},
|
|
||||||
|
|
||||||
merged_metadata: {
|
|
||||||
topics: [...new Set(normalizedPlans.map(p => p.metadata.topic))],
|
|
||||||
average_complexity: calculateAverage(normalizedPlans.map(p => parseComplexity(p.metadata.complexity))),
|
|
||||||
combined_scope: estimateScope(unifiedTasks)
|
|
||||||
},
|
|
||||||
|
|
||||||
tasks: unifiedTasks,
|
|
||||||
|
|
||||||
execution_sequence: executionSequence,
|
|
||||||
critical_path: criticalPath,
|
|
||||||
|
|
||||||
risks: aggregateRisks(unifiedTasks),
|
|
||||||
success_criteria: aggregateSuccessCriteria(unifiedTasks),
|
|
||||||
|
|
||||||
audit_trail: {
|
|
||||||
source_plans: normalizedPlans.length,
|
|
||||||
conflicts_detected: Object.values(conflictDetector).flat().length,
|
|
||||||
conflicts_resolved: Object.values(resolutions).flat().length,
|
|
||||||
resolution_method: resolutionRule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/unified-plan.json`, JSON.stringify(unifiedPlan, null, 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Phase 6: Generate Execution Plan
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Merged Planning Session
|
|
||||||
|
|
||||||
**Session ID**: ${sessionId}
|
|
||||||
**Pattern**: ${planPattern}
|
|
||||||
**Created**: ${getUtc8ISOString()}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Merge Summary
|
|
||||||
|
|
||||||
**Source Plans**: ${unifiedPlan.summary.total_source_plans}
|
|
||||||
**Original Tasks**: ${unifiedPlan.summary.original_tasks_total}
|
|
||||||
**Merged Tasks**: ${unifiedPlan.summary.merged_tasks}
|
|
||||||
**Tasks Deduplicated**: ${unifiedPlan.summary.original_tasks_total - unifiedPlan.summary.merged_tasks}
|
|
||||||
**Conflicts Resolved**: ${unifiedPlan.summary.conflicts_resolved}
|
|
||||||
|
|
||||||
**Resolution Method**: ${unifiedPlan.summary.resolution_rule}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Merged Plan Overview
|
|
||||||
|
|
||||||
**Topics**: ${unifiedPlan.merged_metadata.topics.join(', ')}
|
|
||||||
**Combined Complexity**: ${unifiedPlan.merged_metadata.average_complexity}
|
|
||||||
**Total Scope**: ${unifiedPlan.merged_metadata.combined_scope}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Unified Task List
|
|
||||||
|
|
||||||
${unifiedPlan.tasks.map((task, i) => `
|
|
||||||
${i+1}. **${task.id}: ${task.title}**
|
|
||||||
- Type: ${task.type}
|
|
||||||
- Effort: ${task.effort.estimated}
|
|
||||||
- Risk: ${task.risk.level}
|
|
||||||
- Source Plans: ${task.source_plans.join(', ')}
|
|
||||||
- ${task.description}
|
|
||||||
`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Execution Sequence
|
|
||||||
|
|
||||||
**Critical Path**: ${unifiedPlan.critical_path.join(' → ')}
|
|
||||||
|
|
||||||
**Execution Order**:
|
|
||||||
${unifiedPlan.execution_sequence.map((id, i) => `${i+1}. ${id}`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conflict Resolution Report
|
|
||||||
|
|
||||||
**Total Conflicts**: ${unifiedPlan.summary.conflicts_resolved}
|
|
||||||
|
|
||||||
**Resolved Conflicts**:
|
|
||||||
${Object.entries(resolutions).flatMap(([key, items]) =>
|
|
||||||
items.slice(0, 3).map((item, i) => `
|
|
||||||
- ${key.replace('_', ' ')}: ${item.rationale || item.method}
|
|
||||||
`)
|
|
||||||
).join('\n')}
|
|
||||||
|
|
||||||
**Full Report**: See \`conflicts.json\` and \`resolutions.json\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risks & Considerations
|
|
||||||
|
|
||||||
**Aggregated Risks**:
|
|
||||||
${unifiedPlan.risks.slice(0, 5).map(r => `- **${r.title}**: ${r.mitigation}`).join('\n')}
|
|
||||||
|
|
||||||
**Combined Success Criteria**:
|
|
||||||
${unifiedPlan.success_criteria.slice(0, 5).map(c => `- ${c}`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Option 1: Direct Execution
|
|
||||||
Execute merged plan with unified-execute-with-file:
|
|
||||||
\`\`\`
|
|
||||||
/workflow:unified-execute-with-file -p ${sessionFolder}/unified-plan.json
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Option 2: Detailed Planning
|
|
||||||
Create detailed IMPL_PLAN from merged plan:
|
|
||||||
\`\`\`
|
|
||||||
/workflow:plan "Based on merged plan from $(echo ${planPattern})"
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
### Option 3: Review Conflicts
|
|
||||||
Review detailed conflict analysis:
|
|
||||||
\`\`\`
|
|
||||||
cat ${sessionFolder}/resolutions.json
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Artifacts
|
|
||||||
|
|
||||||
- **source-index.json** - All input plans and sources
|
|
||||||
- **conflicts.json** - Conflict detection results
|
|
||||||
- **resolutions.json** - How each conflict was resolved
|
|
||||||
- **unified-plan.json** - Merged plan data structure (for execution)
|
|
||||||
- **unified-plan.md** - This document (human-readable)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Session Folder Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
.workflow/.merged/{sessionId}/
|
|
||||||
├── merge.md # Merge process and decisions
|
|
||||||
├── source-index.json # All input plan sources
|
|
||||||
├── conflicts.json # Detected conflicts
|
|
||||||
├── resolutions.json # Conflict resolutions applied
|
|
||||||
├── unified-plan.json # Merged plan (machine-parseable, for execution)
|
|
||||||
└── unified-plan.md # Execution-ready plan (human-readable)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Resolution Rules
|
|
||||||
|
|
||||||
### Rule 1: Consensus (default)
|
|
||||||
- Use median or average of conflicting estimates
|
|
||||||
- Good for: Multiple similar perspectives
|
|
||||||
- Tradeoff: May miss important minority viewpoints
|
|
||||||
|
|
||||||
### Rule 2: Priority-Based
|
|
||||||
- First plan has highest priority, subsequent plans are fallback
|
|
||||||
- Good for: Clear ranking of plan sources
|
|
||||||
- Tradeoff: Discards valuable alternative perspectives
|
|
||||||
|
|
||||||
### Rule 3: Hierarchy
|
|
||||||
- User explicitly ranks importance of each plan
|
|
||||||
- Good for: Mixed-source plans (engineering + product + leadership)
|
|
||||||
- Tradeoff: Requires user input
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Input Format Support
|
|
||||||
|
|
||||||
| Source Type | Detection | Parsing | Notes |
|
|
||||||
|-------------|-----------|---------|-------|
|
|
||||||
| **Brainstorm** | `.brainstorm/*/synthesis.json` | Top ideas → tasks | Ideas converted to work items |
|
|
||||||
| **Analysis** | `.analysis/*/conclusions.json` | Recommendations → tasks | Recommendations prioritized |
|
|
||||||
| **Quick-Plan** | `.planning/*/synthesis.json` | Direct task list | Already normalized |
|
|
||||||
| **IMPL_PLAN** | `*IMPL_PLAN.md` | Markdown → tasks | Parsed from markdown structure |
|
|
||||||
| **Task JSON** | `.json` with `tasks` key | Direct mapping | Requires standard schema |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| Situation | Action |
|
|
||||||
|-----------|--------|
|
|
||||||
| No plans found | Suggest search terms, list available plans |
|
|
||||||
| Incompatible formats | Skip unsupported format, continue with others |
|
|
||||||
| Circular dependencies | Alert user, suggest manual review |
|
|
||||||
| Unresolvable conflicts | Require user decision (unless --yes + conflict rule) |
|
|
||||||
| Contradictory recommendations | Document both options for user consideration |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage Patterns
|
|
||||||
|
|
||||||
### Pattern 1: Merge Multiple Brainstorms
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/workflow:merge-plans-with-file "authentication" -y -r consensus
|
|
||||||
# → Finds all brainstorm sessions with "auth"
|
|
||||||
# → Merges top ideas into unified task list
|
|
||||||
# → Uses consensus method for conflicts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 2: Synthesize Team Input
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/workflow:merge-plans-with-file "payment-integration" -r hierarchy
|
|
||||||
# → Loads plans from different team members
|
|
||||||
# → Asks for ranking by importance
|
|
||||||
# → Applies hierarchy-based conflict resolution
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern 3: Bridge Planning Phases
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/workflow:merge-plans-with-file "user-auth" -f analysis
|
|
||||||
# → Takes analysis conclusions
|
|
||||||
# → Merges with existing quick-plans
|
|
||||||
# → Produces execution-ready plan
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Advanced: Custom Conflict Resolution
|
|
||||||
|
|
||||||
For complex conflict scenarios, create custom resolution script:
|
|
||||||
|
|
||||||
```
|
|
||||||
.workflow/.merged/{sessionId}/
|
|
||||||
└── custom-resolutions.js (optional)
|
|
||||||
- Define custom conflict resolution logic
|
|
||||||
- Applied after automatic resolution
|
|
||||||
- Override specific decisions
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Before merging**:
|
|
||||||
- Ensure all source plans have same quality level
|
|
||||||
- Verify plans address same scope/topic
|
|
||||||
- Document any special considerations
|
|
||||||
|
|
||||||
2. **During merging**:
|
|
||||||
- Review conflict matrix (conflicts.json)
|
|
||||||
- Understand resolution rationale (resolutions.json)
|
|
||||||
- Challenge assumptions if results seem odd
|
|
||||||
|
|
||||||
3. **After merging**:
|
|
||||||
- Validate unified plan makes sense
|
|
||||||
- Review critical path
|
|
||||||
- Ensure no important details lost
|
|
||||||
- Execute or iterate if needed
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Integration with Other Workflows
|
|
||||||
|
|
||||||
```
|
|
||||||
Multiple Brainstorms / Analyses
|
|
||||||
│
|
|
||||||
├─ brainstorm-with-file (session 1)
|
|
||||||
├─ brainstorm-with-file (session 2)
|
|
||||||
├─ analyze-with-file (session 3)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
merge-plans-with-file ◄──── This workflow
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
unified-plan.json
|
|
||||||
│
|
|
||||||
├─ /workflow:unified-execute-with-file (direct execution)
|
|
||||||
├─ /workflow:plan (detailed planning)
|
|
||||||
└─ /workflow:quick-plan-with-file (refinement)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Comparison: When to Use Which Merge Rule
|
|
||||||
|
|
||||||
| Rule | Use When | Pros | Cons |
|
|
||||||
|------|----------|------|------|
|
|
||||||
| **Consensus** | Similar-quality inputs | Fair, balanced | May miss extremes |
|
|
||||||
| **Priority** | Clear hierarchy | Simple, predictable | May bias to first input |
|
|
||||||
| **Hierarchy** | Mixed stakeholders | Respects importance | Requires user ranking |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Ready to execute**: Run `/workflow:merge-plans-with-file` to start merging plans!
|
|
||||||
@@ -155,27 +155,65 @@ bash(`mkdir -p ${executionFolder}`)
|
|||||||
|
|
||||||
## Plan Format Parsers
|
## Plan Format Parsers
|
||||||
|
|
||||||
Support multiple plan sources:
|
Support multiple plan sources (all JSON plans follow plan-json-schema.json):
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function parsePlan(content, filePath) {
|
function parsePlan(content, filePath) {
|
||||||
const ext = filePath.split('.').pop()
|
const ext = filePath.split('.').pop()
|
||||||
|
|
||||||
if (filePath.includes('IMPL_PLAN')) {
|
if (filePath.includes('IMPL_PLAN')) {
|
||||||
return parseImplPlan(content) // From /workflow:plan
|
return parseImplPlan(content) // From /workflow:plan (markdown)
|
||||||
} else if (filePath.includes('brainstorm')) {
|
} else if (filePath.includes('brainstorm')) {
|
||||||
return parseBrainstormPlan(content) // From /workflow:brainstorm-with-file
|
return parseBrainstormPlan(content) // From /workflow:brainstorm-with-file
|
||||||
} else if (filePath.includes('synthesis')) {
|
} else if (filePath.includes('synthesis')) {
|
||||||
return parseSynthesisPlan(content) // From /workflow:brainstorm-with-file synthesis.json
|
return parseSynthesisPlan(content) // From /workflow:brainstorm-with-file synthesis.json
|
||||||
} else if (filePath.includes('conclusions')) {
|
} else if (filePath.includes('conclusions')) {
|
||||||
return parseConclusionsPlan(content) // From /workflow:analyze-with-file conclusions.json
|
return parseConclusionsPlan(content) // From /workflow:analyze-with-file conclusions.json
|
||||||
} else if (filePath.endsWith('.json') && content.includes('tasks')) {
|
} else if (filePath.endsWith('.json') && content.includes('"tasks"')) {
|
||||||
return parseTaskJson(content) // Direct task JSON
|
return parsePlanJson(content) // Standard plan-json-schema (lite-plan, collaborative-plan, sub-plans)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Unsupported plan format: ${filePath}`)
|
throw new Error(`Unsupported plan format: ${filePath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Standard plan-json-schema parser
|
||||||
|
// Handles: lite-plan, collaborative-plan, sub-plans (all follow same schema)
|
||||||
|
function parsePlanJson(content) {
|
||||||
|
const plan = JSON.parse(content)
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: plan.merge_metadata ? 'collaborative-plan' : 'lite-plan',
|
||||||
|
title: plan.summary?.split('.')[0] || 'Untitled Plan',
|
||||||
|
slug: plan._metadata?.session_id || generateSlug(plan.summary),
|
||||||
|
summary: plan.summary,
|
||||||
|
approach: plan.approach,
|
||||||
|
tasks: plan.tasks.map(task => ({
|
||||||
|
id: task.id,
|
||||||
|
type: inferTaskTypeFromAction(task.action),
|
||||||
|
title: task.title,
|
||||||
|
description: task.description,
|
||||||
|
dependencies: task.depends_on || [],
|
||||||
|
agent_type: selectAgentFromTask(task),
|
||||||
|
prompt: buildPromptFromTask(task),
|
||||||
|
files_to_modify: task.modification_points?.map(mp => mp.file) || [],
|
||||||
|
expected_output: task.acceptance || [],
|
||||||
|
priority: task.effort?.complexity === 'high' ? 'high' : 'normal',
|
||||||
|
estimated_duration: task.effort?.estimated_hours ? `${task.effort.estimated_hours}h` : null,
|
||||||
|
verification: task.verification,
|
||||||
|
risks: task.risks,
|
||||||
|
source_agent: task.source_agent // From collaborative-plan sub-agents
|
||||||
|
})),
|
||||||
|
flow_control: plan.flow_control,
|
||||||
|
data_flow: plan.data_flow,
|
||||||
|
design_decisions: plan.design_decisions,
|
||||||
|
estimatedDuration: plan.estimated_time,
|
||||||
|
recommended_execution: plan.recommended_execution,
|
||||||
|
complexity: plan.complexity,
|
||||||
|
merge_metadata: plan.merge_metadata, // Present if from collaborative-plan
|
||||||
|
_metadata: plan._metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IMPL_PLAN.md parser
|
// IMPL_PLAN.md parser
|
||||||
function parseImplPlan(content) {
|
function parseImplPlan(content) {
|
||||||
// Extract:
|
// Extract:
|
||||||
@@ -216,6 +254,65 @@ function parseSynthesisPlan(content) {
|
|||||||
recommendations: synthesis.recommendations
|
recommendations: synthesis.recommendations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: Infer task type from action field
|
||||||
|
function inferTaskTypeFromAction(action) {
|
||||||
|
const actionMap = {
|
||||||
|
'Create': 'code',
|
||||||
|
'Update': 'code',
|
||||||
|
'Implement': 'code',
|
||||||
|
'Refactor': 'code',
|
||||||
|
'Add': 'code',
|
||||||
|
'Delete': 'code',
|
||||||
|
'Configure': 'config',
|
||||||
|
'Test': 'test',
|
||||||
|
'Fix': 'debug'
|
||||||
|
}
|
||||||
|
return actionMap[action] || 'code'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Select agent based on task properties
|
||||||
|
function selectAgentFromTask(task) {
|
||||||
|
if (task.verification?.unit_tests?.length > 0) {
|
||||||
|
return 'tdd-developer'
|
||||||
|
} else if (task.action === 'Test') {
|
||||||
|
return 'test-fix-agent'
|
||||||
|
} else if (task.action === 'Fix') {
|
||||||
|
return 'debug-explore-agent'
|
||||||
|
} else {
|
||||||
|
return 'code-developer'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: Build prompt from task details
|
||||||
|
function buildPromptFromTask(task) {
|
||||||
|
let prompt = `## Task: ${task.title}\n\n${task.description}\n\n`
|
||||||
|
|
||||||
|
if (task.modification_points?.length > 0) {
|
||||||
|
prompt += `### Modification Points\n`
|
||||||
|
task.modification_points.forEach(mp => {
|
||||||
|
prompt += `- **${mp.file}**: ${mp.target} → ${mp.change}\n`
|
||||||
|
})
|
||||||
|
prompt += '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.implementation?.length > 0) {
|
||||||
|
prompt += `### Implementation Steps\n`
|
||||||
|
task.implementation.forEach((step, i) => {
|
||||||
|
prompt += `${i + 1}. ${step}\n`
|
||||||
|
})
|
||||||
|
prompt += '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.acceptance?.length > 0) {
|
||||||
|
prompt += `### Acceptance Criteria\n`
|
||||||
|
task.acceptance.forEach(ac => {
|
||||||
|
prompt += `- ${ac}\n`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,450 +0,0 @@
|
|||||||
---
|
|
||||||
description: Multi-agent rapid planning with minimal documentation, conflict resolution, and actionable synthesis. Lightweight planning from raw task, brainstorm, or analysis artifacts
|
|
||||||
argument-hint: "TOPIC=\"<planning topic or task>\" [--from=brainstorm|analysis|task|raw] [--perspectives=arch,impl,risk,decision] [--auto] [--verbose]"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Codex Quick-Plan-With-File Prompt
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Multi-agent rapid planning workflow with **minimal documentation overhead**. Coordinates parallel agent analysis (architecture, implementation, validation, decision), synthesizes conflicting perspectives into actionable decisions, and generates an implementation-ready plan.
|
|
||||||
|
|
||||||
**Core workflow**: Parse Input → Parallel Analysis → Conflict Resolution → Plan Synthesis → Output
|
|
||||||
|
|
||||||
**Key features**:
|
|
||||||
- **Format Agnostic**: Consumes brainstorm conclusions, analysis recommendations, quick tasks, or raw descriptions
|
|
||||||
- **Minimal Docs**: Single plan.md (no lengthy timeline documentation)
|
|
||||||
- **Parallel Multi-Agent**: 4 concurrent perspectives for rapid analysis
|
|
||||||
- **Conflict Resolution**: Automatic conflict detection and synthesis
|
|
||||||
- **Actionable Output**: Direct task breakdown ready for execution
|
|
||||||
|
|
||||||
## Target Planning
|
|
||||||
|
|
||||||
**$TOPIC**
|
|
||||||
|
|
||||||
- `--from`: Input source type (brainstorm | analysis | task | raw) - auto-detected if omitted
|
|
||||||
- `--perspectives`: Which perspectives to use (arch, impl, risk, decision) - all by default
|
|
||||||
- `--auto`: Auto-confirm decisions, minimal user prompts
|
|
||||||
- `--verbose`: Verbose output with all reasoning
|
|
||||||
|
|
||||||
## Execution Process
|
|
||||||
|
|
||||||
```
|
|
||||||
Phase 1: Input Validation & Loading
|
|
||||||
├─ Parse input: topic | artifact reference
|
|
||||||
├─ Load artifact if referenced (synthesis.json | conclusions.json)
|
|
||||||
├─ Extract constraints and key requirements
|
|
||||||
└─ Initialize session folder
|
|
||||||
|
|
||||||
Phase 2: Parallel Multi-Agent Analysis (concurrent)
|
|
||||||
├─ Agent 1 (Architecture): Design decomposition, patterns, scalability
|
|
||||||
├─ Agent 2 (Implementation): Tech stack, feasibility, effort estimates
|
|
||||||
├─ Agent 3 (Validation): Risk matrix, testing strategy, monitoring
|
|
||||||
├─ Agent 4 (Decision): Recommendations, tradeoffs, execution strategy
|
|
||||||
└─ Aggregate findings into perspectives.json
|
|
||||||
|
|
||||||
Phase 3: Conflict Detection & Resolution
|
|
||||||
├─ Detect: effort conflicts, architecture conflicts, risk conflicts
|
|
||||||
├─ Analyze rationale for each conflict
|
|
||||||
├─ Synthesis via arbitration: generate unified recommendation
|
|
||||||
├─ Document conflicts and resolutions
|
|
||||||
└─ Update plan.md
|
|
||||||
|
|
||||||
Phase 4: Plan Synthesis
|
|
||||||
├─ Consolidate all insights
|
|
||||||
├─ Generate task breakdown (5-8 major tasks)
|
|
||||||
├─ Create execution strategy and dependencies
|
|
||||||
├─ Document assumptions and risks
|
|
||||||
└─ Output plan.md + synthesis.json
|
|
||||||
|
|
||||||
Output:
|
|
||||||
├─ .workflow/.planning/{sessionId}/plan.md (minimal, actionable)
|
|
||||||
├─ .workflow/.planning/{sessionId}/perspectives.json (agent findings)
|
|
||||||
├─ .workflow/.planning/{sessionId}/conflicts.json (decision points)
|
|
||||||
└─ .workflow/.planning/{sessionId}/synthesis.json (task breakdown for execution)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
### Phase 1: Session Setup & Input Loading
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
|
||||||
|
|
||||||
// Parse input
|
|
||||||
const planSlug = "$TOPIC".toLowerCase()
|
|
||||||
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
|
|
||||||
.substring(0, 30)
|
|
||||||
const sessionId = `PLAN-${planSlug}-${getUtc8ISOString().substring(0, 10)}`
|
|
||||||
const sessionFolder = `.workflow/.planning/${sessionId}`
|
|
||||||
|
|
||||||
// Detect input type
|
|
||||||
let artifact = null
|
|
||||||
if ($TOPIC.startsWith('BS-') || "$TOPIC".includes('brainstorm')) {
|
|
||||||
artifact = loadBrainstormArtifact($TOPIC)
|
|
||||||
} else if ($TOPIC.startsWith('ANL-') || "$TOPIC".includes('analysis')) {
|
|
||||||
artifact = loadAnalysisArtifact($TOPIC)
|
|
||||||
}
|
|
||||||
|
|
||||||
bash(`mkdir -p ${sessionFolder}`)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 2: Parallel Multi-Agent Analysis
|
|
||||||
|
|
||||||
Run 4 agents in parallel using ccw cli:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Launch all 4 agents concurrently with Bash run_in_background
|
|
||||||
const agentPromises = []
|
|
||||||
|
|
||||||
// Agent 1 - Architecture (Gemini)
|
|
||||||
agentPromises.push(
|
|
||||||
Bash({
|
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Architecture & high-level design for '${planningTopic}'
|
|
||||||
Success: Clear component decomposition and architectural approach
|
|
||||||
|
|
||||||
TASK:
|
|
||||||
• Decompose problem into major components/modules
|
|
||||||
• Identify architectural patterns and integration points
|
|
||||||
• Design component interfaces and data models
|
|
||||||
• Assess scalability and maintainability implications
|
|
||||||
• Propose architectural approach with rationale
|
|
||||||
|
|
||||||
MODE: analysis
|
|
||||||
|
|
||||||
CONTEXT: @**/*
|
|
||||||
${artifact ? \`| Source artifact: \${artifact.type}\` : ''}
|
|
||||||
|
|
||||||
EXPECTED:
|
|
||||||
- Component decomposition (list with responsibilities)
|
|
||||||
- Module interfaces and contracts
|
|
||||||
- Data flow between components
|
|
||||||
- Architectural patterns applied (e.g., MVC, Event-Driven, etc.)
|
|
||||||
- Scalability assessment (1-5 rating with rationale)
|
|
||||||
- Architectural risks identified
|
|
||||||
|
|
||||||
CONSTRAINTS: Focus on long-term maintainability and extensibility
|
|
||||||
" --tool gemini --mode analysis`,
|
|
||||||
run_in_background: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Agent 2 - Implementation (Codex)
|
|
||||||
agentPromises.push(
|
|
||||||
Bash({
|
|
||||||
command: \`ccw cli -p "
|
|
||||||
PURPOSE: Implementation approach & technical feasibility for '\${planningTopic}'
|
|
||||||
Success: Concrete implementation strategy with realistic estimates
|
|
||||||
|
|
||||||
TASK:
|
|
||||||
• Evaluate technical feasibility of proposed approach
|
|
||||||
• Identify required technologies and dependencies
|
|
||||||
• Estimate effort: analysis/design/coding/testing/deployment
|
|
||||||
• Suggest implementation phases and milestones
|
|
||||||
• Highlight technical blockers and challenges
|
|
||||||
|
|
||||||
MODE: analysis
|
|
||||||
|
|
||||||
CONTEXT: @**/*
|
|
||||||
\${artifact ? \`| Source artifact: \${artifact.type}\` : ''}
|
|
||||||
|
|
||||||
EXPECTED:
|
|
||||||
- Technology stack recommendation (languages, frameworks, tools)
|
|
||||||
- Implementation complexity: high|medium|low (with justification)
|
|
||||||
- Effort breakdown (hours or complexity: analysis, design, coding, testing, deployment)
|
|
||||||
- Key technical decisions with tradeoffs explained
|
|
||||||
- Potential blockers and mitigation strategies
|
|
||||||
- Suggested implementation phases with sequencing
|
|
||||||
- Reusable components or libraries identified
|
|
||||||
|
|
||||||
CONSTRAINTS: Realistic with current tech stack
|
|
||||||
" --tool codex --mode analysis\`,
|
|
||||||
run_in_background: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Agent 3 - Validation & Risk (Claude)
|
|
||||||
agentPromises.push(
|
|
||||||
Bash({
|
|
||||||
command: \`ccw cli -p "
|
|
||||||
PURPOSE: Risk analysis and validation strategy for '\${planningTopic}'
|
|
||||||
Success: Comprehensive risk matrix with testing and deployment strategy
|
|
||||||
|
|
||||||
TASK:
|
|
||||||
• Identify technical risks and failure scenarios
|
|
||||||
• Assess timeline and resource risks
|
|
||||||
• Define validation/testing strategy (unit, integration, e2e, performance)
|
|
||||||
• Suggest monitoring and observability requirements
|
|
||||||
• Propose deployment strategy and rollback plan
|
|
||||||
|
|
||||||
MODE: analysis
|
|
||||||
|
|
||||||
CONTEXT: @**/*
|
|
||||||
\${artifact ? \`| Source artifact: \${artifact.type}\` : ''}
|
|
||||||
|
|
||||||
EXPECTED:
|
|
||||||
- Risk matrix (likelihood × impact, each 1-5)
|
|
||||||
- Top 3 technical risks with mitigation approaches
|
|
||||||
- Top 3 timeline/resource risks with mitigation
|
|
||||||
- Testing strategy (what to test, how, when, acceptance criteria)
|
|
||||||
- Deployment strategy (staged rollout, blue-green, canary, etc.)
|
|
||||||
- Rollback plan and recovery procedures
|
|
||||||
- Monitoring/observability requirements (metrics, logs, alerts)
|
|
||||||
- Overall risk rating: low|medium|high (with confidence)
|
|
||||||
|
|
||||||
CONSTRAINTS: Be realistic, not pessimistic
|
|
||||||
" --tool claude --mode analysis\`,
|
|
||||||
run_in_background: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Agent 4 - Strategic Decision (Gemini)
|
|
||||||
agentPromises.push(
|
|
||||||
Bash({
|
|
||||||
command: \`ccw cli -p "
|
|
||||||
PURPOSE: Strategic decisions and execution recommendations for '\${planningTopic}'
|
|
||||||
Success: Clear recommended approach with tradeoff analysis
|
|
||||||
|
|
||||||
TASK:
|
|
||||||
• Synthesize all perspectives into strategic recommendations
|
|
||||||
• Identify 2-3 critical decision points with recommended choices
|
|
||||||
• Clearly outline key tradeoffs (speed vs quality, scope vs timeline, risk vs cost)
|
|
||||||
• Propose go/no-go decision criteria and success metrics
|
|
||||||
• Suggest execution strategy and resource sequencing
|
|
||||||
|
|
||||||
MODE: analysis
|
|
||||||
|
|
||||||
CONTEXT: @**/*
|
|
||||||
\${artifact ? \`| Source artifact: \${artifact.type}\` : ''}
|
|
||||||
|
|
||||||
EXPECTED:
|
|
||||||
- Primary recommendation with strong rationale (1-2 paragraphs)
|
|
||||||
- Alternative approaches with pros/cons (2-3 alternatives)
|
|
||||||
- 2-3 critical decision points:
|
|
||||||
- What decision needs to be made
|
|
||||||
- Trade-offs for each option
|
|
||||||
- Recommended choice and why
|
|
||||||
- Key trade-offs explained (what we're optimizing for: speed/quality/risk/cost)
|
|
||||||
- Success metrics and go/no-go criteria
|
|
||||||
- Resource requirements and critical path items
|
|
||||||
- Suggested execution sequencing and phases
|
|
||||||
|
|
||||||
CONSTRAINTS: Focus on actionable decisions, provide clear rationale
|
|
||||||
" --tool gemini --mode analysis\`,
|
|
||||||
run_in_background: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wait for all agents to complete
|
|
||||||
const [archResult, implResult, riskResult, decisionResult] = await Promise.all(agentPromises)
|
|
||||||
|
|
||||||
// Parse and extract findings from each agent result
|
|
||||||
const architecture = parseArchitectureResult(archResult)
|
|
||||||
const implementation = parseImplementationResult(implResult)
|
|
||||||
const validation = parseValidationResult(riskResult)
|
|
||||||
const recommendation = parseDecisionResult(decisionResult)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Agent Focus Areas**:
|
|
||||||
|
|
||||||
| Agent | Perspective | Focus Areas |
|
|
||||||
|-------|-------------|------------|
|
|
||||||
| Gemini (Design) | Architecture patterns | Components, interfaces, scalability, patterns |
|
|
||||||
| Codex (Build) | Implementation reality | Tech stack, complexity, effort, blockers |
|
|
||||||
| Claude (Validate) | Risk & testing | Risk matrix, testing strategy, deployment, monitoring |
|
|
||||||
| Gemini (Decide) | Strategic synthesis | Recommendations, trade-offs, critical decisions |
|
|
||||||
|
|
||||||
### Phase 3: Parse & Aggregate Perspectives
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const perspectives = {
|
|
||||||
session_id: sessionId,
|
|
||||||
topic: "$TOPIC",
|
|
||||||
timestamp: getUtc8ISOString(),
|
|
||||||
|
|
||||||
architecture: {
|
|
||||||
components: [...],
|
|
||||||
patterns: [...],
|
|
||||||
scalability_rating: 3,
|
|
||||||
risks: [...]
|
|
||||||
},
|
|
||||||
|
|
||||||
implementation: {
|
|
||||||
technology_stack: [...],
|
|
||||||
complexity: "medium",
|
|
||||||
effort_breakdown: { analysis: 2, design: 3, coding: 8, testing: 4 },
|
|
||||||
blockers: [...]
|
|
||||||
},
|
|
||||||
|
|
||||||
validation: {
|
|
||||||
risk_matrix: [...],
|
|
||||||
top_risks: [{ title, impact, mitigation }, ...],
|
|
||||||
testing_strategy: "...",
|
|
||||||
monitoring: [...]
|
|
||||||
},
|
|
||||||
|
|
||||||
recommendation: {
|
|
||||||
primary_approach: "...",
|
|
||||||
alternatives: [...],
|
|
||||||
critical_decisions: [...],
|
|
||||||
tradeoffs: [...]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write(`${sessionFolder}/perspectives.json`, JSON.stringify(perspectives, null, 2))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 4: Conflict Detection
|
|
||||||
|
|
||||||
Detect conflicts:
|
|
||||||
- Effort variance: Are estimates consistent?
|
|
||||||
- Risk disagreement: Do arch and validation agree on risks?
|
|
||||||
- Scope confusion: Are recommendations aligned?
|
|
||||||
- Architecture mismatch: Do design and implementation agree?
|
|
||||||
|
|
||||||
For each conflict: document it, then run synthesis arbitration.
|
|
||||||
|
|
||||||
### Phase 5: Generate Plan
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Quick Planning Session
|
|
||||||
|
|
||||||
**Session ID**: ${sessionId}
|
|
||||||
**Topic**: $TOPIC
|
|
||||||
**Created**: ${timestamp}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
${synthesis.executive_summary}
|
|
||||||
|
|
||||||
**Complexity**: ${synthesis.complexity_level}
|
|
||||||
**Estimated Effort**: ${formatEffort(synthesis.effort_breakdown)}
|
|
||||||
**Optimization Focus**: ${synthesis.optimization_focus}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
**Primary Pattern**: ${synthesis.architecture_approach}
|
|
||||||
|
|
||||||
**Key Components**:
|
|
||||||
${synthesis.key_components.map((c, i) => `${i+1}. ${c.name}: ${c.responsibility}`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Strategy
|
|
||||||
|
|
||||||
**Technology Stack**:
|
|
||||||
${synthesis.technology_stack.map(t => `- ${t}`).join('\n')}
|
|
||||||
|
|
||||||
**Phases**:
|
|
||||||
${synthesis.phases.map((p, i) => `${i+1}. ${p.name} (${p.effort})`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risk Assessment
|
|
||||||
|
|
||||||
**Overall Risk**: ${synthesis.overall_risk_level}
|
|
||||||
|
|
||||||
**Top 3 Risks**:
|
|
||||||
${synthesis.top_risks.map((r, i) => `${i+1}. **${r.title}** (Impact: ${r.impact})\n Mitigation: ${r.mitigation}`).join('\n\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task Breakdown (Ready for Execution)
|
|
||||||
|
|
||||||
${synthesis.tasks.map((task, i) => `
|
|
||||||
${i+1}. **${task.id}: ${task.title}** (Effort: ${task.effort})
|
|
||||||
${task.description}
|
|
||||||
Dependencies: ${task.dependencies.join(', ') || 'none'}
|
|
||||||
`).join('\n')}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
**Execute with**:
|
|
||||||
\`\`\`
|
|
||||||
/workflow:unified-execute-with-file -p ${sessionFolder}/synthesis.json
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
**Detailed planning if needed**:
|
|
||||||
\`\`\`
|
|
||||||
/workflow:plan "Based on: $TOPIC"
|
|
||||||
\`\`\`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Session Folder Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
.workflow/.planning/{sessionId}/
|
|
||||||
├── plan.md # Minimal, actionable
|
|
||||||
├── perspectives.json # Agent findings
|
|
||||||
├── conflicts.json # Conflicts & resolutions (if any)
|
|
||||||
└── synthesis.json # Task breakdown for execution
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Agent Coordination
|
|
||||||
|
|
||||||
| Agent | Perspective | Tools | Output |
|
|
||||||
|-------|-------------|-------|--------|
|
|
||||||
| Gemini (Design) | Architecture patterns | Design thinking, cross-domain | Components, patterns, scalability |
|
|
||||||
| Codex (Build) | Implementation reality | Tech stack evaluation | Stack, effort, feasibility |
|
|
||||||
| Claude (Validate) | Risk & testing | Risk assessment, QA | Risks, testing strategy |
|
|
||||||
| Gemini (Decide) | Strategic synthesis | Decision analysis | Recommendations, tradeoffs |
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
| Situation | Action |
|
|
||||||
|-----------|--------|
|
|
||||||
| Agents conflict | Arbitration agent synthesizes recommendation |
|
|
||||||
| Missing blockers | Continue with available context, note gaps |
|
|
||||||
| Unclear input | Ask for clarification on planning focus |
|
|
||||||
| Estimate too high | Suggest MVP approach or phasing |
|
|
||||||
|
|
||||||
## Integration Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
Raw Task / Brainstorm / Analysis
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
quick-plan-with-file (5-10 min)
|
|
||||||
│
|
|
||||||
├─ plan.md
|
|
||||||
├─ perspectives.json
|
|
||||||
└─ synthesis.json
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
unified-execute-with-file
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Implementation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Patterns
|
|
||||||
|
|
||||||
**Pattern 1: Quick planning from task**
|
|
||||||
```
|
|
||||||
TOPIC="实现实时通知系统" --auto
|
|
||||||
→ Creates actionable plan in ~5 minutes
|
|
||||||
```
|
|
||||||
|
|
||||||
**Pattern 2: Convert brainstorm to execution plan**
|
|
||||||
```
|
|
||||||
TOPIC="BS-notifications-2025-01-28" --from=brainstorm
|
|
||||||
→ Reads synthesis.json from brainstorm
|
|
||||||
→ Generates implementation plan
|
|
||||||
```
|
|
||||||
|
|
||||||
**Pattern 3: From analysis to plan**
|
|
||||||
```
|
|
||||||
TOPIC="ANL-auth-2025-01-28" --from=analysis
|
|
||||||
→ Converts conclusions.json to executable plan
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Now execute quick-plan-with-file for topic**: $TOPIC
|
|
||||||
@@ -525,6 +525,33 @@ function renderCliStatus() {
|
|||||||
const enabledCliSettings = isClaude ? cliSettingsEndpoints.filter(ep => ep.enabled) : [];
|
const enabledCliSettings = isClaude ? cliSettingsEndpoints.filter(ep => ep.enabled) : [];
|
||||||
const hasCliSettings = enabledCliSettings.length > 0;
|
const hasCliSettings = enabledCliSettings.length > 0;
|
||||||
|
|
||||||
|
// Build Settings File info for builtin Claude
|
||||||
|
let settingsFileInfo = '';
|
||||||
|
if (isClaude && config.type === 'builtin' && config.settingsFile) {
|
||||||
|
const settingsFile = config.settingsFile;
|
||||||
|
// Simple path resolution attempt for display (no actual filesystem access)
|
||||||
|
const resolvedPath = settingsFile.startsWith('~')
|
||||||
|
? settingsFile.replace('~', (typeof os !== 'undefined' && os.homedir) ? os.homedir() : '~')
|
||||||
|
: settingsFile;
|
||||||
|
|
||||||
|
settingsFileInfo = `
|
||||||
|
<div class="cli-settings-info mt-2 p-2 rounded bg-muted/50 text-xs">
|
||||||
|
<div class="flex items-center gap-1 text-muted-foreground mb-1">
|
||||||
|
<i data-lucide="file-key" class="w-3 h-3"></i>
|
||||||
|
<span>Settings File:</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-foreground font-mono text-[10px] break-all" title="${resolvedPath}">
|
||||||
|
${settingsFile}
|
||||||
|
</div>
|
||||||
|
${settingsFile !== resolvedPath ? `
|
||||||
|
<div class="text-muted-foreground mt-1 font-mono text-[10px] break-all">
|
||||||
|
→ ${resolvedPath}
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
// Build CLI Settings badge for Claude
|
// Build CLI Settings badge for Claude
|
||||||
let cliSettingsBadge = '';
|
let cliSettingsBadge = '';
|
||||||
if (isClaude && hasCliSettings) {
|
if (isClaude && hasCliSettings) {
|
||||||
@@ -588,6 +615,7 @@ function renderCliStatus() {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${settingsFileInfo}
|
||||||
${cliSettingsInfo}
|
${cliSettingsInfo}
|
||||||
<div class="cli-tool-actions mt-3 flex gap-2">
|
<div class="cli-tool-actions mt-3 flex gap-2">
|
||||||
${isAvailable ? (isEnabled
|
${isAvailable ? (isEnabled
|
||||||
|
|||||||
@@ -562,6 +562,27 @@ function buildToolConfigModalContent(tool, config, models, status) {
|
|||||||
'</div>'
|
'</div>'
|
||||||
) : '') +
|
) : '') +
|
||||||
|
|
||||||
|
// Claude Settings File Section (only for builtin claude type)
|
||||||
|
(tool === 'claude' && config.type === 'builtin' ? (
|
||||||
|
'<div class="tool-config-section">' +
|
||||||
|
'<h4><i data-lucide="file-key" class="w-3.5 h-3.5"></i> Settings File <span class="text-muted">(optional)</span></h4>' +
|
||||||
|
'<div class="env-file-input-group">' +
|
||||||
|
'<div class="env-file-input-row">' +
|
||||||
|
'<input type="text" id="claudeSettingsFileInput" class="tool-config-input" ' +
|
||||||
|
'placeholder="~/path/to/settings.json or D:\\path\\to\\settings.json" ' +
|
||||||
|
'value="' + (config.settingsFile ? escapeHtml(config.settingsFile) : '') + '" />' +
|
||||||
|
'<button type="button" class="btn-sm btn-outline" id="claudeSettingsFileBrowseBtn">' +
|
||||||
|
'<i data-lucide="folder-open" class="w-3.5 h-3.5"></i> Browse' +
|
||||||
|
'</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'<p class="env-file-hint">' +
|
||||||
|
'<i data-lucide="info" class="w-3 h-3"></i> ' +
|
||||||
|
'Path to Claude CLI settings.json file (supports ~, absolute, and Windows paths)' +
|
||||||
|
'</p>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>'
|
||||||
|
) : '') +
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
'<div class="tool-config-footer">' +
|
'<div class="tool-config-footer">' +
|
||||||
'<button class="btn btn-outline" onclick="closeModal()">' + t('common.cancel') + '</button>' +
|
'<button class="btn btn-outline" onclick="closeModal()">' + t('common.cancel') + '</button>' +
|
||||||
@@ -1124,6 +1145,10 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
|
|||||||
var envFileInput = document.getElementById('envFileInput');
|
var envFileInput = document.getElementById('envFileInput');
|
||||||
var envFile = envFileInput ? envFileInput.value.trim() : '';
|
var envFile = envFileInput ? envFileInput.value.trim() : '';
|
||||||
|
|
||||||
|
// Get settingsFile value (only for builtin claude)
|
||||||
|
var claudeSettingsFileInput = document.getElementById('claudeSettingsFileInput');
|
||||||
|
var settingsFile = claudeSettingsFileInput ? claudeSettingsFileInput.value.trim() : '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var updateData = {
|
var updateData = {
|
||||||
primaryModel: primaryModel,
|
primaryModel: primaryModel,
|
||||||
@@ -1137,6 +1162,11 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
|
|||||||
updateData.envFile = envFile || null;
|
updateData.envFile = envFile || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only include settingsFile for builtin claude tool
|
||||||
|
if (tool === 'claude' && config.type === 'builtin') {
|
||||||
|
updateData.settingsFile = settingsFile || null;
|
||||||
|
}
|
||||||
|
|
||||||
await updateCliToolConfig(tool, updateData);
|
await updateCliToolConfig(tool, updateData);
|
||||||
// Reload config to reflect changes
|
// Reload config to reflect changes
|
||||||
await loadCliToolConfig();
|
await loadCliToolConfig();
|
||||||
@@ -1164,6 +1194,20 @@ function initToolConfigModalEvents(tool, currentConfig, models) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Claude Settings File browse button (only for builtin claude)
|
||||||
|
var claudeSettingsFileBrowseBtn = document.getElementById('claudeSettingsFileBrowseBtn');
|
||||||
|
if (claudeSettingsFileBrowseBtn) {
|
||||||
|
claudeSettingsFileBrowseBtn.onclick = function() {
|
||||||
|
showFileBrowserModal(function(selectedPath) {
|
||||||
|
var claudeSettingsFileInput = document.getElementById('claudeSettingsFileInput');
|
||||||
|
if (claudeSettingsFileInput && selectedPath) {
|
||||||
|
claudeSettingsFileInput.value = selectedPath;
|
||||||
|
claudeSettingsFileInput.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize lucide icons in modal
|
// Initialize lucide icons in modal
|
||||||
if (window.lucide) lucide.createIcons();
|
if (window.lucide) lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ export interface ClaudeCliTool {
|
|||||||
* Supports both absolute paths and paths relative to home directory (e.g., ~/.my-env)
|
* Supports both absolute paths and paths relative to home directory (e.g., ~/.my-env)
|
||||||
*/
|
*/
|
||||||
envFile?: string;
|
envFile?: string;
|
||||||
|
/**
|
||||||
|
* Path to Claude CLI settings.json file (builtin claude only)
|
||||||
|
* Passed to Claude CLI via --settings parameter
|
||||||
|
* Supports ~, absolute, relative, and Windows paths
|
||||||
|
*/
|
||||||
|
settingsFile?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CliToolName = 'gemini' | 'qwen' | 'codex' | 'claude' | 'opencode' | string;
|
export type CliToolName = 'gemini' | 'qwen' | 'codex' | 'claude' | 'opencode' | string;
|
||||||
@@ -279,7 +285,8 @@ function ensureToolTags(tool: Partial<ClaudeCliTool>): ClaudeCliTool {
|
|||||||
primaryModel: tool.primaryModel,
|
primaryModel: tool.primaryModel,
|
||||||
secondaryModel: tool.secondaryModel,
|
secondaryModel: tool.secondaryModel,
|
||||||
tags: tool.tags ?? [],
|
tags: tool.tags ?? [],
|
||||||
envFile: tool.envFile
|
envFile: tool.envFile,
|
||||||
|
settingsFile: tool.settingsFile
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1015,6 +1022,7 @@ export function getToolConfig(projectDir: string, tool: string): {
|
|||||||
secondaryModel: string;
|
secondaryModel: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
envFile?: string;
|
envFile?: string;
|
||||||
|
settingsFile?: string;
|
||||||
} {
|
} {
|
||||||
const config = loadClaudeCliTools(projectDir);
|
const config = loadClaudeCliTools(projectDir);
|
||||||
const toolConfig = config.tools[tool];
|
const toolConfig = config.tools[tool];
|
||||||
@@ -1034,7 +1042,8 @@ export function getToolConfig(projectDir: string, tool: string): {
|
|||||||
primaryModel: toolConfig.primaryModel ?? '',
|
primaryModel: toolConfig.primaryModel ?? '',
|
||||||
secondaryModel: toolConfig.secondaryModel ?? '',
|
secondaryModel: toolConfig.secondaryModel ?? '',
|
||||||
tags: toolConfig.tags,
|
tags: toolConfig.tags,
|
||||||
envFile: toolConfig.envFile
|
envFile: toolConfig.envFile,
|
||||||
|
settingsFile: toolConfig.settingsFile
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1051,6 +1060,7 @@ export function updateToolConfig(
|
|||||||
availableModels: string[];
|
availableModels: string[];
|
||||||
tags: string[];
|
tags: string[];
|
||||||
envFile: string | null;
|
envFile: string | null;
|
||||||
|
settingsFile: string | null;
|
||||||
}>
|
}>
|
||||||
): ClaudeCliToolsConfig {
|
): ClaudeCliToolsConfig {
|
||||||
const config = loadClaudeCliTools(projectDir);
|
const config = loadClaudeCliTools(projectDir);
|
||||||
@@ -1079,6 +1089,14 @@ export function updateToolConfig(
|
|||||||
config.tools[tool].envFile = updates.envFile;
|
config.tools[tool].envFile = updates.envFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Handle settingsFile: set to undefined if null/empty, otherwise set value
|
||||||
|
if (updates.settingsFile !== undefined) {
|
||||||
|
if (updates.settingsFile === null || updates.settingsFile === '') {
|
||||||
|
delete config.tools[tool].settingsFile;
|
||||||
|
} else {
|
||||||
|
config.tools[tool].settingsFile = updates.settingsFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
saveClaudeCliTools(projectDir, config);
|
saveClaudeCliTools(projectDir, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { spawn, ChildProcess } from 'child_process';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { validatePath } from '../utils/path-resolver.js';
|
import { validatePath, resolvePath } from '../utils/path-resolver.js';
|
||||||
import { escapeWindowsArg } from '../utils/shell-escape.js';
|
import { escapeWindowsArg } from '../utils/shell-escape.js';
|
||||||
import { buildCommand, checkToolAvailability, clearToolCache, debugLog, errorLog, type NativeResumeConfig, type ToolAvailability } from './cli-executor-utils.js';
|
import { buildCommand, checkToolAvailability, clearToolCache, debugLog, errorLog, type NativeResumeConfig, type ToolAvailability } from './cli-executor-utils.js';
|
||||||
import type { ConversationRecord, ConversationTurn, ExecutionOutput, ExecutionRecord } from './cli-executor-state.js';
|
import type { ConversationRecord, ConversationTurn, ExecutionOutput, ExecutionRecord } from './cli-executor-state.js';
|
||||||
@@ -85,7 +85,7 @@ import { findEndpointById } from '../config/litellm-api-config-manager.js';
|
|||||||
|
|
||||||
// CLI Settings (CLI封装) integration
|
// CLI Settings (CLI封装) integration
|
||||||
import { loadEndpointSettings, getSettingsFilePath, findEndpoint } from '../config/cli-settings-manager.js';
|
import { loadEndpointSettings, getSettingsFilePath, findEndpoint } from '../config/cli-settings-manager.js';
|
||||||
import { loadClaudeCliTools, getToolConfig } from './claude-cli-tools.js';
|
import { loadClaudeCliTools, getToolConfig, getPrimaryModel } from './claude-cli-tools.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse .env file content into key-value pairs
|
* Parse .env file content into key-value pairs
|
||||||
@@ -338,8 +338,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
isToolEnabled as isToolEnabledFromConfig,
|
isToolEnabled as isToolEnabledFromConfig,
|
||||||
enableTool as enableToolFromConfig,
|
enableTool as enableToolFromConfig,
|
||||||
disableTool as disableToolFromConfig,
|
disableTool as disableToolFromConfig
|
||||||
getPrimaryModel
|
|
||||||
} from './cli-config-manager.js';
|
} from './cli-config-manager.js';
|
||||||
|
|
||||||
// Built-in CLI tools
|
// Built-in CLI tools
|
||||||
@@ -794,6 +793,25 @@ async function executeCliTool(
|
|||||||
// Use configured primary model if no explicit model provided
|
// Use configured primary model if no explicit model provided
|
||||||
const effectiveModel = model || getPrimaryModel(workingDir, tool);
|
const effectiveModel = model || getPrimaryModel(workingDir, tool);
|
||||||
|
|
||||||
|
// Load and validate settings file for Claude tool (builtin only)
|
||||||
|
let settingsFilePath: string | undefined;
|
||||||
|
if (tool === 'claude') {
|
||||||
|
const toolConfig = getToolConfig(workingDir, tool);
|
||||||
|
if (toolConfig.settingsFile) {
|
||||||
|
try {
|
||||||
|
const resolved = resolvePath(toolConfig.settingsFile);
|
||||||
|
if (fs.existsSync(resolved)) {
|
||||||
|
settingsFilePath = resolved;
|
||||||
|
debugLog('SETTINGS_FILE', `Resolved Claude settings file`, { configured: toolConfig.settingsFile, resolved });
|
||||||
|
} else {
|
||||||
|
errorLog('SETTINGS_FILE', `Claude settings file not found, skipping`, { configured: toolConfig.settingsFile, resolved });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
errorLog('SETTINGS_FILE', `Failed to resolve Claude settings file`, { configured: toolConfig.settingsFile, error: (err as Error).message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build command
|
// Build command
|
||||||
const { command, args, useStdin, outputFormat: autoDetectedFormat } = buildCommand({
|
const { command, args, useStdin, outputFormat: autoDetectedFormat } = buildCommand({
|
||||||
tool,
|
tool,
|
||||||
@@ -803,6 +821,7 @@ async function executeCliTool(
|
|||||||
dir: cd,
|
dir: cd,
|
||||||
include: includeDirs,
|
include: includeDirs,
|
||||||
nativeResume: nativeResumeConfig,
|
nativeResume: nativeResumeConfig,
|
||||||
|
settingsFile: settingsFilePath,
|
||||||
reviewOptions: mode === 'review' ? { uncommitted, base, commit, title } : undefined
|
reviewOptions: mode === 'review' ? { uncommitted, base, commit, title } : undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
48
test-command-build.mjs
Normal file
48
test-command-build.mjs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { buildCommand } from './ccw/dist/tools/cli-executor-utils.js';
|
||||||
|
import { getToolConfig } from './ccw/dist/tools/claude-cli-tools.js';
|
||||||
|
import { resolvePath } from './ccw/dist/utils/path-resolver.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const workingDir = 'D:\\Claude_dms3';
|
||||||
|
const tool = 'claude';
|
||||||
|
|
||||||
|
// Get tool config
|
||||||
|
const toolConfig = getToolConfig(workingDir, tool);
|
||||||
|
console.log('=== Tool Config ===');
|
||||||
|
console.log(JSON.stringify(toolConfig, null, 2));
|
||||||
|
|
||||||
|
// Resolve settings file
|
||||||
|
let settingsFilePath = undefined;
|
||||||
|
if (toolConfig.settingsFile) {
|
||||||
|
try {
|
||||||
|
const resolved = resolvePath(toolConfig.settingsFile);
|
||||||
|
console.log(`\n=== Settings File Resolution ===`);
|
||||||
|
console.log(`Configured: ${toolConfig.settingsFile}`);
|
||||||
|
console.log(`Resolved: ${resolved}`);
|
||||||
|
console.log(`Exists: ${fs.existsSync(resolved)}`);
|
||||||
|
|
||||||
|
if (fs.existsSync(resolved)) {
|
||||||
|
settingsFilePath = resolved;
|
||||||
|
console.log(`✓ Will use settings file: ${settingsFilePath}`);
|
||||||
|
} else {
|
||||||
|
console.log(`✗ File not found, skipping`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`✗ Error resolving: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build command
|
||||||
|
const cmdInfo = buildCommand({
|
||||||
|
tool: 'claude',
|
||||||
|
prompt: 'test prompt',
|
||||||
|
mode: 'analysis',
|
||||||
|
model: 'sonnet',
|
||||||
|
settingsFile: settingsFilePath
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n=== Command Built ===');
|
||||||
|
console.log('Command:', cmdInfo.command);
|
||||||
|
console.log('Args:', JSON.stringify(cmdInfo.args, null, 2));
|
||||||
|
console.log('\n=== Full Command Line ===');
|
||||||
|
console.log(`${cmdInfo.command} ${cmdInfo.args.join(' ')}`);
|
||||||
16
test-config-debug.mjs
Normal file
16
test-config-debug.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { getToolConfig, loadClaudeCliTools } from './ccw/dist/tools/claude-cli-tools.js';
|
||||||
|
|
||||||
|
const workingDir = 'D:\\Claude_dms3';
|
||||||
|
const tool = 'claude';
|
||||||
|
|
||||||
|
console.log('=== Step 1: Load full config ===');
|
||||||
|
const fullConfig = loadClaudeCliTools(workingDir);
|
||||||
|
console.log('Full config:', JSON.stringify(fullConfig, null, 2));
|
||||||
|
|
||||||
|
console.log('\n=== Step 2: Get claude tool from config ===');
|
||||||
|
const claudeTool = fullConfig.tools.claude;
|
||||||
|
console.log('Claude tool raw:', JSON.stringify(claudeTool, null, 2));
|
||||||
|
|
||||||
|
console.log('\n=== Step 3: Get tool config via getToolConfig ===');
|
||||||
|
const config = getToolConfig(workingDir, tool);
|
||||||
|
console.log('Claude tool config:', JSON.stringify(config, null, 2));
|
||||||
7
test-config.js
Normal file
7
test-config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const { getToolConfig } = require('./ccw/dist/tools/claude-cli-tools.js');
|
||||||
|
|
||||||
|
const workingDir = 'D:\\Claude_dms3';
|
||||||
|
const tool = 'claude';
|
||||||
|
|
||||||
|
const config = getToolConfig(workingDir, tool);
|
||||||
|
console.log('Claude tool config:', JSON.stringify(config, null, 2));
|
||||||
7
test-config.mjs
Normal file
7
test-config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { getToolConfig } from './ccw/dist/tools/claude-cli-tools.js';
|
||||||
|
|
||||||
|
const workingDir = 'D:\\Claude_dms3';
|
||||||
|
const tool = 'claude';
|
||||||
|
|
||||||
|
const config = getToolConfig(workingDir, tool);
|
||||||
|
console.log('Claude tool config:', JSON.stringify(config, null, 2));
|
||||||
Reference in New Issue
Block a user