Compare commits

...

6 Commits

Author SHA1 Message Date
catlog22
5114a942dc chore: bump version to 6.1.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 22:32:58 +08:00
catlog22
edef937822 feat(v6.1.4): dashboard lite-fix enhancements and workflow docs update
- fix(dashboard): enhance lite-fix session parsing and plan rendering
- docs(workflow): add task status update command example for ccw dashboard

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 22:32:39 +08:00
catlog22
faa86eded0 docs: add changelog entries for 6.1.2 and 6.1.3
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 15:53:15 +08:00
catlog22
44fa6e0a42 chore: bump version to 6.1.3
- Update README version badge and release notes
- Published to npm with simplified edit_file CLI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 15:52:43 +08:00
catlog22
be9a1c76d4 refactor(tool): simplify edit_file to parameter-based input only
- Remove JSON input support, use CLI parameters only
- Remove line mode, keep text replacement only
- Add sed as line operation alternative in tool-strategy.md
- Update documentation to English

Usage: ccw tool exec edit_file --path file.txt --old "old" --new "new"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 15:50:35 +08:00
catlog22
fcc811d6a1 docs(readme): update to v6.1.2 and simplify installation
- Update version badge and release notes to v6.1.2
- Remove one-click script install section (npm only)
- Update changelog highlights for latest release

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 15:41:39 +08:00
11 changed files with 794 additions and 183 deletions

View File

@@ -67,7 +67,9 @@ Phase 4: Execution Strategy & Task Execution
├─ Get next in_progress task from TodoWrite
├─ Lazy load task JSON
├─ Launch agent with task context
├─ Mark task completed
├─ Mark task completed (update IMPL-*.json status)
│ # Quick fix: Update task status for ccw dashboard
│ # TS=$(date -Iseconds) && jq --arg ts "$TS" '.status="completed" | .status_history=(.status_history // [])+[{"from":"in_progress","to":"completed","changed_at":$ts}]' IMPL-X.json > tmp.json && mv tmp.json IMPL-X.json
└─ Advance to next task
Phase 5: Completion

View File

@@ -14,71 +14,66 @@
**When to Use**: Edit tool fails 1+ times on same file
### update Mode (Default)
### Usage
**Best for**: Code block replacements, function rewrites, multi-line changes
```bash
ccw tool exec edit_file '{
"path": "file.py",
"oldText": "def old():\n pass",
"newText": "def new():\n return True"
}'
ccw tool exec edit_file --path "file.py" --old "def old():
pass" --new "def new():
return True"
```
### line Mode (Precise Line Operations)
**Parameters**:
- `--path`: File path to edit
- `--old`: Text to find and replace
- `--new`: New text to insert
**Best for**: Config files, line insertions/deletions, precise line number control
```bash
# Insert after specific line
ccw tool exec edit_file '{
"path": "config.txt",
"mode": "line",
"operation": "insert_after",
"line": 10,
"text": "new config line"
}'
# Delete line range
ccw tool exec edit_file '{
"path": "log.txt",
"mode": "line",
"operation": "delete",
"line": 5,
"end_line": 8
}'
# Replace specific line
ccw tool exec edit_file '{
"path": "script.sh",
"mode": "line",
"operation": "replace",
"line": 3,
"text": "#!/bin/bash"
}'
```
**Operations**:
- `insert_before`: Insert text before specified line
- `insert_after`: Insert text after specified line
- `replace`: Replace line or line range
- `delete`: Delete line or line range
### Mode Selection Guide
| Scenario | Mode | Reason |
|----------|------|--------|
| Code refactoring | update | Content-driven replacement |
| Function rewrite | update | Simple oldText/newText |
| Config line change | line | Precise line number control |
| Insert at specific position | line | Exact line number needed |
| Delete line range | line | Line-based operation |
**Features**:
- ✅ Exact text matching (precise and predictable)
- ✅ Auto line ending adaptation (CRLF/LF)
- ✅ No JSON escaping issues
- ✅ Multi-line text supported with quotes
### Fallback Strategy
1. **Edit fails 1+ times** → Use `ccw tool exec edit_file` (update mode)
2. **update mode fails**Try line mode with precise line numbers
3. **All fails** → Use Write to recreate file
1. **Edit fails 1+ times** → Use `ccw tool exec edit_file`
2. **Still fails**Use Write to recreate file
**Default mode**: update (exact matching with line ending adaptation)
## ⚡ sed Line Operations (Line Mode Alternative)
**When to Use**: Precise line number control (insert, delete, replace specific lines)
### Common Operations
```bash
# Insert after line 10
sed -i '10a\new line content' file.txt
# Insert before line 5
sed -i '5i\new line content' file.txt
# Delete line 3
sed -i '3d' file.txt
# Delete lines 5-8
sed -i '5,8d' file.txt
# Replace line 3 content
sed -i '3c\replacement line' file.txt
# Replace lines 3-5 content
sed -i '3,5c\single replacement line' file.txt
```
### Operation Reference
| Operation | Command | Example |
|-----------|---------|---------|
| Insert after | `Na\text` | `sed -i '10a\new' file` |
| Insert before | `Ni\text` | `sed -i '5i\new' file` |
| Delete line | `Nd` | `sed -i '3d' file` |
| Delete range | `N,Md` | `sed -i '5,8d' file` |
| Replace line | `Nc\text` | `sed -i '3c\new' file` |
**Note**: Use `sed -i` for in-place file modification (works in Git Bash on Windows)

