mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-13 02:41:50 +08:00
Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -10,63 +10,33 @@ allowed-tools: TodoWrite(*), Task(*), AskUserQuestion(*), Read(*), Bash(*), mcp_
|
|||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Basic usage
|
|
||||||
/workflow:lite-lite-lite "Fix the login bug"
|
/workflow:lite-lite-lite "Fix the login bug"
|
||||||
|
|
||||||
# Complex task
|
|
||||||
/workflow:lite-lite-lite "Refactor payment module for multi-gateway support"
|
/workflow:lite-lite-lite "Refactor payment module for multi-gateway support"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Core Philosophy**: Minimal friction, maximum velocity. No files, no artifacts - just analyze and execute.
|
**Core Philosophy**: Minimal friction, maximum velocity. No files, no artifacts - just analyze and execute.
|
||||||
|
|
||||||
## What & Why
|
## Overview
|
||||||
|
|
||||||
### Core Concept
|
**Zero-artifact workflow**: Clarify → Select Tools → Multi-Mode Analysis → Decision → Direct Execution
|
||||||
|
|
||||||
**Zero-artifact workflow**: Clarify requirements → Auto-select tools → Mixed tool analysis → User decision → Direct execution. All state in memory, all decisions via AskUser.
|
**vs multi-cli-plan**: No IMPL_PLAN.md, plan.json, synthesis.json - all state in memory.
|
||||||
|
|
||||||
**vs multi-cli-plan**:
|
|
||||||
- **multi-cli-plan**: Full artifacts (IMPL_PLAN.md, plan.json, synthesis.json)
|
|
||||||
- **lite-lite-lite**: No files, direct in-memory flow, immediate execution
|
|
||||||
|
|
||||||
### Value Proposition
|
|
||||||
|
|
||||||
1. **Ultra-Fast**: No file I/O overhead, no session management
|
|
||||||
2. **Smart Selection**: Auto-select optimal tool combination based on task
|
|
||||||
3. **Interactive**: Key decisions validated via AskUser
|
|
||||||
4. **Direct**: Analysis → Execution without intermediate artifacts
|
|
||||||
|
|
||||||
## Execution Flow
|
## Execution Flow
|
||||||
|
|
||||||
```
|
```
|
||||||
Phase 1: Clarify Requirements
|
Phase 1: Clarify Requirements → AskUser for missing details
|
||||||
└─ Parse input → AskUser for missing details (if needed)
|
Phase 2: Select Tools (CLI → Mode → Agent) → 3-step selection
|
||||||
|
Phase 3: Multi-Mode Analysis → Execute with --resume chaining
|
||||||
Phase 2: Auto-Select Tools
|
Phase 4: User Decision → Execute / Refine / Change / Cancel
|
||||||
└─ Analyze task → Match to tool strengths → Confirm selection
|
Phase 5: Direct Execution → No plan files, immediate implementation
|
||||||
|
|
||||||
Phase 3: Mixed Tool Analysis
|
|
||||||
└─ Execute selected tools in parallel → Aggregate results
|
|
||||||
|
|
||||||
Phase 4: User Decision
|
|
||||||
├─ Present analysis summary
|
|
||||||
├─ AskUser: Execute / Refine / Change tools / Cancel
|
|
||||||
└─ Loop to Phase 3 if refinement needed
|
|
||||||
|
|
||||||
Phase 5: Direct Execution
|
|
||||||
└─ Execute solution directly (no plan files)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Phase Details
|
## Phase 1: Clarify Requirements
|
||||||
|
|
||||||
### Phase 1: Clarify Requirements
|
|
||||||
|
|
||||||
**Parse Task Description**:
|
|
||||||
```javascript
|
```javascript
|
||||||
// Extract intent from user input
|
|
||||||
const taskDescription = $ARGUMENTS
|
const taskDescription = $ARGUMENTS
|
||||||
|
|
||||||
// Check if clarification needed
|
|
||||||
if (taskDescription.length < 20 || isAmbiguous(taskDescription)) {
|
if (taskDescription.length < 20 || isAmbiguous(taskDescription)) {
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
@@ -80,173 +50,72 @@ if (taskDescription.length < 20 || isAmbiguous(taskDescription)) {
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
**Quick ACE Context** (optional, for complex tasks):
|
// Optional: Quick ACE Context for complex tasks
|
||||||
```javascript
|
|
||||||
// Only if task seems to need codebase context
|
|
||||||
mcp__ace-tool__search_context({
|
mcp__ace-tool__search_context({
|
||||||
project_root_path: process.cwd(),
|
project_root_path: process.cwd(),
|
||||||
query: `${taskDescription} implementation patterns`
|
query: `${taskDescription} implementation patterns`
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 2: Auto-Select Analysis Tools
|
## Phase 2: Select Tools
|
||||||
|
|
||||||
**Tool Categories**:
|
### Tool Definitions
|
||||||
|
|
||||||
| Category | Source | Execution |
|
**CLI Tools** (from cli-tools.json):
|
||||||
|----------|--------|-----------|
|
|
||||||
| **CLI Tools** | cli-tools.json | `ccw cli -p "..." --tool <name>` |
|
|
||||||
| **Sub Agents** | Task tool | `Task({ subagent_type: "...", prompt: "..." })` |
|
|
||||||
|
|
||||||
**Task Analysis Dimensions**:
|
|
||||||
```javascript
|
```javascript
|
||||||
function analyzeTask(taskDescription) {
|
|
||||||
return {
|
|
||||||
complexity: detectComplexity(taskDescription), // simple, medium, complex
|
|
||||||
taskType: detectTaskType(taskDescription), // bugfix, feature, refactor, analysis, etc.
|
|
||||||
domain: detectDomain(taskDescription), // frontend, backend, fullstack
|
|
||||||
needsExecution: detectExecutionNeed(taskDescription) // analysis-only vs needs-write
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**CLI Tools** (dynamically loaded from cli-tools.json):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Load CLI tools from config file
|
|
||||||
const cliConfig = JSON.parse(Read("~/.claude/cli-tools.json"))
|
const cliConfig = JSON.parse(Read("~/.claude/cli-tools.json"))
|
||||||
const cliTools = Object.entries(cliConfig.tools)
|
const cliTools = Object.entries(cliConfig.tools)
|
||||||
.filter(([_, config]) => config.enabled)
|
.filter(([_, config]) => config.enabled)
|
||||||
.map(([name, config]) => ({
|
.map(([name, config]) => ({
|
||||||
name,
|
name, type: 'cli',
|
||||||
type: 'cli',
|
|
||||||
tags: config.tags || [],
|
tags: config.tags || [],
|
||||||
model: config.primaryModel,
|
model: config.primaryModel,
|
||||||
toolType: config.type // builtin, cli-wrapper, api-endpoint
|
toolType: config.type // builtin, cli-wrapper, api-endpoint
|
||||||
}))
|
}))
|
||||||
```
|
```
|
||||||
|
|
||||||
**Tags** (user-defined in cli-tools.json, no fixed specification):
|
**Sub Agents**:
|
||||||
|
|
||||||
Tags are completely user-defined. Users can create any tags that match their workflow needs.
|
|
||||||
|
|
||||||
**Config Example** (cli-tools.json):
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"tools": {
|
|
||||||
"gemini": {
|
|
||||||
"enabled": true,
|
|
||||||
"tags": ["architecture", "reasoning", "performance"],
|
|
||||||
"primaryModel": "gemini-2.5-pro"
|
|
||||||
},
|
|
||||||
"codex": {
|
|
||||||
"enabled": true,
|
|
||||||
"tags": ["implementation", "fast"],
|
|
||||||
"primaryModel": "gpt-5.2"
|
|
||||||
},
|
|
||||||
"qwen": {
|
|
||||||
"enabled": true,
|
|
||||||
"tags": ["implementation", "chinese", "documentation"],
|
|
||||||
"primaryModel": "coder-model"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Sub Agents** (predefined, canExecute marks execution capability):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const agents = [
|
|
||||||
{ name: 'code-developer', type: 'agent', strength: 'Code implementation, test writing', canExecute: true },
|
|
||||||
{ name: 'Explore', type: 'agent', strength: 'Fast code exploration', canExecute: false },
|
|
||||||
{ name: 'cli-explore-agent', type: 'agent', strength: 'Dual-source deep analysis', canExecute: false },
|
|
||||||
{ name: 'cli-discuss-agent', type: 'agent', strength: 'Multi-CLI collaborative verification', canExecute: false },
|
|
||||||
{ name: 'debug-explore-agent', type: 'agent', strength: 'Hypothesis-driven debugging', canExecute: false },
|
|
||||||
{ name: 'context-search-agent', type: 'agent', strength: 'Context collection', canExecute: false },
|
|
||||||
{ name: 'test-fix-agent', type: 'agent', strength: 'Test execution and fixing', canExecute: true },
|
|
||||||
{ name: 'universal-executor', type: 'agent', strength: 'General multi-step execution', canExecute: true }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
| Agent | Strengths | canExecute |
|
| Agent | Strengths | canExecute |
|
||||||
|-------|-----------|------------|
|
|-------|-----------|------------|
|
||||||
| **code-developer** | Code implementation, test writing, incremental development | ✅ |
|
| **code-developer** | Code implementation, test writing | ✅ |
|
||||||
| **Explore** | Fast code exploration, file search, pattern discovery | ❌ |
|
| **Explore** | Fast code exploration, pattern discovery | ❌ |
|
||||||
| **cli-explore-agent** | Dual-source analysis (Bash+CLI), read-only exploration | ❌ |
|
| **cli-explore-agent** | Dual-source analysis (Bash+CLI) | ❌ |
|
||||||
| **cli-discuss-agent** | Multi-CLI collaboration, cross-verification, solution synthesis | ❌ |
|
| **cli-discuss-agent** | Multi-CLI collaboration, cross-verification | ❌ |
|
||||||
| **debug-explore-agent** | Hypothesis-driven debugging, NDJSON logging, iterative verification | ❌ |
|
| **debug-explore-agent** | Hypothesis-driven debugging | ❌ |
|
||||||
| **context-search-agent** | Multi-layer file discovery, dependency analysis, conflict assessment | ❌ |
|
| **context-search-agent** | Multi-layer file discovery, dependency analysis | ❌ |
|
||||||
| **test-fix-agent** | Test execution, failure diagnosis, code fixing | ✅ |
|
| **test-fix-agent** | Test execution, failure diagnosis, code fixing | ✅ |
|
||||||
| **universal-executor** | General execution, multi-domain adaptation | ✅ |
|
| **universal-executor** | General execution, multi-domain adaptation | ✅ |
|
||||||
|
|
||||||
**Three-Step Selection Flow** (CLI → Mode → Agent):
|
**Analysis Modes**:
|
||||||
|
|
||||||
|
| Mode | Pattern | Use Case | minCLIs |
|
||||||
|
|------|---------|----------|---------|
|
||||||
|
| **Parallel** | `A \|\| B \|\| C → Aggregate` | Fast multi-perspective | 1+ |
|
||||||
|
| **Sequential** | `A → B(resume) → C(resume)` | Incremental deepening | 2+ |
|
||||||
|
| **Collaborative** | `A → B → A → B → Synthesize` | Multi-round refinement | 2+ |
|
||||||
|
| **Debate** | `A(propose) → B(challenge) → A(defend)` | Adversarial validation | 2 |
|
||||||
|
| **Challenge** | `A(analyze) → B(challenge)` | Find flaws and risks | 2 |
|
||||||
|
|
||||||
|
### Three-Step Selection Flow
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Step 1: Present CLI options from config (multiSelect for multi-CLI modes)
|
// Step 1: Select CLIs (multiSelect)
|
||||||
function getCliDescription(cli) {
|
|
||||||
return cli.tags.length > 0 ? cli.tags.join(', ') : cli.model || 'general'
|
|
||||||
}
|
|
||||||
|
|
||||||
const cliOptions = cliTools.map(cli => ({
|
|
||||||
label: cli.name,
|
|
||||||
description: getCliDescription(cli)
|
|
||||||
}))
|
|
||||||
|
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
question: "Select CLI tools for analysis (select 1-3 for collaboration modes)",
|
question: "Select CLI tools for analysis (1-3 for collaboration modes)",
|
||||||
header: "CLI Tools",
|
header: "CLI Tools",
|
||||||
options: cliOptions,
|
options: cliTools.map(cli => ({
|
||||||
multiSelect: true // Allow multiple selection for collaboration modes
|
label: cli.name,
|
||||||
|
description: cli.tags.length > 0 ? cli.tags.join(', ') : cli.model || 'general'
|
||||||
|
})),
|
||||||
|
multiSelect: true
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
// Step 2: Select Mode (filtered by CLI count)
|
||||||
// Step 2: Select Analysis Mode
|
|
||||||
const analysisModes = [
|
|
||||||
{
|
|
||||||
name: 'parallel',
|
|
||||||
label: 'Parallel',
|
|
||||||
description: 'All CLIs analyze simultaneously, aggregate results',
|
|
||||||
minCLIs: 1,
|
|
||||||
pattern: 'A || B || C → Aggregate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sequential',
|
|
||||||
label: 'Sequential',
|
|
||||||
description: 'Chain analysis: each CLI builds on previous via --resume',
|
|
||||||
minCLIs: 2,
|
|
||||||
pattern: 'A → B(resume A) → C(resume B)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'collaborative',
|
|
||||||
label: 'Collaborative',
|
|
||||||
description: 'Multi-round synthesis: CLIs take turns refining analysis',
|
|
||||||
minCLIs: 2,
|
|
||||||
pattern: 'A → B(resume A) → A(resume B) → Synthesize'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'debate',
|
|
||||||
label: 'Debate',
|
|
||||||
description: 'Adversarial: CLI B challenges CLI A findings, A responds',
|
|
||||||
minCLIs: 2,
|
|
||||||
pattern: 'A(propose) → B(challenge, resume A) → A(defend, resume B)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'challenge',
|
|
||||||
label: 'Challenge',
|
|
||||||
description: 'Stress test: CLI B finds flaws/alternatives in CLI A analysis',
|
|
||||||
minCLIs: 2,
|
|
||||||
pattern: 'A(analyze) → B(challenge, resume A) → Evaluate'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Filter modes based on selected CLI count
|
|
||||||
const availableModes = analysisModes.filter(m => selectedCLIs.length >= m.minCLIs)
|
const availableModes = analysisModes.filter(m => selectedCLIs.length >= m.minCLIs)
|
||||||
|
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
question: "Select analysis mode",
|
question: "Select analysis mode",
|
||||||
@@ -258,43 +127,24 @@ AskUserQuestion({
|
|||||||
multiSelect: false
|
multiSelect: false
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
```
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Step 3: Present Agent options for execution
|
|
||||||
const agentOptions = agents.map(agent => ({
|
|
||||||
label: agent.name,
|
|
||||||
description: agent.strength
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
// Step 3: Select Agent for execution
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
question: "Select Sub Agent for execution",
|
question: "Select Sub Agent for execution",
|
||||||
header: "Agent",
|
header: "Agent",
|
||||||
options: agentOptions,
|
options: agents.map(a => ({ label: a.name, description: a.strength })),
|
||||||
multiSelect: false
|
multiSelect: false
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
```
|
|
||||||
|
|
||||||
**Selection Summary**:
|
|
||||||
```javascript
|
|
||||||
console.log(`
|
|
||||||
## Selected Configuration
|
|
||||||
|
|
||||||
**CLI Tools**: ${selectedCLIs.map(c => c.name).join(' → ')}
|
|
||||||
**Analysis Mode**: ${selectedMode.label} - ${selectedMode.pattern}
|
|
||||||
**Execution Agent**: ${selectedAgent.name} - ${selectedAgent.strength}
|
|
||||||
|
|
||||||
> Mode determines how CLIs collaborate, Agent handles final execution
|
|
||||||
`)
|
|
||||||
|
|
||||||
|
// Confirm selection
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
question: "Confirm selection?",
|
question: "Confirm selection?",
|
||||||
header: "Confirm",
|
header: "Confirm",
|
||||||
options: [
|
options: [
|
||||||
{ label: "Confirm and continue", description: `${selectedMode.label} mode with ${selectedCLIs.length} CLIs` },
|
{ label: "Confirm and continue", description: `${selectedMode.label} with ${selectedCLIs.length} CLIs` },
|
||||||
{ label: "Re-select CLIs", description: "Choose different CLI tools" },
|
{ label: "Re-select CLIs", description: "Choose different CLI tools" },
|
||||||
{ label: "Re-select Mode", description: "Choose different analysis mode" },
|
{ label: "Re-select Mode", description: "Choose different analysis mode" },
|
||||||
{ label: "Re-select Agent", description: "Choose different Sub Agent" }
|
{ label: "Re-select Agent", description: "Choose different Sub Agent" }
|
||||||
@@ -304,409 +154,226 @@ AskUserQuestion({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 3: Multi-Mode Analysis
|
## Phase 3: Multi-Mode Analysis
|
||||||
|
|
||||||
**Mode-Specific Execution Patterns**:
|
### Universal CLI Prompt Template
|
||||||
|
|
||||||
#### Mode 1: Parallel (并行)
|
|
||||||
```javascript
|
```javascript
|
||||||
// All CLIs run simultaneously, no resume dependency
|
// Unified prompt builder - used by all modes
|
||||||
async function executeParallel(clis, taskDescription) {
|
function buildPrompt({ purpose, tasks, expected, rules, taskDescription }) {
|
||||||
const promises = clis.map(cli => Bash({
|
return `
|
||||||
command: `ccw cli -p "
|
PURPOSE: ${purpose}: ${taskDescription}
|
||||||
PURPOSE: Analyze and provide solution for: ${taskDescription}
|
TASK: ${tasks.map(t => `• ${t}`).join(' ')}
|
||||||
TASK: • Identify affected files • Analyze implementation approach • List specific changes needed
|
|
||||||
MODE: analysis
|
MODE: analysis
|
||||||
CONTEXT: @**/*
|
CONTEXT: @**/*
|
||||||
EXPECTED: Concise analysis with: 1) Root cause/approach 2) Files to modify 3) Key changes 4) Risks
|
EXPECTED: ${expected}
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Focus on actionable insights
|
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | ${rules}
|
||||||
" --tool ${cli.name} --mode analysis`,
|
`
|
||||||
run_in_background: true
|
}
|
||||||
}))
|
|
||||||
|
|
||||||
return await Promise.all(promises)
|
// Execute CLI with prompt
|
||||||
|
function execCLI(cli, prompt, options = {}) {
|
||||||
|
const { resume, background = false } = options
|
||||||
|
const resumeFlag = resume ? `--resume ${resume}` : ''
|
||||||
|
return Bash({
|
||||||
|
command: `ccw cli -p "${prompt}" --tool ${cli.name} --mode analysis ${resumeFlag}`,
|
||||||
|
run_in_background: background
|
||||||
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Mode 2: Sequential (串联)
|
### Prompt Presets by Role
|
||||||
|
|
||||||
|
| Role | PURPOSE | TASKS | EXPECTED | RULES |
|
||||||
|
|------|---------|-------|----------|-------|
|
||||||
|
| **initial** | Initial analysis | Identify files, Analyze approach, List changes | Root cause, files, changes, risks | Focus on actionable insights |
|
||||||
|
| **extend** | Build on previous | Review previous, Extend, Add insights | Extended analysis building on findings | Build incrementally, avoid repetition |
|
||||||
|
| **synthesize** | Refine and synthesize | Review, Identify gaps, Synthesize | Refined synthesis with new perspectives | Add value not repetition |
|
||||||
|
| **propose** | Propose comprehensive analysis | Analyze thoroughly, Propose solution, State assumptions | Well-reasoned proposal with trade-offs | Be clear about assumptions |
|
||||||
|
| **challenge** | Challenge and stress-test | Identify weaknesses, Question assumptions, Suggest alternatives | Critique with counter-arguments | Be adversarial but constructive |
|
||||||
|
| **defend** | Respond to challenges | Address challenges, Defend valid aspects, Propose refined solution | Refined proposal incorporating feedback | Be open to criticism, synthesize |
|
||||||
|
| **criticize** | Find flaws ruthlessly | Find logical flaws, Identify edge cases, Rate criticisms | Critique with severity: [CRITICAL]/[HIGH]/[MEDIUM]/[LOW] | Be ruthlessly critical |
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Chain analysis: each CLI builds on previous via --resume
|
const PROMPTS = {
|
||||||
async function executeSequential(clis, taskDescription) {
|
initial: { purpose: 'Initial analysis', tasks: ['Identify affected files', 'Analyze implementation approach', 'List specific changes'], expected: 'Root cause, files to modify, key changes, risks', rules: 'Focus on actionable insights' },
|
||||||
|
extend: { purpose: 'Build on previous analysis', tasks: ['Review previous findings', 'Extend analysis', 'Add new insights'], expected: 'Extended analysis building on previous', rules: 'Build incrementally, avoid repetition' },
|
||||||
|
synthesize: { purpose: 'Refine and synthesize', tasks: ['Review previous', 'Identify gaps', 'Add insights', 'Synthesize findings'], expected: 'Refined synthesis with new perspectives', rules: 'Build collaboratively, add value' },
|
||||||
|
propose: { purpose: 'Propose comprehensive analysis', tasks: ['Analyze thoroughly', 'Propose solution', 'State assumptions clearly'], expected: 'Well-reasoned proposal with trade-offs', rules: 'Be clear about assumptions' },
|
||||||
|
challenge: { purpose: 'Challenge and stress-test', tasks: ['Identify weaknesses', 'Question assumptions', 'Suggest alternatives', 'Highlight overlooked risks'], expected: 'Constructive critique with counter-arguments', rules: 'Be adversarial but constructive' },
|
||||||
|
defend: { purpose: 'Respond to challenges', tasks: ['Address each challenge', 'Defend valid aspects', 'Acknowledge valid criticisms', 'Propose refined solution'], expected: 'Refined proposal incorporating alternatives', rules: 'Be open to criticism, synthesize best ideas' },
|
||||||
|
criticize: { purpose: 'Stress-test and find weaknesses', tasks: ['Find logical flaws', 'Identify missed edge cases', 'Propose alternatives', 'Rate criticisms (High/Medium/Low)'], expected: 'Detailed critique with severity ratings', rules: 'Be ruthlessly critical, find every flaw' }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mode Implementations
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Parallel: All CLIs run simultaneously
|
||||||
|
async function executeParallel(clis, task) {
|
||||||
|
return await Promise.all(clis.map(cli =>
|
||||||
|
execCLI(cli, buildPrompt({ ...PROMPTS.initial, taskDescription: task }), { background: true })
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequential: Each CLI builds on previous via --resume
|
||||||
|
async function executeSequential(clis, task) {
|
||||||
const results = []
|
const results = []
|
||||||
let previousSessionId = null
|
let prevId = null
|
||||||
|
|
||||||
for (const cli of clis) {
|
for (const cli of clis) {
|
||||||
const resumeFlag = previousSessionId ? `--resume ${previousSessionId}` : ''
|
const preset = prevId ? PROMPTS.extend : PROMPTS.initial
|
||||||
|
const result = await execCLI(cli, buildPrompt({ ...preset, taskDescription: task }), { resume: prevId })
|
||||||
const result = await Bash({
|
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: ${previousSessionId ? 'Build on previous analysis and deepen' : 'Initial analysis'}: ${taskDescription}
|
|
||||||
TASK: • ${previousSessionId ? 'Review previous findings • Extend analysis • Add new insights' : 'Identify affected files • Analyze implementation approach'}
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: ${previousSessionId ? 'Extended analysis building on previous findings' : 'Initial analysis with root cause and approach'}
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | ${previousSessionId ? 'Build incrementally, avoid repetition' : 'Focus on actionable insights'}
|
|
||||||
" --tool ${cli.name} --mode analysis ${resumeFlag}`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
|
|
||||||
results.push(result)
|
results.push(result)
|
||||||
previousSessionId = extractSessionId(result) // Extract session ID for next iteration
|
prevId = extractSessionId(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
#### Mode 3: Collaborative (协同)
|
// Collaborative: Multi-round synthesis
|
||||||
```javascript
|
async function executeCollaborative(clis, task, rounds = 2) {
|
||||||
// Multi-round synthesis: CLIs take turns refining analysis
|
|
||||||
async function executeCollaborative(clis, taskDescription, rounds = 2) {
|
|
||||||
const results = []
|
const results = []
|
||||||
let previousSessionId = null
|
let prevId = null
|
||||||
|
for (let r = 0; r < rounds; r++) {
|
||||||
for (let round = 0; round < rounds; round++) {
|
|
||||||
for (const cli of clis) {
|
for (const cli of clis) {
|
||||||
const resumeFlag = previousSessionId ? `--resume ${previousSessionId}` : ''
|
const preset = !prevId ? PROMPTS.initial : PROMPTS.synthesize
|
||||||
const roundContext = round === 0 ? 'Initial analysis' : `Round ${round + 1}: Refine and synthesize`
|
const result = await execCLI(cli, buildPrompt({ ...preset, taskDescription: task }), { resume: prevId })
|
||||||
|
results.push({ cli: cli.name, round: r, result })
|
||||||
const result = await Bash({
|
prevId = extractSessionId(result)
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: ${roundContext} for: ${taskDescription}
|
|
||||||
TASK: • ${round === 0 ? 'Initial analysis of the problem' : 'Review previous analysis • Identify gaps • Add complementary insights • Synthesize findings'}
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: ${round === 0 ? 'Foundational analysis' : 'Refined synthesis with new perspectives'}
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | ${round === 0 ? 'Be thorough' : 'Build collaboratively, add value not repetition'}
|
|
||||||
" --tool ${cli.name} --mode analysis ${resumeFlag}`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
|
|
||||||
results.push({ cli: cli.name, round, result })
|
|
||||||
previousSessionId = extractSessionId(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
#### Mode 4: Debate (辩论)
|
// Debate: Propose → Challenge → Defend
|
||||||
```javascript
|
async function executeDebate(clis, task) {
|
||||||
// Adversarial: CLI B challenges CLI A findings, A responds
|
|
||||||
async function executeDebate(clis, taskDescription) {
|
|
||||||
const [cliA, cliB] = clis
|
const [cliA, cliB] = clis
|
||||||
const results = []
|
const results = []
|
||||||
|
|
||||||
// Step 1: CLI A proposes initial analysis
|
const propose = await execCLI(cliA, buildPrompt({ ...PROMPTS.propose, taskDescription: task }))
|
||||||
const proposeResult = await Bash({
|
results.push({ phase: 'propose', cli: cliA.name, result: propose })
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Propose comprehensive analysis for: ${taskDescription}
|
|
||||||
TASK: • Analyze problem thoroughly • Propose solution approach • Identify implementation details • State assumptions clearly
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: Well-reasoned proposal with clear assumptions and trade-offs stated
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Be clear about assumptions and trade-offs
|
|
||||||
" --tool ${cliA.name} --mode analysis`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
results.push({ phase: 'propose', cli: cliA.name, result: proposeResult })
|
|
||||||
const proposeSessionId = extractSessionId(proposeResult)
|
|
||||||
|
|
||||||
// Step 2: CLI B challenges the proposal
|
const challenge = await execCLI(cliB, buildPrompt({ ...PROMPTS.challenge, taskDescription: task }), { resume: extractSessionId(propose) })
|
||||||
const challengeResult = await Bash({
|
results.push({ phase: 'challenge', cli: cliB.name, result: challenge })
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Challenge and stress-test the previous analysis for: ${taskDescription}
|
|
||||||
TASK: • Identify weaknesses in proposed approach • Question assumptions • Suggest alternative approaches • Highlight potential risks overlooked
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: Constructive critique with specific counter-arguments and alternatives
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Be adversarial but constructive, focus on improving the solution
|
|
||||||
" --tool ${cliB.name} --mode analysis --resume ${proposeSessionId}`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
results.push({ phase: 'challenge', cli: cliB.name, result: challengeResult })
|
|
||||||
const challengeSessionId = extractSessionId(challengeResult)
|
|
||||||
|
|
||||||
// Step 3: CLI A defends and refines
|
const defend = await execCLI(cliA, buildPrompt({ ...PROMPTS.defend, taskDescription: task }), { resume: extractSessionId(challenge) })
|
||||||
const defendResult = await Bash({
|
results.push({ phase: 'defend', cli: cliA.name, result: defend })
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Respond to challenges and refine analysis for: ${taskDescription}
|
|
||||||
TASK: • Address each challenge point • Defend valid aspects • Acknowledge valid criticisms • Propose refined solution incorporating feedback
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: Refined proposal that addresses criticisms and incorporates valid alternatives
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Be open to valid criticism, synthesize best ideas
|
|
||||||
" --tool ${cliA.name} --mode analysis --resume ${challengeSessionId}`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
results.push({ phase: 'defend', cli: cliA.name, result: defendResult })
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
#### Mode 5: Challenge (挑战)
|
// Challenge: Analyze → Criticize
|
||||||
```javascript
|
async function executeChallenge(clis, task) {
|
||||||
// Stress test: CLI B finds flaws/alternatives in CLI A analysis
|
|
||||||
async function executeChallenge(clis, taskDescription) {
|
|
||||||
const [cliA, cliB] = clis
|
const [cliA, cliB] = clis
|
||||||
const results = []
|
const results = []
|
||||||
|
|
||||||
// Step 1: CLI A provides initial analysis
|
const analyze = await execCLI(cliA, buildPrompt({ ...PROMPTS.initial, taskDescription: task }))
|
||||||
const analyzeResult = await Bash({
|
results.push({ phase: 'analyze', cli: cliA.name, result: analyze })
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Provide comprehensive analysis for: ${taskDescription}
|
|
||||||
TASK: • Deep analysis of problem space • Propose implementation approach • List specific changes • Identify risks
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: Thorough analysis with clear reasoning
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Be thorough and explicit about reasoning
|
|
||||||
" --tool ${cliA.name} --mode analysis`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
results.push({ phase: 'analyze', cli: cliA.name, result: analyzeResult })
|
|
||||||
const analyzeSessionId = extractSessionId(analyzeResult)
|
|
||||||
|
|
||||||
// Step 2: CLI B challenges with focus on finding flaws
|
const criticize = await execCLI(cliB, buildPrompt({ ...PROMPTS.criticize, taskDescription: task }), { resume: extractSessionId(analyze) })
|
||||||
const challengeResult = await Bash({
|
results.push({ phase: 'challenge', cli: cliB.name, result: criticize })
|
||||||
command: `ccw cli -p "
|
|
||||||
PURPOSE: Stress-test and find weaknesses in the analysis for: ${taskDescription}
|
|
||||||
TASK: • Find logical flaws in reasoning • Identify missed edge cases • Propose better alternatives • Rate confidence in each criticism (High/Medium/Low)
|
|
||||||
MODE: analysis
|
|
||||||
CONTEXT: @**/*
|
|
||||||
EXPECTED: Detailed critique with severity ratings: [CRITICAL] [HIGH] [MEDIUM] [LOW] for each issue found
|
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Be ruthlessly critical, find every possible flaw
|
|
||||||
" --tool ${cliB.name} --mode analysis --resume ${analyzeSessionId}`,
|
|
||||||
run_in_background: false
|
|
||||||
})
|
|
||||||
results.push({ phase: 'challenge', cli: cliB.name, result: challengeResult })
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Mode Router**:
|
### Mode Router & Result Aggregation
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
async function executeAnalysis(mode, clis, taskDescription) {
|
async function executeAnalysis(mode, clis, taskDescription) {
|
||||||
switch (mode.name) {
|
switch (mode.name) {
|
||||||
case 'parallel':
|
case 'parallel': return await executeParallel(clis, taskDescription)
|
||||||
return await executeParallel(clis, taskDescription)
|
case 'sequential': return await executeSequential(clis, taskDescription)
|
||||||
case 'sequential':
|
case 'collaborative': return await executeCollaborative(clis, taskDescription)
|
||||||
return await executeSequential(clis, taskDescription)
|
case 'debate': return await executeDebate(clis, taskDescription)
|
||||||
case 'collaborative':
|
case 'challenge': return await executeChallenge(clis, taskDescription)
|
||||||
return await executeCollaborative(clis, taskDescription)
|
|
||||||
case 'debate':
|
|
||||||
return await executeDebate(clis, taskDescription)
|
|
||||||
case 'challenge':
|
|
||||||
return await executeChallenge(clis, taskDescription)
|
|
||||||
default:
|
|
||||||
return await executeParallel(clis, taskDescription)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute based on selected mode
|
|
||||||
const analysisResults = await executeAnalysis(selectedMode, selectedCLIs, taskDescription)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Result Aggregation** (mode-aware):
|
|
||||||
```javascript
|
|
||||||
function aggregateResults(mode, results) {
|
function aggregateResults(mode, results) {
|
||||||
const base = {
|
const base = { mode: mode.name, pattern: mode.pattern, tools_used: results.map(r => r.cli || 'unknown') }
|
||||||
mode: mode.name,
|
|
||||||
pattern: mode.pattern,
|
|
||||||
tools_used: results.map(r => r.cli || 'unknown')
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (mode.name) {
|
switch (mode.name) {
|
||||||
case 'parallel':
|
case 'parallel':
|
||||||
return {
|
return { ...base, findings: results.map(parseOutput), consensus: findCommonPoints(results), divergences: findDifferences(results) }
|
||||||
...base,
|
|
||||||
findings: results.map(r => parseOutput(r)),
|
|
||||||
consensus: findCommonPoints(results),
|
|
||||||
divergences: findDifferences(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'sequential':
|
case 'sequential':
|
||||||
return {
|
return { ...base, evolution: results.map((r, i) => ({ step: i + 1, analysis: parseOutput(r) })), finalAnalysis: parseOutput(results.at(-1)) }
|
||||||
...base,
|
|
||||||
evolution: results.map((r, i) => ({ step: i + 1, analysis: parseOutput(r) })),
|
|
||||||
finalAnalysis: parseOutput(results[results.length - 1])
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'collaborative':
|
case 'collaborative':
|
||||||
return {
|
return { ...base, rounds: groupByRound(results), synthesis: extractSynthesis(results.at(-1)) }
|
||||||
...base,
|
|
||||||
rounds: groupByRound(results),
|
|
||||||
synthesis: extractSynthesis(results[results.length - 1])
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'debate':
|
case 'debate':
|
||||||
return {
|
return { ...base, proposal: parseOutput(results.find(r => r.phase === 'propose')?.result),
|
||||||
...base,
|
|
||||||
proposal: parseOutput(results.find(r => r.phase === 'propose')?.result),
|
|
||||||
challenges: parseOutput(results.find(r => r.phase === 'challenge')?.result),
|
challenges: parseOutput(results.find(r => r.phase === 'challenge')?.result),
|
||||||
resolution: parseOutput(results.find(r => r.phase === 'defend')?.result),
|
resolution: parseOutput(results.find(r => r.phase === 'defend')?.result), confidence: calculateDebateConfidence(results) }
|
||||||
confidence: calculateDebateConfidence(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'challenge':
|
case 'challenge':
|
||||||
return {
|
return { ...base, originalAnalysis: parseOutput(results.find(r => r.phase === 'analyze')?.result),
|
||||||
...base,
|
critiques: parseCritiques(results.find(r => r.phase === 'challenge')?.result), riskScore: calculateRiskScore(results) }
|
||||||
originalAnalysis: parseOutput(results.find(r => r.phase === 'analyze')?.result),
|
|
||||||
critiques: parseCritiques(results.find(r => r.phase === 'challenge')?.result),
|
|
||||||
riskScore: calculateRiskScore(results)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const aggregatedAnalysis = aggregateResults(selectedMode, analysisResults)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Phase 4: User Decision
|
## Phase 4: User Decision
|
||||||
|
|
||||||
**Present Mode-Specific Summary**:
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function presentSummary(aggregatedAnalysis) {
|
function presentSummary(analysis) {
|
||||||
const { mode, pattern } = aggregatedAnalysis
|
console.log(`## Analysis Result\n**Mode**: ${analysis.mode} (${analysis.pattern})\n**Tools**: ${analysis.tools_used.join(' → ')}`)
|
||||||
|
|
||||||
console.log(`
|
switch (analysis.mode) {
|
||||||
## Analysis Result Summary
|
|
||||||
|
|
||||||
**Mode**: ${mode} (${pattern})
|
|
||||||
**Tools**: ${aggregatedAnalysis.tools_used.join(' → ')}
|
|
||||||
`)
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case 'parallel':
|
case 'parallel':
|
||||||
console.log(`
|
console.log(`### Consensus\n${analysis.consensus.map(c => `- ${c}`).join('\n')}\n### Divergences\n${analysis.divergences.map(d => `- ${d}`).join('\n')}`)
|
||||||
### Consensus Points
|
|
||||||
${aggregatedAnalysis.consensus.map(c => `- ${c}`).join('\n')}
|
|
||||||
|
|
||||||
### Divergence Points
|
|
||||||
${aggregatedAnalysis.divergences.map(d => `- ${d}`).join('\n')}
|
|
||||||
`)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'sequential':
|
case 'sequential':
|
||||||
console.log(`
|
console.log(`### Evolution\n${analysis.evolution.map(e => `**Step ${e.step}**: ${e.analysis.summary}`).join('\n')}\n### Final\n${analysis.finalAnalysis.summary}`)
|
||||||
### Analysis Evolution
|
|
||||||
${aggregatedAnalysis.evolution.map(e => `**Step ${e.step}**: ${e.analysis.summary}`).join('\n')}
|
|
||||||
|
|
||||||
### Final Analysis
|
|
||||||
${aggregatedAnalysis.finalAnalysis.summary}
|
|
||||||
`)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'collaborative':
|
case 'collaborative':
|
||||||
console.log(`
|
console.log(`### Rounds\n${Object.entries(analysis.rounds).map(([r, a]) => `**Round ${r}**: ${a.map(x => x.cli).join(' + ')}`).join('\n')}\n### Synthesis\n${analysis.synthesis}`)
|
||||||
### Collaboration Rounds
|
|
||||||
${Object.entries(aggregatedAnalysis.rounds).map(([round, analyses]) =>
|
|
||||||
`**Round ${round}**: ${analyses.map(a => a.cli).join(' + ')}`
|
|
||||||
).join('\n')}
|
|
||||||
|
|
||||||
### Synthesized Result
|
|
||||||
${aggregatedAnalysis.synthesis}
|
|
||||||
`)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'debate':
|
case 'debate':
|
||||||
console.log(`
|
console.log(`### Debate\n**Proposal**: ${analysis.proposal.summary}\n**Challenges**: ${analysis.challenges.points?.length || 0} points\n**Resolution**: ${analysis.resolution.summary}\n**Confidence**: ${analysis.confidence}%`)
|
||||||
### Debate Summary
|
|
||||||
**Proposal**: ${aggregatedAnalysis.proposal.summary}
|
|
||||||
**Challenges**: ${aggregatedAnalysis.challenges.points?.length || 0} points raised
|
|
||||||
**Resolution**: ${aggregatedAnalysis.resolution.summary}
|
|
||||||
**Confidence**: ${aggregatedAnalysis.confidence}%
|
|
||||||
`)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'challenge':
|
case 'challenge':
|
||||||
console.log(`
|
console.log(`### Challenge\n**Original**: ${analysis.originalAnalysis.summary}\n**Critiques**: ${analysis.critiques.length} issues\n${analysis.critiques.map(c => `- [${c.severity}] ${c.description}`).join('\n')}\n**Risk Score**: ${analysis.riskScore}/100`)
|
||||||
### Challenge Summary
|
|
||||||
**Original Analysis**: ${aggregatedAnalysis.originalAnalysis.summary}
|
|
||||||
**Critiques Found**: ${aggregatedAnalysis.critiques.length} issues
|
|
||||||
${aggregatedAnalysis.critiques.map(c => `- [${c.severity}] ${c.description}`).join('\n')}
|
|
||||||
**Risk Score**: ${aggregatedAnalysis.riskScore}/100
|
|
||||||
`)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presentSummary(aggregatedAnalysis)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Decision Options**:
|
|
||||||
```javascript
|
|
||||||
AskUserQuestion({
|
AskUserQuestion({
|
||||||
questions: [{
|
questions: [{
|
||||||
question: "How to proceed?",
|
question: "How to proceed?",
|
||||||
header: "Next Step",
|
header: "Next Step",
|
||||||
options: [
|
options: [
|
||||||
{ label: "Execute directly", description: "Implement immediately based on analysis" },
|
{ label: "Execute directly", description: "Implement immediately" },
|
||||||
{ label: "Refine analysis", description: "Provide more constraints, re-analyze" },
|
{ label: "Refine analysis", description: "Add constraints, re-analyze" },
|
||||||
{ label: "Change tools", description: "Select different tool combination" },
|
{ label: "Change tools", description: "Different tool combination" },
|
||||||
{ label: "Cancel", description: "End current workflow" }
|
{ label: "Cancel", description: "End workflow" }
|
||||||
],
|
],
|
||||||
multiSelect: false
|
multiSelect: false
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
// Routing: Execute → Phase 5 | Refine → Phase 3 | Change → Phase 2 | Cancel → End
|
||||||
```
|
```
|
||||||
|
|
||||||
**Routing Logic**:
|
## Phase 5: Direct Execution
|
||||||
- **Execute directly** → Phase 5
|
|
||||||
- **Refine analysis** → Collect feedback, return to Phase 3
|
|
||||||
- **Change tools** → Return to Phase 2
|
|
||||||
- **Cancel** → End workflow
|
|
||||||
|
|
||||||
### Phase 5: Direct Execution
|
|
||||||
|
|
||||||
**No Artifacts - Direct Implementation**:
|
|
||||||
```javascript
|
```javascript
|
||||||
// Use the aggregated analysis directly
|
// No IMPL_PLAN.md, no plan.json - direct implementation
|
||||||
// No IMPL_PLAN.md, no plan.json, no session files
|
|
||||||
|
|
||||||
console.log("Starting direct execution based on analysis...")
|
|
||||||
|
|
||||||
// Execution-capable agents (canExecute: true)
|
|
||||||
const executionAgents = agents.filter(a => a.canExecute)
|
const executionAgents = agents.filter(a => a.canExecute)
|
||||||
|
const executionTool = selectedAgent.canExecute ? selectedAgent : selectedCLIs[0]
|
||||||
// Select execution tool: prefer execution-capable agent, fallback to CLI
|
|
||||||
const executionTool = selectedTools.find(t =>
|
|
||||||
t.type === 'agent' && executionAgents.some(ea => ea.name === t.name)
|
|
||||||
) || selectedTools.find(t => t.type === 'cli')
|
|
||||||
|
|
||||||
if (executionTool.type === 'agent') {
|
if (executionTool.type === 'agent') {
|
||||||
// Use Agent for execution (preferred if available)
|
|
||||||
Task({
|
Task({
|
||||||
subagent_type: executionTool.name,
|
subagent_type: executionTool.name,
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
description: `Execute: ${taskDescription.slice(0, 30)}`,
|
description: `Execute: ${taskDescription.slice(0, 30)}`,
|
||||||
prompt: `
|
prompt: `## Task\n${taskDescription}\n\n## Analysis Results\n${JSON.stringify(aggregatedAnalysis, null, 2)}\n\n## Instructions\n1. Apply changes to identified files\n2. Follow recommended approach\n3. Handle identified risks\n4. Verify changes work correctly`
|
||||||
## Task
|
|
||||||
${taskDescription}
|
|
||||||
|
|
||||||
## Analysis Results (from previous tools)
|
|
||||||
${JSON.stringify(aggregatedAnalysis, null, 2)}
|
|
||||||
|
|
||||||
## Instructions
|
|
||||||
Based on the analysis above, implement the solution:
|
|
||||||
1. Apply changes to identified files
|
|
||||||
2. Follow the recommended approach
|
|
||||||
3. Handle identified risks
|
|
||||||
4. Verify changes work correctly
|
|
||||||
`
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Use CLI with write mode
|
|
||||||
Bash({
|
Bash({
|
||||||
command: `ccw cli -p "
|
command: `ccw cli -p "
|
||||||
PURPOSE: Implement the solution based on analysis: ${taskDescription}
|
PURPOSE: Implement solution: ${taskDescription}
|
||||||
TASK: ${extractedTasks.join(' • ')}
|
TASK: ${extractedTasks.join(' • ')}
|
||||||
MODE: write
|
MODE: write
|
||||||
CONTEXT: @${affectedFiles.join(' @')}
|
CONTEXT: @${affectedFiles.join(' @')}
|
||||||
EXPECTED: Working implementation with all changes applied
|
EXPECTED: Working implementation with all changes applied
|
||||||
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) | Apply analysis findings directly
|
RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md)
|
||||||
" --tool ${executionTool.name} --mode write`,
|
" --tool ${executionTool.name} --mode write`,
|
||||||
run_in_background: false
|
run_in_background: false
|
||||||
})
|
})
|
||||||
@@ -718,81 +385,45 @@ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/write-protocol.md) | Ap
|
|||||||
```javascript
|
```javascript
|
||||||
TodoWrite({ todos: [
|
TodoWrite({ todos: [
|
||||||
{ content: "Phase 1: Clarify requirements", status: "in_progress", activeForm: "Clarifying requirements" },
|
{ content: "Phase 1: Clarify requirements", status: "in_progress", activeForm: "Clarifying requirements" },
|
||||||
{ content: "Phase 2: Auto-select tools", status: "pending", activeForm: "Analyzing task" },
|
{ content: "Phase 2: Select tools", status: "pending", activeForm: "Selecting tools" },
|
||||||
{ content: "Phase 3: Mixed tool analysis", status: "pending", activeForm: "Running analysis" },
|
{ content: "Phase 3: Multi-mode analysis", status: "pending", activeForm: "Running analysis" },
|
||||||
{ content: "Phase 4: User decision", status: "pending", activeForm: "Awaiting decision" },
|
{ content: "Phase 4: User decision", status: "pending", activeForm: "Awaiting decision" },
|
||||||
{ content: "Phase 5: Direct execution", status: "pending", activeForm: "Executing implementation" }
|
{ content: "Phase 5: Direct execution", status: "pending", activeForm: "Executing" }
|
||||||
]})
|
]})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Iteration Patterns
|
## Iteration Patterns
|
||||||
|
|
||||||
### Pattern A: Direct Path (Most Common)
|
| Pattern | Flow |
|
||||||
```
|
|---------|------|
|
||||||
Phase 1 → Phase 2 (auto) → Phase 3 → Phase 4 (execute) → Phase 5
|
| **Direct** | Phase 1 → 2 → 3 → 4(execute) → 5 |
|
||||||
```
|
| **Refinement** | Phase 3 → 4(refine) → 3 → 4 → 5 |
|
||||||
|
| **Tool Adjust** | Phase 2(adjust) → 3 → 4 → 5 |
|
||||||
### Pattern B: Refinement Loop
|
|
||||||
```
|
|
||||||
Phase 3 → Phase 4 (refine) → Phase 3 → Phase 4 → Phase 5
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pattern C: Tool Adjustment
|
|
||||||
```
|
|
||||||
Phase 2 (adjust) → Phase 3 → Phase 4 → Phase 5
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
| Error | Resolution |
|
| Error | Resolution |
|
||||||
|-------|------------|
|
|-------|------------|
|
||||||
| CLI timeout | Retry with secondary model |
|
| CLI timeout | Retry with secondary model |
|
||||||
| No enabled tools | Load cli-tools.json, ask user to enable tools |
|
| No enabled tools | Ask user to enable tools in cli-tools.json |
|
||||||
| Task type unclear | Default to first available CLI + code-developer |
|
| Task unclear | Default to first CLI + code-developer |
|
||||||
| Ambiguous task | Force clarification via AskUser |
|
| Ambiguous task | Force clarification via AskUser |
|
||||||
| Execution fails | Present error, ask user for direction |
|
| Execution fails | Present error, ask user for direction |
|
||||||
|
|
||||||
## Analysis Modes Reference
|
## Comparison with multi-cli-plan
|
||||||
|
|
||||||
| Mode | Pattern | Use Case | CLI Count |
|
|
||||||
|------|---------|----------|-----------|
|
|
||||||
| **Parallel** | `A \|\| B \|\| C → Aggregate` | Fast multi-perspective analysis | 1+ |
|
|
||||||
| **Sequential** | `A → B(resume) → C(resume)` | Deep incremental analysis | 2+ |
|
|
||||||
| **Collaborative** | `A → B → A → B → Synthesize` | Multi-round refinement | 2+ |
|
|
||||||
| **Debate** | `A(propose) → B(challenge) → A(defend)` | Stress-test solutions | 2 |
|
|
||||||
| **Challenge** | `A(analyze) → B(challenge)` | Find flaws and risks | 2 |
|
|
||||||
|
|
||||||
## Comparison
|
|
||||||
|
|
||||||
| Aspect | lite-lite-lite | multi-cli-plan |
|
| Aspect | lite-lite-lite | multi-cli-plan |
|
||||||
|--------|----------------|----------------|
|
|--------|----------------|----------------|
|
||||||
| **Artifacts** | None | IMPL_PLAN.md, plan.json, synthesis.json |
|
| **Artifacts** | None | IMPL_PLAN.md, plan.json, synthesis.json |
|
||||||
| **Session** | Stateless (uses --resume for chaining) | Persistent session folder |
|
| **Session** | Stateless (--resume chaining) | Persistent session folder |
|
||||||
| **Tool Selection** | Multi-CLI + Agent via 3-step selection | Config-driven with fixed tools |
|
| **Tool Selection** | 3-step (CLI → Mode → Agent) | Config-driven fixed tools |
|
||||||
| **Analysis Modes** | 5 modes (parallel/sequential/collaborative/debate/challenge) | Fixed synthesis rounds |
|
| **Analysis Modes** | 5 modes with --resume | Fixed synthesis rounds |
|
||||||
| **CLI Collaboration** | Auto --resume chaining | Manual session management |
|
| **Best For** | Quick analysis, adversarial validation | Complex multi-step implementations |
|
||||||
| **Iteration** | Via AskUser | Via rounds/synthesis |
|
|
||||||
| **Execution** | Direct | Via lite-execute |
|
|
||||||
| **Best For** | Quick analysis, adversarial validation, rapid iteration | Complex multi-step implementations |
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Be Specific**: Clear task description improves auto-selection accuracy
|
|
||||||
2. **Trust Auto-Selection**: Algorithm matches task type to tool strengths
|
|
||||||
3. **Adjust When Needed**: Use "Adjust tools" if auto-selection doesn't fit
|
|
||||||
4. **Trust Consensus**: When tools agree, confidence is high
|
|
||||||
5. **Iterate Fast**: Use refinement loop for complex requirements
|
|
||||||
6. **Direct is Fast**: Skip artifacts when task is straightforward
|
|
||||||
|
|
||||||
## Related Commands
|
## Related Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Full planning workflow
|
/workflow:multi-cli-plan "complex task" # Full planning workflow
|
||||||
/workflow:multi-cli-plan "complex task"
|
/workflow:lite-plan "task" # Single CLI planning
|
||||||
|
/workflow:lite-execute --in-memory # Direct execution
|
||||||
# Single CLI planning
|
|
||||||
/workflow:lite-plan "task"
|
|
||||||
|
|
||||||
# Direct execution
|
|
||||||
/workflow:lite-execute --in-memory
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -347,11 +347,8 @@ function extractTimelineFromSynthesis(synthesis) {
|
|||||||
return timeline;
|
return timeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track multi-cli toolbar state
|
|
||||||
let isMultiCliToolbarExpanded = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show multi-cli detail page with tabs
|
* Show multi-cli detail page with tabs (same layout as lite-plan)
|
||||||
*/
|
*/
|
||||||
function showMultiCliDetailPage(sessionKey) {
|
function showMultiCliDetailPage(sessionKey) {
|
||||||
const session = liteTaskDataStore[sessionKey];
|
const session = liteTaskDataStore[sessionKey];
|
||||||
@@ -364,27 +361,23 @@ function showMultiCliDetailPage(sessionKey) {
|
|||||||
|
|
||||||
const container = document.getElementById('mainContent');
|
const container = document.getElementById('mainContent');
|
||||||
const metadata = session.metadata || {};
|
const metadata = session.metadata || {};
|
||||||
const discussionTopic = session.discussionTopic || {};
|
const plan = session.plan || {};
|
||||||
const latestSynthesis = session.latestSynthesis || discussionTopic;
|
const tasks = plan.tasks || [];
|
||||||
const roundCount = metadata.roundId || session.roundCount || 1;
|
const roundCount = metadata.roundId || session.roundCount || 1;
|
||||||
const topicTitle = getI18nText(latestSynthesis.title) || session.topicTitle || 'Discussion Topic';
|
const status = session.status || 'analyzing';
|
||||||
const status = latestSynthesis.status || session.status || 'analyzing';
|
|
||||||
|
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="session-detail-page multi-cli-detail-page multi-cli-detail-with-toolbar">
|
<div class="session-detail-page lite-task-detail-page">
|
||||||
<!-- Main Content Area -->
|
|
||||||
<div class="multi-cli-main-content">
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<button class="btn-back" onclick="goBackToLiteTasks()">
|
<button class="btn-back" onclick="goBackToLiteTasks()">
|
||||||
<span class="back-icon">←</span>
|
<span class="back-icon">←</span>
|
||||||
<span>${t('multiCli.backToList') || 'Back to Multi-CLI Plan'}</span>
|
<span>${t('multiCli.backToList') || 'Back to Multi-CLI Plan'}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="detail-title-row">
|
<div class="detail-title-row">
|
||||||
<h2 class="detail-session-id"><i data-lucide="messages-square" class="w-5 h-5 inline mr-2"></i> ${escapeHtml(session.id)}</h2>
|
<h2 class="detail-session-id"><i data-lucide="messages-square" class="w-5 h-5 inline mr-2"></i> ${escapeHtml(session.id)}</h2>
|
||||||
<div class="detail-badges">
|
<div class="detail-badges">
|
||||||
<span class="session-type-badge multi-cli-plan">multi-cli-plan</span>
|
<span class="session-type-badge multi-cli-plan">MULTI-CLI</span>
|
||||||
<span class="session-status-badge ${status}">${escapeHtml(status)}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -396,77 +389,52 @@ function showMultiCliDetailPage(sessionKey) {
|
|||||||
<span class="info-value">${formatDate(metadata.timestamp || session.createdAt)}</span>
|
<span class="info-value">${formatDate(metadata.timestamp || session.createdAt)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">${t('multiCli.roundCount') || 'Rounds'}</span>
|
<span class="info-label">${t('detail.tasks') || 'Tasks'}</span>
|
||||||
<span class="info-value">${roundCount}</span>
|
<span class="info-value">${tasks.length} ${t('session.tasks') || 'tasks'}</span>
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">${t('multiCli.topic') || 'Topic'}</span>
|
|
||||||
<span class="info-value">${escapeHtml(topicTitle)}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab Navigation -->
|
<!-- Tab Navigation (same as lite-plan) -->
|
||||||
<div class="detail-tabs">
|
<div class="detail-tabs">
|
||||||
<button class="detail-tab active" data-tab="planning" onclick="switchMultiCliDetailTab('planning')">
|
<button class="detail-tab active" data-tab="tasks" onclick="switchMultiCliDetailTab('tasks')">
|
||||||
<span class="tab-icon"><i data-lucide="list-checks" class="w-4 h-4"></i></span>
|
<span class="tab-icon"><i data-lucide="list-checks" class="w-4 h-4"></i></span>
|
||||||
<span class="tab-text">${t('multiCli.tab.planning') || 'Planning'}</span>
|
<span class="tab-text">${t('tab.tasks') || 'Tasks'}</span>
|
||||||
|
<span class="tab-count">${tasks.length}</span>
|
||||||
|
</button>
|
||||||
|
<button class="detail-tab" data-tab="plan" onclick="switchMultiCliDetailTab('plan')">
|
||||||
|
<span class="tab-icon"><i data-lucide="ruler" class="w-4 h-4"></i></span>
|
||||||
|
<span class="tab-text">${t('tab.plan') || 'Plan'}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="discussion" onclick="switchMultiCliDetailTab('discussion')">
|
<button class="detail-tab" data-tab="discussion" onclick="switchMultiCliDetailTab('discussion')">
|
||||||
<span class="tab-icon"><i data-lucide="messages-square" class="w-4 h-4"></i></span>
|
<span class="tab-icon"><i data-lucide="messages-square" class="w-4 h-4"></i></span>
|
||||||
<span class="tab-text">${t('multiCli.tab.discussion') || 'Discussion'}</span>
|
<span class="tab-text">${t('multiCli.tab.discussion') || 'Discussion'}</span>
|
||||||
<span class="tab-count">${roundCount}</span>
|
<span class="tab-count">${roundCount}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="association" onclick="switchMultiCliDetailTab('association')">
|
<button class="detail-tab" data-tab="context" onclick="switchMultiCliDetailTab('context')">
|
||||||
<span class="tab-icon"><i data-lucide="link-2" class="w-4 h-4"></i></span>
|
<span class="tab-icon"><i data-lucide="package" class="w-4 h-4"></i></span>
|
||||||
<span class="tab-text">${t('multiCli.tab.association') || 'Association'}</span>
|
<span class="tab-text">${t('tab.context') || 'Context'}</span>
|
||||||
|
</button>
|
||||||
|
<button class="detail-tab" data-tab="summary" onclick="switchMultiCliDetailTab('summary')">
|
||||||
|
<span class="tab-icon"><i data-lucide="file-text" class="w-4 h-4"></i></span>
|
||||||
|
<span class="tab-text">${t('tab.summary') || 'Summary'}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab Content -->
|
<!-- Tab Content -->
|
||||||
<div class="detail-tab-content" id="multiCliDetailTabContent">
|
<div class="detail-tab-content" id="multiCliDetailTabContent">
|
||||||
${renderMultiCliPlanningTab(session)}
|
${renderMultiCliTasksTab(session)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right Toolbar -->
|
|
||||||
<div class="multi-cli-toolbar ${isMultiCliToolbarExpanded ? 'expanded' : 'collapsed'}" id="multiCliToolbar">
|
|
||||||
<button class="toolbar-toggle-btn" onclick="toggleMultiCliToolbar()" title="${t('multiCli.toolbar.toggle') || 'Toggle Task Panel'}">
|
|
||||||
<i data-lucide="${isMultiCliToolbarExpanded ? 'panel-right-close' : 'panel-right-open'}" class="w-5 h-5"></i>
|
|
||||||
</button>
|
|
||||||
<div class="toolbar-content">
|
|
||||||
${renderMultiCliToolbar(session)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Initialize icons and collapsible sections
|
// Initialize icons, collapsible sections, and task click handlers
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
initCollapsibleSections(container);
|
initCollapsibleSections(container);
|
||||||
|
initMultiCliTaskClickHandlers();
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle multi-cli toolbar expanded/collapsed state
|
|
||||||
*/
|
|
||||||
function toggleMultiCliToolbar() {
|
|
||||||
isMultiCliToolbarExpanded = !isMultiCliToolbarExpanded;
|
|
||||||
const toolbar = document.getElementById('multiCliToolbar');
|
|
||||||
const toggleBtn = toolbar?.querySelector('.toolbar-toggle-btn i');
|
|
||||||
|
|
||||||
if (toolbar) {
|
|
||||||
toolbar.classList.toggle('expanded', isMultiCliToolbarExpanded);
|
|
||||||
toolbar.classList.toggle('collapsed', !isMultiCliToolbarExpanded);
|
|
||||||
|
|
||||||
// Update icon
|
|
||||||
if (toggleBtn) {
|
|
||||||
toggleBtn.setAttribute('data-lucide', isMultiCliToolbarExpanded ? 'panel-right-close' : 'panel-right-open');
|
|
||||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the multi-cli toolbar content
|
* Render the multi-cli toolbar content
|
||||||
*/
|
*/
|
||||||
@@ -507,10 +475,10 @@ function renderMultiCliToolbar(session) {
|
|||||||
${tasks.map((task, idx) => {
|
${tasks.map((task, idx) => {
|
||||||
const taskTitle = task.title || task.summary || `Task ${idx + 1}`;
|
const taskTitle = task.title || task.summary || `Task ${idx + 1}`;
|
||||||
const taskScope = task.scope || '';
|
const taskScope = task.scope || '';
|
||||||
const taskId = `task-${idx}`;
|
const taskIdValue = task.id || `T${idx + 1}`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="toolbar-task-item" onclick="scrollToMultiCliTask(${idx})" data-task-idx="${idx}">
|
<div class="toolbar-task-item" onclick="openToolbarTaskDrawer('${escapeHtml(session.id)}', '${escapeHtml(taskIdValue)}')" data-task-idx="${idx}">
|
||||||
<span class="toolbar-task-num">#${idx + 1}</span>
|
<span class="toolbar-task-num">#${idx + 1}</span>
|
||||||
<div class="toolbar-task-info">
|
<div class="toolbar-task-info">
|
||||||
<span class="toolbar-task-title" title="${escapeHtml(taskTitle)}">${escapeHtml(taskTitle)}</span>
|
<span class="toolbar-task-title" title="${escapeHtml(taskTitle)}">${escapeHtml(taskTitle)}</span>
|
||||||
@@ -564,6 +532,13 @@ function scrollToMultiCliTask(taskIdx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open task drawer from toolbar (wrapper for openTaskDrawerForMultiCli)
|
||||||
|
*/
|
||||||
|
function openToolbarTaskDrawer(sessionId, taskId) {
|
||||||
|
openTaskDrawerForMultiCli(sessionId, taskId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to task element in the DOM
|
* Scroll to task element in the DOM
|
||||||
*/
|
*/
|
||||||
@@ -672,21 +647,31 @@ function switchMultiCliDetailTab(tabName) {
|
|||||||
const contentArea = document.getElementById('multiCliDetailTabContent');
|
const contentArea = document.getElementById('multiCliDetailTabContent');
|
||||||
|
|
||||||
switch (tabName) {
|
switch (tabName) {
|
||||||
case 'planning':
|
case 'tasks':
|
||||||
contentArea.innerHTML = renderMultiCliPlanningTab(session);
|
contentArea.innerHTML = renderMultiCliTasksTab(session);
|
||||||
|
break;
|
||||||
|
case 'plan':
|
||||||
|
contentArea.innerHTML = renderMultiCliPlanTab(session);
|
||||||
break;
|
break;
|
||||||
case 'discussion':
|
case 'discussion':
|
||||||
contentArea.innerHTML = renderMultiCliDiscussionSection(session);
|
contentArea.innerHTML = renderMultiCliDiscussionSection(session);
|
||||||
break;
|
break;
|
||||||
case 'association':
|
case 'context':
|
||||||
contentArea.innerHTML = renderMultiCliAssociationSection(session);
|
loadAndRenderMultiCliContextTab(session, contentArea);
|
||||||
break;
|
return; // Early return as this is async
|
||||||
|
case 'summary':
|
||||||
|
loadAndRenderMultiCliSummaryTab(session, contentArea);
|
||||||
|
return; // Early return as this is async
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-initialize after tab switch
|
// Re-initialize after tab switch
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
initCollapsibleSections(contentArea);
|
initCollapsibleSections(contentArea);
|
||||||
|
// Initialize task click handlers for tasks tab
|
||||||
|
if (tabName === 'tasks') {
|
||||||
|
initMultiCliTaskClickHandlers();
|
||||||
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -694,6 +679,383 @@ function switchMultiCliDetailTab(tabName) {
|
|||||||
// MULTI-CLI TAB RENDERERS
|
// MULTI-CLI TAB RENDERERS
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render Tasks tab - displays tasks from plan.json (same style as lite-plan)
|
||||||
|
*/
|
||||||
|
function renderMultiCliTasksTab(session) {
|
||||||
|
const plan = session.plan || {};
|
||||||
|
const tasks = plan.tasks || [];
|
||||||
|
|
||||||
|
// Populate drawer tasks for click-to-open functionality
|
||||||
|
currentDrawerTasks = tasks;
|
||||||
|
|
||||||
|
if (tasks.length === 0) {
|
||||||
|
return `
|
||||||
|
<div class="tab-empty-state">
|
||||||
|
<div class="empty-icon"><i data-lucide="clipboard-list" class="w-12 h-12"></i></div>
|
||||||
|
<div class="empty-title">${t('empty.noTasks') || 'No Tasks'}</div>
|
||||||
|
<div class="empty-text">${t('empty.noTasksText') || 'No tasks available for this session.'}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="tasks-tab-content">
|
||||||
|
<div class="tasks-list" id="multiCliTasksListContent">
|
||||||
|
${tasks.map((task, idx) => renderMultiCliTaskItem(session.id, task, idx)).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render Plan tab - displays plan.json content (summary, approach, metadata)
|
||||||
|
*/
|
||||||
|
function renderMultiCliPlanTab(session) {
|
||||||
|
const plan = session.plan;
|
||||||
|
|
||||||
|
if (!plan) {
|
||||||
|
return `
|
||||||
|
<div class="tab-empty-state">
|
||||||
|
<div class="empty-icon"><i data-lucide="ruler" class="w-12 h-12"></i></div>
|
||||||
|
<div class="empty-title">${t('multiCli.empty.planning') || 'No Plan Data'}</div>
|
||||||
|
<div class="empty-text">${t('multiCli.empty.planningText') || 'No plan.json found for this session.'}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="plan-tab-content">
|
||||||
|
<!-- Summary -->
|
||||||
|
${plan.summary ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="clipboard-list" class="w-4 h-4 inline mr-1"></i> Summary</h4>
|
||||||
|
<p class="plan-summary-text">${escapeHtml(plan.summary)}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Root Cause (fix-plan specific) -->
|
||||||
|
${plan.root_cause ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="search" class="w-4 h-4 inline mr-1"></i> Root Cause</h4>
|
||||||
|
<p class="plan-root-cause-text">${escapeHtml(plan.root_cause)}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Strategy (fix-plan specific) -->
|
||||||
|
${plan.strategy ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="route" class="w-4 h-4 inline mr-1"></i> Fix Strategy</h4>
|
||||||
|
<p class="plan-strategy-text">${escapeHtml(plan.strategy)}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Approach -->
|
||||||
|
${plan.approach ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="target" class="w-4 h-4 inline mr-1"></i> Approach</h4>
|
||||||
|
<p class="plan-approach-text">${escapeHtml(plan.approach)}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- User Requirements -->
|
||||||
|
${plan.user_requirements ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="user" class="w-4 h-4 inline mr-1"></i> User Requirements</h4>
|
||||||
|
<p class="plan-requirements-text">${escapeHtml(plan.user_requirements)}</p>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Focus Paths -->
|
||||||
|
${plan.focus_paths?.length ? `
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="folder" class="w-4 h-4 inline mr-1"></i> Focus Paths</h4>
|
||||||
|
<div class="path-tags">
|
||||||
|
${plan.focus_paths.map(p => `<span class="path-tag">${escapeHtml(p)}</span>`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- Metadata -->
|
||||||
|
<div class="plan-section">
|
||||||
|
<h4 class="plan-section-title"><i data-lucide="info" class="w-4 h-4 inline mr-1"></i> Metadata</h4>
|
||||||
|
<div class="plan-meta-grid">
|
||||||
|
${plan.severity ? `<div class="meta-item"><span class="meta-label">Severity:</span> <span class="severity-badge ${escapeHtml(plan.severity)}">${escapeHtml(plan.severity)}</span></div>` : ''}
|
||||||
|
${plan.risk_level ? `<div class="meta-item"><span class="meta-label">Risk Level:</span> <span class="risk-badge ${escapeHtml(plan.risk_level)}">${escapeHtml(plan.risk_level)}</span></div>` : ''}
|
||||||
|
${plan.estimated_time ? `<div class="meta-item"><span class="meta-label">Estimated Time:</span> ${escapeHtml(plan.estimated_time)}</div>` : ''}
|
||||||
|
${plan.complexity ? `<div class="meta-item"><span class="meta-label">Complexity:</span> ${escapeHtml(plan.complexity)}</div>` : ''}
|
||||||
|
${plan.recommended_execution ? `<div class="meta-item"><span class="meta-label">Execution:</span> ${escapeHtml(plan.recommended_execution)}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Raw JSON -->
|
||||||
|
<div class="plan-section collapsible-section">
|
||||||
|
<div class="collapsible-header">
|
||||||
|
<span class="collapse-icon">▶</span>
|
||||||
|
<span class="section-label">{ } Raw JSON</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content collapsed">
|
||||||
|
<pre class="json-content">${escapeHtml(JSON.stringify(plan, null, 2))}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and render Context tab - displays context-package content
|
||||||
|
*/
|
||||||
|
async function loadAndRenderMultiCliContextTab(session, contentArea) {
|
||||||
|
contentArea.innerHTML = `<div class="tab-loading">${t('common.loading') || 'Loading...'}</div>`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (window.SERVER_MODE && session.path) {
|
||||||
|
const response = await fetch(`/api/session-detail?path=${encodeURIComponent(session.path)}&type=context`);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
contentArea.innerHTML = renderMultiCliContextContent(data.context, session);
|
||||||
|
initCollapsibleSections(contentArea);
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: show session context_package if available
|
||||||
|
contentArea.innerHTML = renderMultiCliContextContent(session.context_package, session);
|
||||||
|
initCollapsibleSections(contentArea);
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
} catch (err) {
|
||||||
|
contentArea.innerHTML = `<div class="tab-error">Failed to load context: ${err.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render context content for multi-cli
|
||||||
|
*/
|
||||||
|
function renderMultiCliContextContent(context, session) {
|
||||||
|
// Also check for context_package in session
|
||||||
|
const ctx = context || session.context_package || {};
|
||||||
|
|
||||||
|
if (!ctx || Object.keys(ctx).length === 0) {
|
||||||
|
return `
|
||||||
|
<div class="tab-empty-state">
|
||||||
|
<div class="empty-icon"><i data-lucide="package" class="w-12 h-12"></i></div>
|
||||||
|
<div class="empty-title">${t('empty.noContext') || 'No Context Data'}</div>
|
||||||
|
<div class="empty-text">${t('empty.noContextText') || 'No context package available for this session.'}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sections = [];
|
||||||
|
|
||||||
|
// Task Description
|
||||||
|
if (ctx.task_description) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section">
|
||||||
|
<h4 class="context-section-title"><i data-lucide="file-text" class="w-4 h-4 inline mr-1"></i> Task Description</h4>
|
||||||
|
<p class="context-description">${escapeHtml(ctx.task_description)}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
if (ctx.constraints?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section">
|
||||||
|
<h4 class="context-section-title"><i data-lucide="alert-triangle" class="w-4 h-4 inline mr-1"></i> Constraints</h4>
|
||||||
|
<ul class="constraints-list">
|
||||||
|
${ctx.constraints.map(c => `<li>${escapeHtml(c)}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus Paths
|
||||||
|
if (ctx.focus_paths?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section">
|
||||||
|
<h4 class="context-section-title"><i data-lucide="folder" class="w-4 h-4 inline mr-1"></i> Focus Paths</h4>
|
||||||
|
<div class="path-tags">
|
||||||
|
${ctx.focus_paths.map(p => `<span class="path-tag">${escapeHtml(p)}</span>`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relevant Files
|
||||||
|
if (ctx.relevant_files?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section collapsible-section">
|
||||||
|
<div class="collapsible-header">
|
||||||
|
<span class="collapse-icon">▶</span>
|
||||||
|
<span class="section-label"><i data-lucide="files" class="w-4 h-4 inline mr-1"></i> Relevant Files (${ctx.relevant_files.length})</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content collapsed">
|
||||||
|
<ul class="files-list">
|
||||||
|
${ctx.relevant_files.map(f => `
|
||||||
|
<li class="file-item">
|
||||||
|
<span class="file-icon">📄</span>
|
||||||
|
<code>${escapeHtml(typeof f === 'string' ? f : f.path || f.file || '')}</code>
|
||||||
|
${f.reason ? `<span class="file-reason">${escapeHtml(f.reason)}</span>` : ''}
|
||||||
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dependencies
|
||||||
|
if (ctx.dependencies?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section collapsible-section">
|
||||||
|
<div class="collapsible-header">
|
||||||
|
<span class="collapse-icon">▶</span>
|
||||||
|
<span class="section-label"><i data-lucide="git-branch" class="w-4 h-4 inline mr-1"></i> Dependencies (${ctx.dependencies.length})</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content collapsed">
|
||||||
|
<ul class="deps-list">
|
||||||
|
${ctx.dependencies.map(d => `<li>${escapeHtml(typeof d === 'string' ? d : d.name || JSON.stringify(d))}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conflict Risks
|
||||||
|
if (ctx.conflict_risks?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section">
|
||||||
|
<h4 class="context-section-title"><i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> Conflict Risks</h4>
|
||||||
|
<ul class="risks-list">
|
||||||
|
${ctx.conflict_risks.map(r => `<li class="risk-item">${escapeHtml(typeof r === 'string' ? r : r.description || JSON.stringify(r))}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session ID
|
||||||
|
if (ctx.session_id) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section">
|
||||||
|
<h4 class="context-section-title"><i data-lucide="hash" class="w-4 h-4 inline mr-1"></i> Session ID</h4>
|
||||||
|
<code class="session-id-code">${escapeHtml(ctx.session_id)}</code>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw JSON
|
||||||
|
sections.push(`
|
||||||
|
<div class="context-section collapsible-section">
|
||||||
|
<div class="collapsible-header">
|
||||||
|
<span class="collapse-icon">▶</span>
|
||||||
|
<span class="section-label">{ } Raw JSON</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content collapsed">
|
||||||
|
<pre class="json-content">${escapeHtml(JSON.stringify(ctx, null, 2))}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
return `<div class="context-tab-content">${sections.join('')}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and render Summary tab
|
||||||
|
*/
|
||||||
|
async function loadAndRenderMultiCliSummaryTab(session, contentArea) {
|
||||||
|
contentArea.innerHTML = `<div class="tab-loading">${t('common.loading') || 'Loading...'}</div>`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (window.SERVER_MODE && session.path) {
|
||||||
|
const response = await fetch(`/api/session-detail?path=${encodeURIComponent(session.path)}&type=summary`);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
contentArea.innerHTML = renderMultiCliSummaryContent(data.summary, session);
|
||||||
|
initCollapsibleSections(contentArea);
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: show synthesis summary
|
||||||
|
contentArea.innerHTML = renderMultiCliSummaryContent(null, session);
|
||||||
|
initCollapsibleSections(contentArea);
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
} catch (err) {
|
||||||
|
contentArea.innerHTML = `<div class="tab-error">Failed to load summary: ${err.message}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render summary content for multi-cli
|
||||||
|
*/
|
||||||
|
function renderMultiCliSummaryContent(summary, session) {
|
||||||
|
const synthesis = session.latestSynthesis || session.discussionTopic || {};
|
||||||
|
const plan = session.plan || {};
|
||||||
|
|
||||||
|
// Use summary from file or build from synthesis
|
||||||
|
const summaryText = summary || synthesis.convergence?.summary || plan.summary;
|
||||||
|
|
||||||
|
if (!summaryText && !synthesis.solutions?.length) {
|
||||||
|
return `
|
||||||
|
<div class="tab-empty-state">
|
||||||
|
<div class="empty-icon"><i data-lucide="file-text" class="w-12 h-12"></i></div>
|
||||||
|
<div class="empty-title">${t('empty.noSummary') || 'No Summary'}</div>
|
||||||
|
<div class="empty-text">${t('empty.noSummaryText') || 'No summary available for this session.'}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sections = [];
|
||||||
|
|
||||||
|
// Main Summary
|
||||||
|
if (summaryText) {
|
||||||
|
const summaryContent = typeof summaryText === 'string' ? summaryText : getI18nText(summaryText);
|
||||||
|
sections.push(`
|
||||||
|
<div class="summary-section">
|
||||||
|
<h4 class="summary-section-title"><i data-lucide="file-text" class="w-4 h-4 inline mr-1"></i> Summary</h4>
|
||||||
|
<div class="summary-content">${escapeHtml(summaryContent)}</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convergence Status
|
||||||
|
if (synthesis.convergence) {
|
||||||
|
const conv = synthesis.convergence;
|
||||||
|
sections.push(`
|
||||||
|
<div class="summary-section">
|
||||||
|
<h4 class="summary-section-title"><i data-lucide="git-merge" class="w-4 h-4 inline mr-1"></i> Convergence</h4>
|
||||||
|
<div class="convergence-info">
|
||||||
|
<span class="convergence-level ${conv.level || ''}">${escapeHtml(conv.level || 'unknown')}</span>
|
||||||
|
<span class="convergence-rec ${conv.recommendation || ''}">${escapeHtml(conv.recommendation || '')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solutions Summary
|
||||||
|
if (synthesis.solutions?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="summary-section collapsible-section">
|
||||||
|
<div class="collapsible-header">
|
||||||
|
<span class="collapse-icon">▶</span>
|
||||||
|
<span class="section-label"><i data-lucide="lightbulb" class="w-4 h-4 inline mr-1"></i> Solutions (${synthesis.solutions.length})</span>
|
||||||
|
</div>
|
||||||
|
<div class="collapsible-content collapsed">
|
||||||
|
${synthesis.solutions.map((sol, idx) => `
|
||||||
|
<div class="solution-summary-item">
|
||||||
|
<span class="solution-num">#${idx + 1}</span>
|
||||||
|
<span class="solution-name">${escapeHtml(getI18nText(sol.title) || sol.id || `Solution ${idx + 1}`)}</span>
|
||||||
|
${sol.feasibility?.score ? `<span class="feasibility-badge">${Math.round(sol.feasibility.score * 100)}%</span>` : ''}
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<div class="summary-tab-content">${sections.join('')}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render Discussion Topic tab
|
* Render Discussion Topic tab
|
||||||
* Shows: title, description, scope, keyQuestions, status, tags
|
* Shows: title, description, scope, keyQuestions, status, tags
|
||||||
@@ -980,53 +1342,12 @@ function renderMultiCliPlanningTab(session) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tasks -->
|
<!-- Tasks (Click to view details) -->
|
||||||
${plan.tasks?.length ? `
|
${plan.tasks?.length ? `
|
||||||
<div class="plan-section">
|
<div class="plan-section">
|
||||||
<h4 class="plan-section-title"><i data-lucide="list-checks" class="w-4 h-4 inline mr-1"></i> Tasks (${plan.tasks.length})</h4>
|
<h4 class="plan-section-title"><i data-lucide="list-checks" class="w-4 h-4 inline mr-1"></i> Tasks (${plan.tasks.length})</h4>
|
||||||
<div class="fix-tasks-summary">
|
<div class="tasks-list multi-cli-tasks-list">
|
||||||
${plan.tasks.map((task, idx) => `
|
${plan.tasks.map((task, idx) => renderMultiCliTaskItem(session.id, task, idx)).join('')}
|
||||||
<div class="fix-task-summary-item collapsible-section">
|
|
||||||
<div class="collapsible-header">
|
|
||||||
<span class="collapse-icon">▶</span>
|
|
||||||
<span class="task-num">#${idx + 1}</span>
|
|
||||||
<span class="task-title-brief">${escapeHtml(task.title || task.summary || 'Untitled')}</span>
|
|
||||||
${task.scope ? `<span class="task-scope-badge">${escapeHtml(task.scope)}</span>` : ''}
|
|
||||||
</div>
|
|
||||||
<div class="collapsible-content collapsed">
|
|
||||||
${task.modification_points?.length ? `
|
|
||||||
<div class="task-detail-section">
|
|
||||||
<strong>Modification Points:</strong>
|
|
||||||
<ul class="mod-points-list">
|
|
||||||
${task.modification_points.map(mp => `
|
|
||||||
<li>
|
|
||||||
<code>${escapeHtml(mp.file || '')}</code>
|
|
||||||
${mp.function_name ? `<span class="func-name">-> ${escapeHtml(mp.function_name)}</span>` : ''}
|
|
||||||
${mp.change_type ? `<span class="change-type">(${escapeHtml(mp.change_type)})</span>` : ''}
|
|
||||||
</li>
|
|
||||||
`).join('')}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${task.implementation?.length ? `
|
|
||||||
<div class="task-detail-section">
|
|
||||||
<strong>Implementation Steps:</strong>
|
|
||||||
<ol class="impl-steps-list">
|
|
||||||
${task.implementation.map(step => `<li>${escapeHtml(step)}</li>`).join('')}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${task.verification?.length ? `
|
|
||||||
<div class="task-detail-section">
|
|
||||||
<strong>Verification:</strong>
|
|
||||||
<ul class="verify-list">
|
|
||||||
${task.verification.map(v => `<li>${escapeHtml(v)}</li>`).join('')}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('')}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -1045,6 +1366,346 @@ function renderMultiCliPlanningTab(session) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a single task item for multi-cli-plan (reuses lite-plan style)
|
||||||
|
* Supports click to open drawer for details
|
||||||
|
*/
|
||||||
|
function renderMultiCliTaskItem(sessionId, task, idx) {
|
||||||
|
const taskId = task.id || `T${idx + 1}`;
|
||||||
|
const taskJsonId = `multi-cli-task-${sessionId}-${taskId}`.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||||
|
taskJsonStore[taskJsonId] = task;
|
||||||
|
|
||||||
|
// Get preview info
|
||||||
|
const action = task.action || '';
|
||||||
|
const scope = task.scope || task.file || '';
|
||||||
|
const modCount = task.modification_points?.length || 0;
|
||||||
|
const implCount = task.implementation?.length || 0;
|
||||||
|
const acceptCount = task.acceptance?.length || 0;
|
||||||
|
|
||||||
|
// Escape for data attributes
|
||||||
|
const safeSessionId = escapeHtml(sessionId);
|
||||||
|
const safeTaskId = escapeHtml(taskId);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="detail-task-item-full multi-cli-task-item" data-session-id="${safeSessionId}" data-task-id="${safeTaskId}" style="cursor: pointer;" title="Click to view details">
|
||||||
|
<div class="task-item-header-lite">
|
||||||
|
<span class="task-id-badge">${escapeHtml(taskId)}</span>
|
||||||
|
<span class="task-title">${escapeHtml(task.title || task.summary || 'Untitled')}</span>
|
||||||
|
<button class="btn-view-json" data-task-json-id="${taskJsonId}" data-task-display-id="${safeTaskId}">{ } JSON</button>
|
||||||
|
</div>
|
||||||
|
<div class="task-item-meta-lite">
|
||||||
|
${action ? `<span class="meta-badge action">${escapeHtml(action)}</span>` : ''}
|
||||||
|
${scope ? `<span class="meta-badge scope">${escapeHtml(scope)}</span>` : ''}
|
||||||
|
${modCount > 0 ? `<span class="meta-badge mods">${modCount} mods</span>` : ''}
|
||||||
|
${implCount > 0 ? `<span class="meta-badge impl">${implCount} steps</span>` : ''}
|
||||||
|
${acceptCount > 0 ? `<span class="meta-badge accept">${acceptCount} acceptance</span>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize click handlers for multi-cli task items
|
||||||
|
*/
|
||||||
|
function initMultiCliTaskClickHandlers() {
|
||||||
|
// Task item click handlers
|
||||||
|
document.querySelectorAll('.multi-cli-task-item').forEach(item => {
|
||||||
|
if (!item._clickBound) {
|
||||||
|
item._clickBound = true;
|
||||||
|
item.addEventListener('click', function(e) {
|
||||||
|
// Don't trigger if clicking on JSON button
|
||||||
|
if (e.target.closest('.btn-view-json')) return;
|
||||||
|
|
||||||
|
const sessionId = this.dataset.sessionId;
|
||||||
|
const taskId = this.dataset.taskId;
|
||||||
|
openTaskDrawerForMultiCli(sessionId, taskId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// JSON button click handlers
|
||||||
|
document.querySelectorAll('.multi-cli-task-item .btn-view-json').forEach(btn => {
|
||||||
|
if (!btn._clickBound) {
|
||||||
|
btn._clickBound = true;
|
||||||
|
btn.addEventListener('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const taskJsonId = this.dataset.taskJsonId;
|
||||||
|
const displayId = this.dataset.taskDisplayId;
|
||||||
|
showJsonModal(taskJsonId, displayId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open task drawer for multi-cli task
|
||||||
|
*/
|
||||||
|
function openTaskDrawerForMultiCli(sessionId, taskId) {
|
||||||
|
const session = liteTaskDataStore[currentSessionDetailKey];
|
||||||
|
if (!session || !session.plan) return;
|
||||||
|
|
||||||
|
const task = session.plan.tasks?.find(t => (t.id || `T${session.plan.tasks.indexOf(t) + 1}`) === taskId);
|
||||||
|
if (!task) return;
|
||||||
|
|
||||||
|
// Set current drawer tasks
|
||||||
|
currentDrawerTasks = session.plan.tasks || [];
|
||||||
|
window._currentDrawerSession = session;
|
||||||
|
|
||||||
|
document.getElementById('drawerTaskTitle').textContent = task.title || task.summary || taskId;
|
||||||
|
document.getElementById('drawerContent').innerHTML = renderMultiCliTaskDrawerContent(task, session);
|
||||||
|
document.getElementById('taskDetailDrawer').classList.add('open');
|
||||||
|
document.getElementById('drawerOverlay').classList.add('active');
|
||||||
|
|
||||||
|
// Re-init lucide icons in drawer
|
||||||
|
setTimeout(() => {
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render drawer content for multi-cli task
|
||||||
|
*/
|
||||||
|
function renderMultiCliTaskDrawerContent(task, session) {
|
||||||
|
const taskId = task.id || 'Task';
|
||||||
|
const action = task.action || '';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<!-- Task Header -->
|
||||||
|
<div class="drawer-task-header">
|
||||||
|
<span class="task-id-badge">${escapeHtml(taskId)}</span>
|
||||||
|
${action ? `<span class="action-badge">${escapeHtml(action.toUpperCase())}</span>` : ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab Navigation -->
|
||||||
|
<div class="drawer-tabs">
|
||||||
|
<button class="drawer-tab active" data-tab="overview" onclick="switchMultiCliDrawerTab('overview')">Overview</button>
|
||||||
|
<button class="drawer-tab" data-tab="implementation" onclick="switchMultiCliDrawerTab('implementation')">Implementation</button>
|
||||||
|
<button class="drawer-tab" data-tab="files" onclick="switchMultiCliDrawerTab('files')">Files</button>
|
||||||
|
<button class="drawer-tab" data-tab="raw" onclick="switchMultiCliDrawerTab('raw')">Raw JSON</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tab Content -->
|
||||||
|
<div class="drawer-tab-content">
|
||||||
|
<!-- Overview Tab (default) -->
|
||||||
|
<div class="drawer-panel active" data-tab="overview">
|
||||||
|
${renderMultiCliTaskOverview(task)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Implementation Tab -->
|
||||||
|
<div class="drawer-panel" data-tab="implementation">
|
||||||
|
${renderMultiCliTaskImplementation(task)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Files Tab -->
|
||||||
|
<div class="drawer-panel" data-tab="files">
|
||||||
|
${renderMultiCliTaskFiles(task)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Raw JSON Tab -->
|
||||||
|
<div class="drawer-panel" data-tab="raw">
|
||||||
|
<pre class="json-view">${escapeHtml(JSON.stringify(task, null, 2))}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch drawer tab for multi-cli task
|
||||||
|
*/
|
||||||
|
function switchMultiCliDrawerTab(tabName) {
|
||||||
|
document.querySelectorAll('.drawer-tab').forEach(tab => {
|
||||||
|
tab.classList.toggle('active', tab.dataset.tab === tabName);
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.drawer-panel').forEach(panel => {
|
||||||
|
panel.classList.toggle('active', panel.dataset.tab === tabName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render multi-cli task overview section
|
||||||
|
*/
|
||||||
|
function renderMultiCliTaskOverview(task) {
|
||||||
|
let sections = [];
|
||||||
|
|
||||||
|
// Description Card
|
||||||
|
if (task.description) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="lite-card">
|
||||||
|
<div class="lite-card-header">
|
||||||
|
<span class="lite-card-icon">📝</span>
|
||||||
|
<h4 class="lite-card-title">Description</h4>
|
||||||
|
</div>
|
||||||
|
<div class="lite-card-body">
|
||||||
|
<p class="lite-description">${escapeHtml(task.description)}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope Card
|
||||||
|
if (task.scope || task.file) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="lite-card">
|
||||||
|
<div class="lite-card-header">
|
||||||
|
<span class="lite-card-icon">📂</span>
|
||||||
|
<h4 class="lite-card-title">Scope</h4>
|
||||||
|
</div>
|
||||||
|
<div class="lite-card-body">
|
||||||
|
<div class="lite-scope-box">
|
||||||
|
<code>${escapeHtml(task.scope || task.file)}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acceptance Criteria Card
|
||||||
|
if (task.acceptance?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="lite-card">
|
||||||
|
<div class="lite-card-header">
|
||||||
|
<span class="lite-card-icon">✅</span>
|
||||||
|
<h4 class="lite-card-title">Acceptance Criteria</h4>
|
||||||
|
</div>
|
||||||
|
<div class="lite-card-body">
|
||||||
|
<ul class="lite-acceptance-list">
|
||||||
|
${task.acceptance.map(ac => `<li>${escapeHtml(ac)}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference Card
|
||||||
|
if (task.reference) {
|
||||||
|
const ref = task.reference;
|
||||||
|
sections.push(`
|
||||||
|
<div class="lite-card">
|
||||||
|
<div class="lite-card-header">
|
||||||
|
<span class="lite-card-icon">📚</span>
|
||||||
|
<h4 class="lite-card-title">Reference</h4>
|
||||||
|
</div>
|
||||||
|
<div class="lite-card-body">
|
||||||
|
${ref.pattern ? `<div class="ref-item"><strong>PATTERN:</strong> ${escapeHtml(ref.pattern)}</div>` : ''}
|
||||||
|
${ref.files?.length ? `<div class="ref-item"><strong>FILES:</strong><br><code class="ref-files">${ref.files.map(f => escapeHtml(f)).join('\n')}</code></div>` : ''}
|
||||||
|
${ref.examples ? `<div class="ref-item"><strong>EXAMPLES:</strong> ${escapeHtml(ref.examples)}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sections.length ? sections.join('') : '<div class="empty-section">No overview data available</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render multi-cli task implementation section
|
||||||
|
*/
|
||||||
|
function renderMultiCliTaskImplementation(task) {
|
||||||
|
let sections = [];
|
||||||
|
|
||||||
|
// Modification Points
|
||||||
|
if (task.modification_points?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="drawer-section">
|
||||||
|
<h4 class="drawer-section-title">
|
||||||
|
<i data-lucide="file-edit" class="w-4 h-4"></i>
|
||||||
|
Modification Points
|
||||||
|
</h4>
|
||||||
|
<ul class="mod-points-detail-list">
|
||||||
|
${task.modification_points.map(mp => `
|
||||||
|
<li class="mod-point-item">
|
||||||
|
<code class="mod-file">${escapeHtml(mp.file || '')}</code>
|
||||||
|
${mp.target ? `<span class="mod-target">→ ${escapeHtml(mp.target)}</span>` : ''}
|
||||||
|
${mp.function_name ? `<span class="mod-func">→ ${escapeHtml(mp.function_name)}</span>` : ''}
|
||||||
|
${mp.change || mp.change_type ? `<span class="mod-change">(${escapeHtml(mp.change || mp.change_type)})</span>` : ''}
|
||||||
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation Steps
|
||||||
|
if (task.implementation?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="drawer-section">
|
||||||
|
<h4 class="drawer-section-title">
|
||||||
|
<i data-lucide="list-ordered" class="w-4 h-4"></i>
|
||||||
|
Implementation Steps
|
||||||
|
</h4>
|
||||||
|
<ol class="impl-steps-detail-list">
|
||||||
|
${task.implementation.map((step, idx) => `
|
||||||
|
<li class="impl-step-item">
|
||||||
|
<span class="step-num">${idx + 1}</span>
|
||||||
|
<span class="step-text">${escapeHtml(step)}</span>
|
||||||
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification
|
||||||
|
if (task.verification?.length) {
|
||||||
|
sections.push(`
|
||||||
|
<div class="drawer-section">
|
||||||
|
<h4 class="drawer-section-title">
|
||||||
|
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
||||||
|
Verification
|
||||||
|
</h4>
|
||||||
|
<ul class="verification-list">
|
||||||
|
${task.verification.map(v => `<li>${escapeHtml(v)}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sections.length ? sections.join('') : '<div class="empty-section">No implementation details available</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render multi-cli task files section
|
||||||
|
*/
|
||||||
|
function renderMultiCliTaskFiles(task) {
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
// Collect from modification_points
|
||||||
|
if (task.modification_points) {
|
||||||
|
task.modification_points.forEach(mp => {
|
||||||
|
if (mp.file && !files.includes(mp.file)) files.push(mp.file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect from scope/file
|
||||||
|
if (task.scope && !files.includes(task.scope)) files.push(task.scope);
|
||||||
|
if (task.file && !files.includes(task.file)) files.push(task.file);
|
||||||
|
|
||||||
|
// Collect from reference.files
|
||||||
|
if (task.reference?.files) {
|
||||||
|
task.reference.files.forEach(f => {
|
||||||
|
if (f && !files.includes(f)) files.push(f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
return '<div class="empty-section">No files specified</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="drawer-section">
|
||||||
|
<h4 class="drawer-section-title">Target Files</h4>
|
||||||
|
<ul class="target-files-list">
|
||||||
|
${files.map(f => `
|
||||||
|
<li class="file-item">
|
||||||
|
<span class="file-icon">📄</span>
|
||||||
|
<code>${escapeHtml(f)}</code>
|
||||||
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a single requirement item
|
* Render a single requirement item
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user