View File

@@ -5,6 +5,33 @@ All notable changes to Claude Code Workflow (CCW) will be documented in this fil
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [6.1.3] - 2025-12-09
### 🔧 CLI Tool Simplification
This release simplifies the `ccw tool exec edit_file` command for better usability.
#### 🔄 Changed
- **Simplified edit_file**: Removed JSON input support, now uses parameter-based input only (`--path`, `--old`, `--new`)
- **Removed line mode**: Line operations now recommended via `sed` command
- **Updated tool-strategy.md**: Added sed as line operation alternative with usage examples
#### Usage
```bash
ccw tool exec edit_file --path "file.txt" --old "old text" --new "new text"
```
## [6.1.2] - 2025-12-09
### 🔔 Dashboard Update Notification & Bug Fixes
#### ✨ Added
- **Version Update Notification**: Dashboard now checks npm registry for updates and displays upgrade banner
- **Version Check API**: New `/api/version-check` endpoint with 1-hour cache
#### 🐛 Fixed
- **Hook Manager**: Fixed button click event handling for edit/delete operations (changed `e.target` to `e.currentTarget`)
## [5.9.6] - 2025-11-28
### 🚀 Review Cycle & Dashboard Enhancement

View File

@@ -5,7 +5,7 @@
<div align="center">
[![Version](https://img.shields.io/badge/version-v6.1.0-blue.svg)](https://github.com/catlog22/Claude-Code-Workflow/releases)
[![Version](https://img.shields.io/badge/version-v6.1.3-blue.svg)](https://github.com/catlog22/Claude-Code-Workflow/releases)
[![npm](https://img.shields.io/npm/v/claude-code-workflow.svg)](https://www.npmjs.com/package/claude-code-workflow)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]()
@@ -18,13 +18,11 @@
**Claude Code Workflow (CCW)** is a JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution. It transforms AI development from simple prompt chaining into a powerful orchestration system.
> **🎉 Version 6.1.0: Dashboard Icon Unification & CCW Tool System**
> **🎉 Version 6.1.3: CLI Tool Simplification**
>
> **Core Improvements**:
> - 🎨 **Dashboard Icon Unification**: Complete migration to Lucide Icons library across all views
> - 🛠️ **CCW Tool Exec System**: New `ccw tool exec` command for executing tools with JSON parameters
> - 🚀 **Explorer Enhancements**: Async task execution, CLI selector improvements, WebSocket frame handling
> - ✨ **Smart Server Recognition**: Intelligent workspace switching and MCP multi-source configuration
> - 🔧 **Simplified edit_file**: Parameter-based input only (`--path`, `--old`, `--new`)
> - 📝 **Updated tool-strategy.md**: Added sed as line operation alternative
>
> See [CHANGELOG.md](CHANGELOG.md) for complete details.
@@ -66,18 +64,6 @@ ccw install -m Global
ccw install -m Path -p /path/to/project
```
### **🚀 Alternative: One-Click Script Install**
**Windows (PowerShell):**
```powershell
Invoke-Expression (Invoke-WebRequest -Uri "https://raw.githubusercontent.com/catlog22/Claude-Code-Workflow/main/install-remote.ps1" -UseBasicParsing).Content
```
**Linux/macOS (Bash/Zsh):**
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/catlog22/Claude-Code-Workflow/main/install-remote.sh)
```
### **✅ Verify Installation**
After installation, open **Claude Code** and verify that workflow commands are available by running:
```bash

View File

@@ -108,9 +108,12 @@ export function run(argv) {
// Tool command
program
.command('tool [subcommand] [args] [json]')
.command('tool [subcommand] [args]')
.description('Execute CCW tools')
.action((subcommand, args, json) => toolCommand(subcommand, args, { json }));
.option('--path <path>', 'File path (for edit_file)')
.option('--old <text>', 'Old text to replace (for edit_file)')
.option('--new <text>', 'New text (for edit_file)')
.action((subcommand, args, options) => toolCommand(subcommand, args, options));
program.parse(argv);
}

View File

@@ -66,80 +66,13 @@ async function schemaAction(options) {
}
}
/**
* Read from stdin if available
*/
async function readStdin() {
// Check if stdin is a TTY (interactive terminal)
if (process.stdin.isTTY) {
return null;
}
return new Promise((resolve, reject) => {
let data = '';
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
let chunk;
while ((chunk = process.stdin.read()) !== null) {
data += chunk;
}
});
process.stdin.on('end', () => {
resolve(data.trim() || null);
});
process.stdin.on('error', (err) => {
reject(err);
});
});
}
/**
* Smart JSON parser with Windows path handling
*/
function parseJsonWithPathFix(jsonString) {
try {
// Try normal parse first
return JSON.parse(jsonString);
} catch (firstError) {
// If parsing fails, try to fix Windows paths
try {
// Pattern: "path": "X:\..." or "path":"X:\..."
const fixedJson = jsonString.replace(
/("(?:path|file|target|source|dest|destination)":\s*")([A-Za-z]:[^"]+)"/g,
(match, prefix, path) => {
// Convert backslashes to forward slashes (universal)
const fixedPath = path.replace(/\\/g, '/');
return `${prefix}${fixedPath}"`;
}
);
return JSON.parse(fixedJson);
} catch (secondError) {
// If still fails, throw original error with helpful message
const errorMsg = firstError.message;
const hint = errorMsg.includes('escaped character') || errorMsg.includes('position')
? '\n\n' + chalk.yellow('Hint: Windows paths in JSON need forward slashes or double backslashes:') +
'\n ' + chalk.green('✓ "D:/Claude_dms3/file.md"') +
'\n ' + chalk.green('✓ "D:\\\\Claude_dms3\\\\file.md"') +
'\n ' + chalk.red('✗ "D:\\Claude_dms3\\file.md"')
: '';
throw new Error(errorMsg + hint);
}
}
}
/**
* Execute a tool with given parameters
*/
async function execAction(toolName, jsonInput, options) {
async function execAction(toolName, options) {
if (!toolName) {
console.error(chalk.red('Tool name is required'));
console.error(chalk.gray('Usage: ccw tool exec <tool-name> \'{"param": "value"}\''));
console.error(chalk.gray('Usage: ccw tool exec edit_file --path file.txt --old "old" --new "new"'));
process.exit(1);
}
@@ -150,34 +83,22 @@ async function execAction(toolName, jsonInput, options) {
process.exit(1);
}
// Parse JSON input (default format)
let params = {};
// Build params from CLI options
const params = {};
if (jsonInput) {
try {
params = parseJsonWithPathFix(jsonInput);
} catch (error) {
console.error(chalk.red(`Invalid JSON: ${error.message}`));
if (toolName === 'edit_file') {
if (!options.path || !options.old || !options.new) {
console.error(chalk.red('edit_file requires --path, --old, and --new parameters'));
console.error(chalk.gray('Usage: ccw tool exec edit_file --path file.txt --old "old text" --new "new text"'));
process.exit(1);
}
}
// Check for stdin input (for piped commands)
const stdinData = await readStdin();
if (stdinData) {
// If tool has an 'input' parameter, use it
// Otherwise, try to parse stdin as JSON and merge with params
if (tool.parameters?.properties?.input) {
params.input = stdinData;
} else {
try {
const stdinJson = JSON.parse(stdinData);
params = { ...stdinJson, ...params };
} catch {
// If not JSON, store as 'input' anyway
params.input = stdinData;
}
}
params.path = options.path;
params.oldText = options.old;
params.newText = options.new;
} else {
console.error(chalk.red(`Tool "${toolName}" is not supported via CLI parameters`));
console.error(chalk.gray('Currently only edit_file is supported'));
process.exit(1);
}
// Execute tool
@@ -200,7 +121,7 @@ export async function toolCommand(subcommand, args, options) {
await schemaAction({ name: args });
break;
case 'exec':
await execAction(args, options.json, options);
await execAction(args, options);
break;
default:
console.log(chalk.bold.cyan('\nCCW Tool System\n'));
@@ -209,9 +130,9 @@ export async function toolCommand(subcommand, args, options) {
console.log(chalk.gray(' schema [name] Show tool schema (JSON)'));
console.log(chalk.gray(' exec <name> Execute a tool'));
console.log();
console.log('Examples:');
console.log('Usage:');
console.log(chalk.gray(' ccw tool list'));
console.log(chalk.gray(' ccw tool schema edit_file'));
console.log(chalk.gray(' ccw tool exec edit_file \'{"path":"file.txt","oldText":"old","newText":"new"}\''));
console.log(chalk.gray(' ccw tool exec edit_file --path file.txt --old "old text" --new "new text"'));
}
}

View File

@@ -39,6 +39,11 @@ function scanLiteDir(dir, type) {
tasks: loadTaskJsons(sessionPath)
};
// For lite-fix sessions, also load diagnoses separately
if (type === 'lite-fix') {
session.diagnoses = loadDiagnoses(sessionPath);
}
// Calculate progress
session.progress = calculateProgress(session.tasks);
@@ -268,7 +273,7 @@ export function getLiteTaskDetail(workflowDir, type, sessionId) {
if (!existsSync(dir)) return null;
return {
const detail = {
id: sessionId,
type,
path: dir,
@@ -277,6 +282,13 @@ export function getLiteTaskDetail(workflowDir, type, sessionId) {
explorations: loadExplorations(dir),
clarifications: loadClarifications(dir)
};
// For lite-fix sessions, also load diagnoses
if (type === 'lite-fix') {
detail.diagnoses = loadDiagnoses(dir);
}
return detail;
}
/**
@@ -312,3 +324,50 @@ function loadClarifications(sessionPath) {
return null;
}
}
/**
* Load diagnosis files for lite-fix sessions
* Loads diagnosis-*.json files from session root directory
* @param {string} sessionPath - Session directory path
* @returns {Object} - Diagnoses data with manifest and items
*/
function loadDiagnoses(sessionPath) {
const result = {
manifest: null,
items: []
};
// Try to load diagnoses-manifest.json first
const manifestPath = join(sessionPath, 'diagnoses-manifest.json');
if (existsSync(manifestPath)) {
try {
result.manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
} catch {
// Continue without manifest
}
}
// Load all diagnosis-*.json files from session root
try {
const diagnosisFiles = readdirSync(sessionPath)
.filter(f => f.startsWith('diagnosis-') && f.endsWith('.json'));
for (const file of diagnosisFiles) {
const filePath = join(sessionPath, file);
try {
const content = JSON.parse(readFileSync(filePath, 'utf8'));
result.items.push({
id: file.replace('diagnosis-', '').replace('.json', ''),
filename: file,
...content
});
} catch {
// Skip invalid files
}
}
} catch {
// Return empty items if directory read fails
}
return result;
}

View File

@@ -841,3 +841,331 @@
overflow: hidden;
}
/* ===================================
Fix Plan Enhanced Styles
=================================== */
.plan-root-cause-text,
.plan-strategy-text,
.plan-requirements-text {
font-size: 0.875rem;
line-height: 1.6;
color: hsl(var(--foreground));
background: hsl(var(--muted) / 0.3);
padding: 0.75rem;
border-radius: 0.375rem;
border-left: 3px solid hsl(var(--primary));
}
.severity-badge,
.risk-badge {
display: inline-block;
padding: 0.125rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 500;
text-transform: uppercase;
}
.severity-badge.critical,
.risk-badge.high {
background: hsl(0 70% 50% / 0.15);
color: hsl(0 70% 45%);
}
.severity-badge.high,
.risk-badge.medium {
background: hsl(30 90% 50% / 0.15);
color: hsl(30 90% 40%);
}
.severity-badge.medium {
background: hsl(45 90% 50% / 0.15);
color: hsl(45 80% 35%);
}
.severity-badge.low,
.risk-badge.low {
background: hsl(142 70% 50% / 0.15);
color: hsl(142 70% 35%);
}
.fix-tasks-summary {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.fix-task-summary-item {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
overflow: hidden;
}
.fix-task-summary-item .collapsible-header {
padding: 0.75rem 1rem;
}
.task-num {
font-weight: 600;
color: hsl(var(--primary));
font-size: 0.85rem;
}
.task-title-brief {
font-size: 0.875rem;
color: hsl(var(--foreground));
flex: 1;
}
.task-scope-badge {
padding: 0.125rem 0.5rem;
background: hsl(var(--muted));
border-radius: 0.25rem;
font-size: 0.7rem;
color: hsl(var(--muted-foreground));
}
.task-detail-section {
margin-bottom: 1rem;
}
.task-detail-section:last-child {
margin-bottom: 0;
}
.task-detail-section strong {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.025em;
color: hsl(var(--muted-foreground));
margin-bottom: 0.5rem;
}
.mod-points-list,
.verify-list {
margin: 0;
padding-left: 1rem;
font-size: 0.8rem;
}
.mod-points-list li,
.verify-list li {
margin-bottom: 0.375rem;
line-height: 1.5;
}
.mod-points-list code {
font-size: 0.75rem;
padding: 0.125rem 0.375rem;
background: hsl(var(--muted));
border-radius: 0.25rem;
color: hsl(var(--primary));
}
.func-name {
color: hsl(var(--muted-foreground));
font-size: 0.75rem;
}
.change-type {
color: hsl(var(--muted-foreground));
font-size: 0.7rem;
font-style: italic;
}
/* ===================================
Diagnoses Tab Styles
=================================== */
.diagnoses-tab-content {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.diagnoses-section-title {
font-size: 1rem;
font-weight: 600;
color: hsl(var(--foreground));
margin-bottom: 0.75rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.diagnoses-manifest-section {
background: hsl(var(--muted) / 0.3);
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
padding: 1rem;
}
.manifest-meta-grid {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.diagnoses-grid {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.diagnosis-card {
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
border-radius: 0.5rem;
overflow: hidden;
}
.diagnosis-header {
background: hsl(var(--muted) / 0.3);
}
.diagnosis-id {
font-weight: 600;
color: hsl(var(--foreground));
flex: 1;
display: flex;
align-items: center;
gap: 0.5rem;
}
.diag-section {
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid hsl(var(--border) / 0.5);
}
.diag-section:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.diag-section strong {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.025em;
color: hsl(var(--muted-foreground));
margin-bottom: 0.5rem;
}
.diag-section p {
margin: 0;
font-size: 0.875rem;
line-height: 1.6;
color: hsl(var(--foreground));
}
.issues-list {
margin: 0;
padding-left: 1rem;
font-size: 0.85rem;
}
.issue-item {
margin-bottom: 0.5rem;
padding: 0.5rem;
background: hsl(var(--muted) / 0.3);
border-radius: 0.25rem;
}
.issue-title {
font-weight: 500;
color: hsl(var(--foreground));
}
.issue-location {
font-size: 0.75rem;
margin-top: 0.25rem;
}
.issue-location code {
padding: 0.125rem 0.375rem;
background: hsl(var(--muted));
border-radius: 0.25rem;
color: hsl(var(--primary));
}
.contracts-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.contract-item {
padding: 0.75rem;
background: hsl(var(--muted) / 0.3);
border: 1px solid hsl(var(--border));
border-radius: 0.375rem;
}
.contract-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
}
.contract-endpoint {
font-weight: 500;
color: hsl(var(--foreground));
}
.contract-method {
padding: 0.125rem 0.375rem;
background: hsl(var(--primary) / 0.15);
color: hsl(var(--primary));
border-radius: 0.25rem;
font-size: 0.7rem;
font-weight: 600;
}
.contract-desc {
font-size: 0.8rem;
color: hsl(var(--muted-foreground));
}
.contract-issues {
font-size: 0.75rem;
color: hsl(0 70% 50%);
margin-top: 0.25rem;
}
.dataflow-details {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.df-item {
font-size: 0.85rem;
}
.df-label {
font-weight: 500;
color: hsl(var(--muted-foreground));
}
.df-transforms {
margin: 0.25rem 0 0 1rem;
padding-left: 0;
}
.recommendations-list {
margin: 0;
padding-left: 1.25rem;
font-size: 0.85rem;
}
.recommendations-list li {
margin-bottom: 0.375rem;
line-height: 1.5;
}

View File

@@ -136,6 +136,13 @@ function showLiteTaskDetailPage(sessionKey) {
<span class="tab-icon"><i data-lucide="ruler" class="w-4 h-4"></i></span>
<span class="tab-text">Plan</span>
</button>
${session.type === 'lite-fix' ? `
<button class="detail-tab" data-tab="diagnoses" onclick="switchLiteDetailTab('diagnoses')">
<span class="tab-icon"><i data-lucide="stethoscope" class="w-4 h-4"></i></span>
<span class="tab-text">Diagnoses</span>
${session.diagnoses?.items?.length ? `<span class="tab-count">${session.diagnoses.items.length}</span>` : ''}
</button>
` : ''}
<button class="detail-tab" data-tab="context" onclick="switchLiteDetailTab('context')">
<span class="tab-icon"><i data-lucide="package" class="w-4 h-4"></i></span>
<span class="tab-text">Context</span>
@@ -196,6 +203,17 @@ function switchLiteDetailTab(tabName) {
break;
case 'plan':
contentArea.innerHTML = renderLitePlanTab(session);
// Re-initialize collapsible sections for plan tab
setTimeout(() => {
initCollapsibleSections(contentArea);
}, 50);
break;
case 'diagnoses':
contentArea.innerHTML = renderDiagnosesTab(session);
// Re-initialize collapsible sections for diagnoses tab
setTimeout(() => {
initCollapsibleSections(contentArea);
}, 50);
break;
case 'context':
loadAndRenderLiteContextTab(session, contentArea);
@@ -287,13 +305,14 @@ function openTaskDrawerForLite(sessionId, taskId) {
function renderLitePlanTab(session) {
const plan = session.plan;
const isFixPlan = session.type === 'lite-fix';
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">No Plan Data</div>
<div class="empty-text">No plan.json found for this session.</div>
<div class="empty-text">No ${isFixPlan ? 'fix-plan.json' : 'plan.json'} found for this session.</div>
</div>
`;
}
@@ -308,6 +327,22 @@ function renderLitePlanTab(session) {
</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">
@@ -316,6 +351,14 @@ function renderLitePlanTab(session) {
</div>
` : ''}
<!-- User Requirements (fix-plan specific) -->
${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">
@@ -330,16 +373,74 @@ function renderLitePlanTab(session) {
<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>
<!-- Fix Tasks Summary (fix-plan specific) -->
${plan.tasks?.length ? `
<div class="plan-section">
<h4 class="plan-section-title"><i data-lucide="list-checks" class="w-4 h-4 inline mr-1"></i> Fix Tasks (${plan.tasks.length})</h4>
<div class="fix-tasks-summary">
${plan.tasks.map((task, idx) => `
<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>
` : ''}
<!-- Raw JSON -->
<div class="plan-section">
<h4 class="plan-section-title">{ } Raw JSON</h4>
<pre class="json-content">${escapeHtml(JSON.stringify(plan, null, 2))}</pre>
<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>
`;
@@ -393,3 +494,192 @@ async function loadAndRenderLiteSummaryTab(session, contentArea) {
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
}
}
// ============================================
// DIAGNOSES TAB RENDERING (lite-fix specific)
// ============================================
function renderDiagnosesTab(session) {
const diagnoses = session.diagnoses;
if (!diagnoses || (!diagnoses.manifest && diagnoses.items?.length === 0)) {
return `
<div class="tab-empty-state">
<div class="empty-icon"><i data-lucide="stethoscope" class="w-12 h-12"></i></div>
<div class="empty-title">No Diagnoses</div>
<div class="empty-text">No diagnosis-*.json files found for this session.</div>
</div>
`;
}
let sections = [];
// Manifest summary (if available)
if (diagnoses.manifest) {
sections.push(`
<div class="diagnoses-manifest-section">
<h4 class="diagnoses-section-title"><i data-lucide="clipboard-check" class="w-4 h-4 inline mr-1"></i> Diagnosis Summary</h4>
<div class="manifest-meta-grid">
${diagnoses.manifest.total_diagnoses ? `<div class="meta-item"><span class="meta-label">Total Diagnoses:</span> ${diagnoses.manifest.total_diagnoses}</div>` : ''}
${diagnoses.manifest.diagnosis_angles ? `<div class="meta-item"><span class="meta-label">Angles:</span> ${diagnoses.manifest.diagnosis_angles.join(', ')}</div>` : ''}
${diagnoses.manifest.created_at ? `<div class="meta-item"><span class="meta-label">Created:</span> ${formatDate(diagnoses.manifest.created_at)}</div>` : ''}
</div>
</div>
`);
}
// Individual diagnosis items
if (diagnoses.items && diagnoses.items.length > 0) {
const diagnosisCards = diagnoses.items.map(diag => renderDiagnosisCard(diag)).join('');
sections.push(`
<div class="diagnoses-items-section">
<h4 class="diagnoses-section-title"><i data-lucide="search" class="w-4 h-4 inline mr-1"></i> Diagnosis Details (${diagnoses.items.length})</h4>
<div class="diagnoses-grid">
${diagnosisCards}
</div>
</div>
`);
}
return `<div class="diagnoses-tab-content">${sections.join('')}</div>`;
}
function renderDiagnosisCard(diag) {
const diagJsonId = `diag-json-${diag.id}`.replace(/[^a-zA-Z0-9-]/g, '-');
taskJsonStore[diagJsonId] = diag;
return `
<div class="diagnosis-card collapsible-section">
<div class="collapsible-header diagnosis-header">
<span class="collapse-icon">▶</span>
<span class="diagnosis-id"><i data-lucide="file-search" class="w-4 h-4 inline mr-1"></i>${escapeHtml(diag.id)}</span>
<button class="btn-view-json" onclick="event.stopPropagation(); showJsonModal('${diagJsonId}', '${escapeHtml(diag.id)}')">{ } JSON</button>
</div>
<div class="collapsible-content collapsed">
${renderDiagnosisContent(diag)}
</div>
</div>
`;
}
function renderDiagnosisContent(diag) {
let content = [];
// Summary/Overview
if (diag.summary || diag.overview) {
content.push(`
<div class="diag-section">
<strong>Summary:</strong>
<p>${escapeHtml(diag.summary || diag.overview)}</p>
</div>
`);
}
// Root Cause Analysis
if (diag.root_cause || diag.root_cause_analysis) {
content.push(`
<div class="diag-section">
<strong>Root Cause:</strong>
<p>${escapeHtml(diag.root_cause || diag.root_cause_analysis)}</p>
</div>
`);
}
// Issues/Findings
if (diag.issues && Array.isArray(diag.issues)) {
content.push(`
<div class="diag-section">
<strong>Issues Found (${diag.issues.length}):</strong>
<ul class="issues-list">
${diag.issues.map(issue => `
<li class="issue-item">
${typeof issue === 'string' ? escapeHtml(issue) : `
<div class="issue-title">${escapeHtml(issue.title || issue.description || 'Unknown')}</div>
${issue.location ? `<div class="issue-location"><code>${escapeHtml(issue.location)}</code></div>` : ''}
${issue.severity ? `<span class="severity-badge ${issue.severity}">${escapeHtml(issue.severity)}</span>` : ''}
`}
</li>
`).join('')}
</ul>
</div>
`);
}
// Affected Files
if (diag.affected_files && Array.isArray(diag.affected_files)) {
content.push(`
<div class="diag-section">
<strong>Affected Files:</strong>
<div class="path-tags">
${diag.affected_files.map(f => `<span class="path-tag">${escapeHtml(typeof f === 'string' ? f : f.path || f.file)}</span>`).join('')}
</div>
</div>
`);
}
// API Contracts (for api-contracts diagnosis)
if (diag.contracts && Array.isArray(diag.contracts)) {
content.push(`
<div class="diag-section">
<strong>API Contracts (${diag.contracts.length}):</strong>
<div class="contracts-list">
${diag.contracts.map(contract => `
<div class="contract-item">
<div class="contract-header">
<span class="contract-endpoint">${escapeHtml(contract.endpoint || contract.name || 'Unknown')}</span>
${contract.method ? `<span class="contract-method">${escapeHtml(contract.method)}</span>` : ''}
</div>
${contract.description ? `<div class="contract-desc">${escapeHtml(contract.description)}</div>` : ''}
${contract.issues?.length ? `<div class="contract-issues">${contract.issues.length} issue(s)</div>` : ''}
</div>
`).join('')}
</div>
</div>
`);
}
// Dataflow Analysis (for dataflow diagnosis)
if (diag.dataflow || diag.data_flow) {
const df = diag.dataflow || diag.data_flow;
content.push(`
<div class="diag-section">
<strong>Data Flow Analysis:</strong>
${typeof df === 'string' ? `<p>${escapeHtml(df)}</p>` : `
<div class="dataflow-details">
${df.source ? `<div class="df-item"><span class="df-label">Source:</span> ${escapeHtml(df.source)}</div>` : ''}
${df.sink ? `<div class="df-item"><span class="df-label">Sink:</span> ${escapeHtml(df.sink)}</div>` : ''}
${df.transformations?.length ? `
<div class="df-item">
<span class="df-label">Transformations:</span>
<ol class="df-transforms">${df.transformations.map(t => `<li>${escapeHtml(t)}</li>`).join('')}</ol>
</div>
` : ''}
</div>
`}
</div>
`);
}
// Recommendations
if (diag.recommendations && Array.isArray(diag.recommendations)) {
content.push(`
<div class="diag-section">
<strong>Recommendations:</strong>
<ol class="recommendations-list">
${diag.recommendations.map(rec => `<li>${escapeHtml(typeof rec === 'string' ? rec : rec.description || rec.action)}</li>`).join('')}
</ol>
</div>
`);
}
// If no specific content was rendered, show raw JSON preview
if (content.length === 0) {
content.push(`
<div class="diag-section">
<pre class="json-content">${escapeHtml(JSON.stringify(diag, null, 2))}</pre>
</div>
`);
}
return content.join('');
}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "claude-code-workflow",
"version": "6.0.5",
"version": "6.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-code-workflow",
"version": "6.0.5",
"version": "6.1.3",
"license": "MIT",
"dependencies": {
"boxen": "^7.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "claude-code-workflow",
"version": "6.1.2",
"version": "6.1.4",
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
"type": "module",
"main": "ccw/src/index.js",