mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
feat: Add Phase 6 for Post-Implementation Review and enhance workflow execution
- Introduced Phase 6: Post-Implementation Review with detailed steps for specialized reviews (quality, security, architecture, action items). - Updated SKILL.md to reflect new phase and its execution lifecycle. - Enhanced Flowchart component to conditionally display step statuses based on task tracking. - Modified TaskDrawer to pass status tracking prop to Flowchart. - Improved AgentList and other terminal dashboard components for better UI consistency and responsiveness. - Removed GlobalKpiBar component as part of UI cleanup. - Added issue detail preview in TerminalWorkbench for better user experience when no terminal is active. - Updated localization files for new strings related to the terminal dashboard and workbench. - Enhanced TaskListTab to conditionally render task stats and status dropdown based on task status tracking.
This commit is contained in:
@@ -1,322 +0,0 @@
|
||||
---
|
||||
name: review
|
||||
description: Post-implementation review with specialized types (security/architecture/action-items/quality) using analysis agents and Gemini
|
||||
argument-hint: "[--type=security|architecture|action-items|quality] [--archived] [optional: session-id]"
|
||||
---
|
||||
|
||||
## Command Overview: /workflow:review
|
||||
|
||||
**Optional specialized review** for completed implementations. In the standard workflow, **passing tests = approved code**. Use this command only when specialized review is required (security, architecture, compliance, docs).
|
||||
|
||||
## Philosophy: "Tests Are the Review"
|
||||
|
||||
- **Default**: All tests pass -> Code approved
|
||||
- **Optional**: Specialized reviews for:
|
||||
- Security audits (vulnerabilities, auth/authz)
|
||||
- Architecture compliance (patterns, technical debt)
|
||||
- Action items verification (requirements met, acceptance criteria)
|
||||
|
||||
## Review Types
|
||||
|
||||
| Type | Focus | Use Case |
|
||||
|------|-------|----------|
|
||||
| `quality` | Code quality, best practices, maintainability | Default general review |
|
||||
| `security` | Security vulnerabilities, data handling, access control | Security audits |
|
||||
| `architecture` | Architectural patterns, technical debt, design decisions | Architecture compliance |
|
||||
| `action-items` | Requirements met, acceptance criteria verified, action items completed | Pre-deployment verification |
|
||||
|
||||
**Notes**:
|
||||
- For documentation generation, use `/workflow:tools:docs`
|
||||
- For CLAUDE.md updates, use `/update-memory-related`
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Input Parsing:
|
||||
├─ Parse --type flag (default: quality)
|
||||
├─ Parse --archived flag (search in archives)
|
||||
└─ Parse session-id argument (optional)
|
||||
|
||||
Step 1: Session Resolution
|
||||
└─ Decision:
|
||||
├─ session-id provided + --archived → Search .workflow/archives/
|
||||
├─ session-id provided → Search .workflow/active/ first, then archives
|
||||
└─ Not provided → Auto-detect from .workflow/active/
|
||||
|
||||
Step 2: Validation
|
||||
├─ Check session directory exists (active or archived)
|
||||
└─ Check for completed implementation (.summaries/IMPL-*.md exists)
|
||||
|
||||
Step 3: Type Check
|
||||
└─ Decision:
|
||||
├─ type=docs → Redirect to /workflow:tools:docs
|
||||
└─ Other types → Continue to analysis
|
||||
|
||||
Step 4: Model Analysis Phase
|
||||
├─ Load context (summaries, test results, changed files)
|
||||
└─ Perform specialized review by type:
|
||||
├─ security → Security patterns + Gemini analysis
|
||||
├─ architecture → Qwen architecture analysis
|
||||
├─ quality → Gemini code quality analysis
|
||||
└─ action-items → Requirements verification
|
||||
|
||||
Step 5: Generate Report
|
||||
└─ Output: REVIEW-{type}.md
|
||||
```
|
||||
|
||||
## Execution Template
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Optional specialized review for completed implementation
|
||||
|
||||
# Step 1: Session ID resolution and location detection
|
||||
if [ -n "$SESSION_ARG" ]; then
|
||||
sessionId="$SESSION_ARG"
|
||||
else
|
||||
sessionId=$(find .workflow/active/ -name "WFS-*" -type d | head -1 | xargs basename)
|
||||
fi
|
||||
|
||||
# Step 2: Resolve session path (active or archived)
|
||||
# Priority: --archived flag → active → archives
|
||||
if [ -n "$ARCHIVED_FLAG" ]; then
|
||||
sessionPath=".workflow/archives/${sessionId}"
|
||||
elif [ -d ".workflow/active/${sessionId}" ]; then
|
||||
sessionPath=".workflow/active/${sessionId}"
|
||||
elif [ -d ".workflow/archives/${sessionId}" ]; then
|
||||
sessionPath=".workflow/archives/${sessionId}"
|
||||
echo "Note: Session found in archives, running review on archived session"
|
||||
else
|
||||
echo "Session ${sessionId} not found in active or archives"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for completed tasks
|
||||
if [ ! -d "${sessionPath}/.summaries" ] || [ -z "$(find ${sessionPath}/.summaries/ -name "IMPL-*.md" -type f 2>/dev/null)" ]; then
|
||||
echo "No completed implementation found. Complete implementation first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Determine review type (default: quality)
|
||||
review_type="${TYPE_ARG:-quality}"
|
||||
|
||||
# Redirect docs review to specialized command
|
||||
if [ "$review_type" = "docs" ]; then
|
||||
echo "For documentation generation, please use:"
|
||||
echo " /workflow:tools:docs"
|
||||
echo ""
|
||||
echo "The docs command provides:"
|
||||
echo " - Hierarchical architecture documentation"
|
||||
echo " - API documentation generation"
|
||||
echo " - Documentation structure analysis"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 4: Analysis handover → Model takes control
|
||||
# BASH_EXECUTION_STOPS → MODEL_ANALYSIS_BEGINS
|
||||
```
|
||||
|
||||
### Model Analysis Phase
|
||||
|
||||
After bash validation, the model takes control to:
|
||||
|
||||
1. **Load Context**: Read completed task summaries and changed files
|
||||
```bash
|
||||
# Load implementation summaries (iterate through .summaries/ directory)
|
||||
for summary in ${sessionPath}/.summaries/*.md; do
|
||||
cat "$summary"
|
||||
done
|
||||
|
||||
# Load test results (if available)
|
||||
for test_summary in ${sessionPath}/.summaries/TEST-FIX-*.md 2>/dev/null; do
|
||||
cat "$test_summary"
|
||||
done
|
||||
|
||||
# Get changed files
|
||||
git log --since="$(cat ${sessionPath}/workflow-session.json | jq -r .created_at)" --name-only --pretty=format: | sort -u
|
||||
```
|
||||
|
||||
2. **Perform Specialized Review**: Based on `review_type`
|
||||
|
||||
**Security Review** (`--type=security`):
|
||||
- Use ripgrep for security patterns:
|
||||
```bash
|
||||
rg "password|token|secret|auth" -g "*.{ts,js,py}"
|
||||
rg "eval|exec|innerHTML|dangerouslySetInnerHTML" -g "*.{ts,js,tsx}"
|
||||
```
|
||||
- Use Gemini for security analysis:
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: Security audit of completed implementation
|
||||
TASK: Review code for security vulnerabilities, insecure patterns, auth/authz issues
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Security findings report with severity levels
|
||||
RULES: Focus on OWASP Top 10, authentication, authorization, data validation, injection risks
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Architecture Review** (`--type=architecture`):
|
||||
- Use Qwen for architecture analysis:
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: Architecture compliance review
|
||||
TASK: Evaluate adherence to architectural patterns, identify technical debt, review design decisions
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Architecture assessment with recommendations
|
||||
RULES: Check for patterns, separation of concerns, modularity, scalability
|
||||
" --tool qwen --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Quality Review** (`--type=quality`):
|
||||
- Use Gemini for code quality:
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: Code quality and best practices review
|
||||
TASK: Assess code readability, maintainability, adherence to best practices
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Quality assessment with improvement suggestions
|
||||
RULES: Check for code smells, duplication, complexity, naming conventions
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Action Items Review** (`--type=action-items`):
|
||||
- Verify all requirements and acceptance criteria met:
|
||||
```bash
|
||||
# Load task requirements and acceptance criteria
|
||||
for task_file in ${sessionPath}/.task/*.json; do
|
||||
cat "$task_file" | jq -r '
|
||||
"Task: " + .id + "\n" +
|
||||
"Requirements: " + .description + "\n" +
|
||||
"Acceptance: " + (.convergence.criteria | join(", "))
|
||||
'
|
||||
done
|
||||
|
||||
# Check implementation summaries against requirements
|
||||
ccw cli -p "
|
||||
PURPOSE: Verify all requirements and acceptance criteria are met
|
||||
TASK: Cross-check implementation summaries against original requirements
|
||||
CONTEXT: @.task/IMPL-*.json,.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED:
|
||||
- Requirements coverage matrix
|
||||
- Acceptance criteria verification
|
||||
- Missing/incomplete action items
|
||||
- Pre-deployment readiness assessment
|
||||
RULES:
|
||||
- Check each requirement has corresponding implementation
|
||||
- Verify all acceptance criteria are met
|
||||
- Flag any incomplete or missing action items
|
||||
- Assess deployment readiness
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
|
||||
3. **Generate Review Report**: Create structured report
|
||||
```markdown
|
||||
# Review Report: ${review_type}
|
||||
|
||||
**Session**: ${sessionId}
|
||||
**Date**: $(date)
|
||||
**Type**: ${review_type}
|
||||
|
||||
## Summary
|
||||
- Tasks Reviewed: [count IMPL tasks]
|
||||
- Files Changed: [count files]
|
||||
- Severity: [High/Medium/Low]
|
||||
|
||||
## Findings
|
||||
|
||||
### Critical Issues
|
||||
- [Issue 1 with file:line reference]
|
||||
- [Issue 2 with file:line reference]
|
||||
|
||||
### Recommendations
|
||||
- [Recommendation 1]
|
||||
- [Recommendation 2]
|
||||
|
||||
### Positive Observations
|
||||
- [Good pattern observed]
|
||||
|
||||
## Action Items
|
||||
- [ ] [Action 1]
|
||||
- [ ] [Action 2]
|
||||
```
|
||||
|
||||
4. **Output Files**:
|
||||
```bash
|
||||
# Save review report
|
||||
Write(${sessionPath}/REVIEW-${review_type}.md)
|
||||
|
||||
# Update session metadata
|
||||
# (optional) Update workflow-session.json with review status
|
||||
```
|
||||
|
||||
5. **Optional: Update Memory** (if docs review or significant findings):
|
||||
```bash
|
||||
# If architecture or quality issues found, suggest memory update
|
||||
if [ "$review_type" = "architecture" ] || [ "$review_type" = "quality" ]; then
|
||||
echo "Consider updating project documentation:"
|
||||
echo " /update-memory-related"
|
||||
fi
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```bash
|
||||
# General quality review after implementation
|
||||
/workflow:review
|
||||
|
||||
# Security audit before deployment
|
||||
/workflow:review --type=security
|
||||
|
||||
# Architecture review for specific session
|
||||
/workflow:review --type=architecture WFS-payment-integration
|
||||
|
||||
# Review an archived session (auto-detects if not in active)
|
||||
/workflow:review --type=security WFS-old-feature
|
||||
|
||||
# Explicitly review archived session
|
||||
/workflow:review --archived --type=quality WFS-completed-feature
|
||||
|
||||
# Documentation review
|
||||
/workflow:review --type=docs
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Simple Validation**: Check session exists and has completed tasks
|
||||
- **No Complex Orchestration**: Direct analysis, no multi-phase pipeline
|
||||
- **Specialized Reviews**: Different prompts and tools for different review types
|
||||
- **Archived Session Support**: Review archived sessions with `--archived` flag or auto-detection
|
||||
- **MCP Integration**: Fast code search for security and architecture patterns
|
||||
- **CLI Tool Integration**: Gemini for analysis, Qwen for architecture
|
||||
- **Structured Output**: Markdown reports with severity levels and action items
|
||||
- **Optional Memory Update**: Suggests documentation updates for significant findings
|
||||
|
||||
## Integration with Workflow
|
||||
|
||||
```
|
||||
Standard Workflow:
|
||||
plan -> execute -> test-gen -> execute (complete)
|
||||
|
||||
Optional Review (when needed):
|
||||
plan -> execute -> test-gen -> execute -> review (security/architecture/docs)
|
||||
```
|
||||
|
||||
**When to Use**:
|
||||
- Before production deployment (security review + action-items review)
|
||||
- After major feature (architecture review)
|
||||
- Before code freeze (quality review)
|
||||
- Pre-deployment verification (action-items review)
|
||||
|
||||
**When NOT to Use**:
|
||||
- Regular development (tests are sufficient)
|
||||
- Simple bug fixes (test-fix-agent handles it)
|
||||
- Minor changes (update-memory-related is enough)
|
||||
|
||||
## Post-Review Action
|
||||
|
||||
After review completion, prompt user:
|
||||
```
|
||||
Review complete. Would you like to complete and archive this session?
|
||||
→ Run /workflow:session:complete to archive with lessons learned
|
||||
```
|
||||
@@ -116,14 +116,21 @@ Phase 5: Completion
|
||||
├─ Update task statuses in JSON files
|
||||
├─ Generate summaries
|
||||
└─ AskUserQuestion: Choose next step
|
||||
├─ "Enter Review" → /workflow:review
|
||||
├─ "Enter Review" → Phase 6
|
||||
└─ "Complete Session" → /workflow:session:complete
|
||||
|
||||
Phase 6: Post-Implementation Review (Optional)
|
||||
└─ Ref: phases/06-review.md
|
||||
├─ Select review type (quality/security/architecture/action-items)
|
||||
├─ CLI-assisted analysis (Gemini/Qwen)
|
||||
├─ Generate REVIEW-{type}.md report
|
||||
└─ Post-review: another review or complete session
|
||||
|
||||
Resume Mode (--resume-session):
|
||||
├─ Skip Phase 1 & Phase 2
|
||||
└─ Entry Point: Phase 3 (TodoWrite Generation)
|
||||
├─ Update session status to "active" (if not already)
|
||||
└─ Continue: Phase 4 → Phase 5
|
||||
└─ Continue: Phase 4 → Phase 5 → [Phase 6]
|
||||
```
|
||||
|
||||
## Execution Lifecycle
|
||||
@@ -346,7 +353,7 @@ if (autoYes) {
|
||||
```
|
||||
|
||||
**Based on user selection**:
|
||||
- **"Enter Review"**: Execute `/workflow:review`
|
||||
- **"Enter Review"**: Execute Phase 6 → `Ref: phases/06-review.md`
|
||||
- **"Complete Session"**: Execute `/workflow:session:complete`
|
||||
|
||||
### Post-Completion Expansion
|
||||
@@ -532,6 +539,12 @@ meta.agent missing → Infer from meta.type:
|
||||
- "docs" → @doc-generator
|
||||
```
|
||||
|
||||
## Phase Reference Documents
|
||||
|
||||
| Phase | Document | Purpose |
|
||||
|-------|----------|---------|
|
||||
| 6 | [phases/06-review.md](phases/06-review.md) | Post-implementation specialized review (security/architecture/quality/action-items) |
|
||||
|
||||
## Workflow File Structure Reference
|
||||
```
|
||||
.workflow/active/WFS-[topic-slug]/
|
||||
|
||||
212
.claude/skills/workflow-execute/phases/06-review.md
Normal file
212
.claude/skills/workflow-execute/phases/06-review.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Phase 6: Post-Implementation Review
|
||||
|
||||
Optional specialized review for completed implementations. In the standard workflow, **passing tests = approved code**. This phase executes only when user selects "Enter Review" in Phase 5 completion.
|
||||
|
||||
## Objective
|
||||
|
||||
- Perform specialized review (security/architecture/quality/action-items) on completed implementation
|
||||
- Generate structured review report with severity levels and action items
|
||||
- Provide CLI-assisted analysis using Gemini/Qwen for deep review
|
||||
|
||||
## Philosophy: "Tests Are the Review"
|
||||
|
||||
- **Default**: All tests pass → Code approved
|
||||
- **Optional**: This phase for specialized reviews:
|
||||
- Security audits (vulnerabilities, auth/authz)
|
||||
- Architecture compliance (patterns, technical debt)
|
||||
- Action items verification (requirements met, acceptance criteria)
|
||||
- Code quality assessment (best practices, maintainability)
|
||||
|
||||
## Review Types
|
||||
|
||||
| Type | Focus | Use Case |
|
||||
|------|-------|----------|
|
||||
| `quality` | Code quality, best practices, maintainability | Default general review |
|
||||
| `security` | Security vulnerabilities, data handling, access control | Security audits |
|
||||
| `architecture` | Architectural patterns, technical debt, design decisions | Architecture compliance |
|
||||
| `action-items` | Requirements met, acceptance criteria verified | Pre-deployment verification |
|
||||
|
||||
**Notes**:
|
||||
- For documentation generation, use `/workflow:tools:docs`
|
||||
- For CLAUDE.md updates, use `/update-memory-related`
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 6.1: Review Type Selection
|
||||
|
||||
Prompt user to select review type:
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Select review type:",
|
||||
header: "Review Type",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Quality (Recommended)", description: "Code quality, best practices, maintainability" },
|
||||
{ label: "Security", description: "Security vulnerabilities, data handling, access control" },
|
||||
{ label: "Architecture", description: "Architectural patterns, technical debt, design decisions" },
|
||||
{ label: "Action Items", description: "Requirements met, acceptance criteria verified" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
**Auto Mode** (`--yes`): Skip selection, default to `quality`.
|
||||
|
||||
### Step 6.2: Validation
|
||||
|
||||
```bash
|
||||
# Verify completed implementation exists
|
||||
sessionPath=".workflow/active/${sessionId}"
|
||||
|
||||
if [ ! -d "${sessionPath}/.summaries" ] || [ -z "$(find ${sessionPath}/.summaries/ -name "IMPL-*.md" -type f 2>/dev/null)" ]; then
|
||||
echo "No completed implementation found. Complete implementation first."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 6.3: Context Loading
|
||||
|
||||
```bash
|
||||
# Load implementation summaries
|
||||
for summary in ${sessionPath}/.summaries/*.md; do
|
||||
cat "$summary"
|
||||
done
|
||||
|
||||
# Load test results (if available)
|
||||
for test_summary in ${sessionPath}/.summaries/TEST-FIX-*.md 2>/dev/null; do
|
||||
cat "$test_summary"
|
||||
done
|
||||
|
||||
# Get changed files
|
||||
git log --since="$(cat ${sessionPath}/workflow-session.json | jq -r .created_at)" --name-only --pretty=format: | sort -u
|
||||
```
|
||||
|
||||
### Step 6.4: Specialized Review Analysis
|
||||
|
||||
Based on `review_type`, execute the corresponding analysis:
|
||||
|
||||
**Security Review** (`security`):
|
||||
```bash
|
||||
# Pattern scan
|
||||
rg "password|token|secret|auth" -g "*.{ts,js,py}"
|
||||
rg "eval|exec|innerHTML|dangerouslySetInnerHTML" -g "*.{ts,js,tsx}"
|
||||
|
||||
# Gemini security analysis
|
||||
ccw cli -p "
|
||||
PURPOSE: Security audit of completed implementation
|
||||
TASK: Review code for security vulnerabilities, insecure patterns, auth/authz issues
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Security findings report with severity levels
|
||||
RULES: Focus on OWASP Top 10, authentication, authorization, data validation, injection risks
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Architecture Review** (`architecture`):
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: Architecture compliance review
|
||||
TASK: Evaluate adherence to architectural patterns, identify technical debt, review design decisions
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Architecture assessment with recommendations
|
||||
RULES: Check for patterns, separation of concerns, modularity, scalability
|
||||
" --tool qwen --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Quality Review** (`quality`):
|
||||
```bash
|
||||
ccw cli -p "
|
||||
PURPOSE: Code quality and best practices review
|
||||
TASK: Assess code readability, maintainability, adherence to best practices
|
||||
CONTEXT: @.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED: Quality assessment with improvement suggestions
|
||||
RULES: Check for code smells, duplication, complexity, naming conventions
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
**Action Items Review** (`action-items`):
|
||||
```bash
|
||||
# Load task requirements and acceptance criteria
|
||||
for task_file in ${sessionPath}/.task/*.json; do
|
||||
cat "$task_file" | jq -r '
|
||||
"Task: " + .id + "\n" +
|
||||
"Requirements: " + .description + "\n" +
|
||||
"Acceptance: " + (.convergence.criteria | join(", "))
|
||||
'
|
||||
done
|
||||
|
||||
# Cross-check implementation against requirements
|
||||
ccw cli -p "
|
||||
PURPOSE: Verify all requirements and acceptance criteria are met
|
||||
TASK: Cross-check implementation summaries against original requirements
|
||||
CONTEXT: @.task/IMPL-*.json,.summaries/IMPL-*.md,../.. @../../project-tech.json @../../project-guidelines.json
|
||||
EXPECTED:
|
||||
- Requirements coverage matrix
|
||||
- Acceptance criteria verification
|
||||
- Missing/incomplete action items
|
||||
- Pre-deployment readiness assessment
|
||||
RULES:
|
||||
- Check each requirement has corresponding implementation
|
||||
- Verify all acceptance criteria are met
|
||||
- Flag any incomplete or missing action items
|
||||
- Assess deployment readiness
|
||||
" --tool gemini --mode write --cd ${sessionPath}
|
||||
```
|
||||
|
||||
### Step 6.5: Generate Review Report
|
||||
|
||||
Write structured report to session directory:
|
||||
|
||||
```markdown
|
||||
# Review Report: ${review_type}
|
||||
|
||||
**Session**: ${sessionId}
|
||||
**Date**: $(date)
|
||||
**Type**: ${review_type}
|
||||
|
||||
## Summary
|
||||
- Tasks Reviewed: [count IMPL tasks]
|
||||
- Files Changed: [count files]
|
||||
- Severity: [High/Medium/Low]
|
||||
|
||||
## Findings
|
||||
|
||||
### Critical Issues
|
||||
- [Issue 1 with file:line reference]
|
||||
|
||||
### Recommendations
|
||||
- [Recommendation 1]
|
||||
|
||||
### Positive Observations
|
||||
- [Good pattern observed]
|
||||
|
||||
## Action Items
|
||||
- [ ] [Action 1]
|
||||
- [ ] [Action 2]
|
||||
```
|
||||
|
||||
**Output**: `${sessionPath}/REVIEW-${review_type}.md`
|
||||
|
||||
### Step 6.6: Post-Review Prompt
|
||||
|
||||
```
|
||||
Review complete. Would you like to:
|
||||
→ Run another review type
|
||||
→ Complete session: /workflow:session:complete
|
||||
```
|
||||
|
||||
If architecture or quality issues found, suggest:
|
||||
```
|
||||
Consider updating project documentation:
|
||||
→ /update-memory-related
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **File**: `${sessionPath}/REVIEW-${review_type}.md`
|
||||
- **TodoWrite**: Mark Phase 6 completed
|
||||
|
||||
## Next Phase
|
||||
|
||||
Return to orchestrator for session completion or additional review cycles.
|
||||
@@ -54,8 +54,9 @@ const StatusIcon: React.FC<{ status?: string; className?: string }> = ({ status,
|
||||
const CustomNode: React.FC<{ data: FlowchartNodeData }> = ({ data }) => {
|
||||
const isPreAnalysis = data.type === 'pre-analysis';
|
||||
const isSection = data.type === 'section';
|
||||
const isCompleted = data.status === 'completed';
|
||||
const isInProgress = data.status === 'in_progress';
|
||||
const showStatus = data.showStepStatus !== false;
|
||||
const isCompleted = showStatus && data.status === 'completed';
|
||||
const isInProgress = showStatus && data.status === 'in_progress';
|
||||
|
||||
if (isSection) {
|
||||
return (
|
||||
@@ -101,14 +102,14 @@ const CustomNode: React.FC<{ data: FlowchartNodeData }> = ({ data }) => {
|
||||
<span
|
||||
className={`flex-shrink-0 w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${stepBgClass}`}
|
||||
>
|
||||
{isCompleted ? <CheckCircle className="h-4 w-4" /> : data.step}
|
||||
{isCompleted && showStatus ? <CheckCircle className="h-4 w-4" /> : data.step}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`text-sm font-semibold ${isCompleted ? 'text-green-700 dark:text-green-400' : 'text-foreground'}`}>
|
||||
{data.label}
|
||||
</span>
|
||||
{data.status && data.status !== 'pending' && (
|
||||
{showStatus && data.status && data.status !== 'pending' && (
|
||||
<StatusIcon status={data.status} className="h-3.5 w-3.5" />
|
||||
)}
|
||||
</div>
|
||||
@@ -141,12 +142,13 @@ const nodeTypes: NodeTypes = {
|
||||
export interface FlowchartProps {
|
||||
flowControl: FlowControl;
|
||||
className?: string;
|
||||
showStepStatus?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flowchart component for visualizing implementation approach
|
||||
*/
|
||||
export function Flowchart({ flowControl, className = '' }: FlowchartProps) {
|
||||
export function Flowchart({ flowControl, className = '', showStepStatus = true }: FlowchartProps) {
|
||||
const preAnalysis = flowControl.pre_analysis || [];
|
||||
const implSteps = flowControl.implementation_approach || [];
|
||||
|
||||
@@ -185,6 +187,7 @@ export function Flowchart({ flowControl, className = '' }: FlowchartProps) {
|
||||
step: `P${idx + 1}`,
|
||||
output: step.output_to,
|
||||
type: 'pre-analysis' as const,
|
||||
showStepStatus,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -308,6 +311,7 @@ export function Flowchart({ flowControl, className = '' }: FlowchartProps) {
|
||||
type: 'implementation' as const,
|
||||
dependsOn,
|
||||
status: stepStatus,
|
||||
showStepStatus,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -411,10 +415,12 @@ export function Flowchart({ flowControl, className = '' }: FlowchartProps) {
|
||||
nodeColor={(node) => {
|
||||
const data = node.data as FlowchartNodeData;
|
||||
if (data.type === 'section') return '#9ca3af';
|
||||
// Status-based colors
|
||||
if (data.status === 'completed') return '#22c55e'; // green-500
|
||||
if (data.status === 'in_progress') return '#f59e0b'; // amber-500
|
||||
if (data.status === 'blocked') return '#ef4444'; // red-500
|
||||
// Status-based colors (only when status tracking is enabled)
|
||||
if (data.showStepStatus !== false) {
|
||||
if (data.status === 'completed') return '#22c55e'; // green-500
|
||||
if (data.status === 'in_progress') return '#f59e0b'; // amber-500
|
||||
if (data.status === 'blocked') return '#ef4444'; // red-500
|
||||
}
|
||||
if (data.type === 'pre-analysis') return '#f59e0b';
|
||||
return '#3b82f6';
|
||||
}}
|
||||
|
||||
@@ -382,7 +382,7 @@ export function TaskDrawer({ task, isOpen, onClose }: TaskDrawerProps) {
|
||||
{/* Flowchart Tab */}
|
||||
{hasFlowchart && (
|
||||
<TabsContent value="flowchart" className="mt-4 pb-6">
|
||||
<Flowchart flowControl={flowControl!} className="min-h-[400px]" />
|
||||
<Flowchart flowControl={flowControl!} className="min-h-[400px]" showStepStatus={hasStatusTracking} />
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ export function AgentList() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{/* Section header */}
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-t border-border shrink-0">
|
||||
{/* Section header with visual separation */}
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-t border-border bg-muted/20 shrink-0">
|
||||
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
{formatMessage({ id: 'terminalDashboard.agentList.title' })}
|
||||
@@ -110,8 +110,8 @@ export function AgentList() {
|
||||
|
||||
{/* Plan list or empty state */}
|
||||
{planEntries.length === 0 ? (
|
||||
<div className="flex items-center justify-center py-4 px-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<div className="flex items-center justify-center py-3 px-3">
|
||||
<p className="text-[10px] text-muted-foreground">
|
||||
{formatMessage({ id: 'terminalDashboard.agentList.noAgents' })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
// ========================================
|
||||
// GlobalKpiBar Component
|
||||
// ========================================
|
||||
// Top bar showing 3 KPI metrics spanning the full page width.
|
||||
// Metrics:
|
||||
// 1. Active Sessions - count from sessionManagerStore (wraps cliSessionStore)
|
||||
// 2. Queue Size - pending/ready items count from useIssueQueue React Query hook
|
||||
// 3. Alert Count - total alerts from all terminalMetas
|
||||
//
|
||||
// Per design spec (V-001): consumes sessionManagerStore, NOT cliSessionStore directly.
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Activity, ListChecks, AlertTriangle } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
useSessionManagerStore,
|
||||
selectGroups,
|
||||
selectTerminalMetas,
|
||||
} from '@/stores/sessionManagerStore';
|
||||
import { useIssueQueue } from '@/hooks/useIssues';
|
||||
import type { TerminalStatus } from '@/types/terminal-dashboard';
|
||||
|
||||
// ========== KPI Item ==========
|
||||
|
||||
function KpiItem({
|
||||
icon: Icon,
|
||||
label,
|
||||
value,
|
||||
variant = 'default',
|
||||
}: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
label: string;
|
||||
value: number;
|
||||
variant?: 'default' | 'primary' | 'warning' | 'destructive';
|
||||
}) {
|
||||
const variantStyles = {
|
||||
default: 'text-muted-foreground',
|
||||
primary: 'text-primary',
|
||||
warning: 'text-warning',
|
||||
destructive: 'text-destructive',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon className={cn('w-4 h-4', variantStyles[variant])} />
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
<span className={cn('text-sm font-semibold tabular-nums', variantStyles[variant])}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Main Component ==========
|
||||
|
||||
export function GlobalKpiBar() {
|
||||
const { formatMessage } = useIntl();
|
||||
const groups = useSessionManagerStore(selectGroups);
|
||||
const terminalMetas = useSessionManagerStore(selectTerminalMetas);
|
||||
const queueQuery = useIssueQueue();
|
||||
|
||||
// Derive active session count from sessionManagerStore groups
|
||||
const sessionCount = useMemo(() => {
|
||||
const allSessionIds = groups.flatMap((g) => g.sessionIds);
|
||||
// Count sessions that have 'active' status in terminalMetas
|
||||
let activeCount = 0;
|
||||
for (const sid of allSessionIds) {
|
||||
const meta = terminalMetas[sid];
|
||||
const status: TerminalStatus = meta?.status ?? 'idle';
|
||||
if (status === 'active') {
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
// If no sessions are managed in groups, return total unique session IDs
|
||||
// This ensures the KPI shows meaningful data even before grouping
|
||||
return activeCount > 0 ? activeCount : allSessionIds.length;
|
||||
}, [groups, terminalMetas]);
|
||||
|
||||
// Derive queue pending count from useIssueQueue data
|
||||
const queuePendingCount = useMemo(() => {
|
||||
const queue = queueQuery.data;
|
||||
if (!queue) return 0;
|
||||
// Count all items across grouped_items
|
||||
let count = 0;
|
||||
if (queue.grouped_items) {
|
||||
for (const items of Object.values(queue.grouped_items)) {
|
||||
count += items.length;
|
||||
}
|
||||
}
|
||||
// Also count ungrouped tasks and solutions
|
||||
if (queue.tasks) count += queue.tasks.length;
|
||||
if (queue.solutions) count += queue.solutions.length;
|
||||
return count;
|
||||
}, [queueQuery.data]);
|
||||
|
||||
// Derive total alert count from all terminalMetas
|
||||
const totalAlerts = useMemo(() => {
|
||||
let count = 0;
|
||||
for (const meta of Object.values(terminalMetas)) {
|
||||
count += meta.alertCount;
|
||||
}
|
||||
return count;
|
||||
}, [terminalMetas]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-6 px-4 py-2 border-b border-border bg-muted/30 shrink-0">
|
||||
<KpiItem
|
||||
icon={Activity}
|
||||
label={formatMessage({ id: 'terminalDashboard.kpi.activeSessions' })}
|
||||
value={sessionCount}
|
||||
variant="primary"
|
||||
/>
|
||||
|
||||
<div className="w-px h-4 bg-border" />
|
||||
|
||||
<KpiItem
|
||||
icon={ListChecks}
|
||||
label={formatMessage({ id: 'terminalDashboard.kpi.queueSize' })}
|
||||
value={queuePendingCount}
|
||||
variant={queuePendingCount > 0 ? 'warning' : 'default'}
|
||||
/>
|
||||
|
||||
<div className="w-px h-4 bg-border" />
|
||||
|
||||
<KpiItem
|
||||
icon={AlertTriangle}
|
||||
label={formatMessage({ id: 'terminalDashboard.kpi.alertCount' })}
|
||||
value={totalAlerts}
|
||||
variant={totalAlerts > 0 ? 'destructive' : 'default'}
|
||||
/>
|
||||
|
||||
<span className="text-xs text-muted-foreground ml-auto">
|
||||
{formatMessage({ id: 'terminalDashboard.page.title' })}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function IssueItem({
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'w-full text-left px-3 py-2 rounded-md transition-colors',
|
||||
'w-full text-left px-2.5 py-1.5 rounded-md transition-colors',
|
||||
'hover:bg-muted/60 focus:outline-none focus:ring-1 focus:ring-primary/30',
|
||||
isSelected && 'bg-primary/10 ring-1 ring-primary/30',
|
||||
isHighlighted && !isSelected && 'bg-accent/50'
|
||||
@@ -120,7 +120,7 @@ function IssueItem({
|
||||
{issue.context}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-1 flex items-center gap-2 text-[10px] text-muted-foreground pl-5">
|
||||
<div className="mt-0.5 flex items-center gap-2 text-[10px] text-muted-foreground pl-5">
|
||||
<span className="font-mono">{issue.id}</span>
|
||||
{issue.labels && issue.labels.length > 0 && (
|
||||
<>
|
||||
@@ -140,7 +140,7 @@ function IssueEmptyState() {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center text-muted-foreground p-4">
|
||||
<div className="text-center">
|
||||
<AlertCircle className="h-10 w-10 mx-auto mb-3 opacity-40" />
|
||||
<AlertCircle className="h-6 w-6 mx-auto mb-1.5 opacity-30" />
|
||||
<p className="text-sm">{formatMessage({ id: 'terminalDashboard.issuePanel.noIssues' })}</p>
|
||||
<p className="text-xs mt-1 opacity-70">
|
||||
{formatMessage({ id: 'terminalDashboard.issuePanel.noIssuesDesc' })}
|
||||
@@ -157,7 +157,7 @@ function IssueErrorState({ error }: { error: Error }) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center text-destructive p-4">
|
||||
<div className="text-center">
|
||||
<AlertTriangle className="h-10 w-10 mx-auto mb-3 opacity-60" />
|
||||
<AlertTriangle className="h-6 w-6 mx-auto mb-1.5 opacity-30" />
|
||||
<p className="text-sm">{formatMessage({ id: 'terminalDashboard.issuePanel.error' })}</p>
|
||||
<p className="text-xs mt-1 opacity-70">{error.message}</p>
|
||||
</div>
|
||||
|
||||
@@ -164,7 +164,7 @@ function QueueErrorState({ error }: { error: Error }) {
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center text-destructive p-4">
|
||||
<div className="text-center">
|
||||
<AlertTriangle className="h-10 w-10 mx-auto mb-3 opacity-60" />
|
||||
<AlertTriangle className="h-6 w-6 mx-auto mb-1.5 opacity-30" />
|
||||
<p className="text-sm">{formatMessage({ id: 'terminalDashboard.queuePanel.error' })}</p>
|
||||
<p className="text-xs mt-1 opacity-70">{error.message}</p>
|
||||
</div>
|
||||
|
||||
@@ -96,8 +96,8 @@ export function SessionGroupTree() {
|
||||
{formatMessage({ id: 'terminalDashboard.sessionTree.createGroup' })}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col items-center justify-center gap-2 text-muted-foreground p-4">
|
||||
<Folder className="w-8 h-8 opacity-50" />
|
||||
<div className="flex-1 flex flex-col items-center justify-center gap-1.5 text-muted-foreground p-4">
|
||||
<Folder className="w-6 h-6 opacity-30" />
|
||||
<p className="text-xs text-center">
|
||||
{formatMessage({ id: 'terminalDashboard.sessionTree.noGroups' })}
|
||||
</p>
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
// Horizontal tab strip for terminal sessions in the Terminal Dashboard.
|
||||
// Renders tabs from sessionManagerStore groups with status indicators and alert badges.
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Terminal } from 'lucide-react';
|
||||
import { Terminal, AlertTriangle } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
useSessionManagerStore,
|
||||
@@ -35,6 +36,15 @@ export function TerminalTabBar() {
|
||||
// Flatten all sessionIds from all groups
|
||||
const allSessionIds = groups.flatMap((g) => g.sessionIds);
|
||||
|
||||
// Total alerts across all terminals
|
||||
const totalAlerts = useMemo(() => {
|
||||
let count = 0;
|
||||
for (const meta of Object.values(terminalMetas)) {
|
||||
count += meta.alertCount;
|
||||
}
|
||||
return count;
|
||||
}, [terminalMetas]);
|
||||
|
||||
if (allSessionIds.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 px-3 py-2 border-b border-border bg-muted/30 min-h-[40px]">
|
||||
@@ -88,6 +98,16 @@ export function TerminalTabBar() {
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Total alerts indicator at right end */}
|
||||
{totalAlerts > 0 && (
|
||||
<div className="ml-auto flex items-center gap-1 px-3 py-2 shrink-0 text-destructive">
|
||||
<AlertTriangle className="w-3.5 h-3.5" />
|
||||
<span className="text-[10px] font-semibold tabular-nums">
|
||||
{totalAlerts > 99 ? '99+' : totalAlerts}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,23 +2,139 @@
|
||||
// TerminalWorkbench Component
|
||||
// ========================================
|
||||
// Container for the right panel of the Terminal Dashboard.
|
||||
// Combines TerminalTabBar (tab switching) and TerminalInstance (xterm.js)
|
||||
// in a flex-col layout. MVP scope: single terminal view (1x1 grid).
|
||||
// Combines TerminalTabBar (tab switching) and TerminalInstance (xterm.js).
|
||||
// When no terminal is active, shows selected issue detail preview
|
||||
// or a compact empty state with action hints.
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Terminal } from 'lucide-react';
|
||||
import {
|
||||
Terminal,
|
||||
CircleDot,
|
||||
Tag,
|
||||
Clock,
|
||||
User,
|
||||
} from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
useSessionManagerStore,
|
||||
selectSessionManagerActiveTerminalId,
|
||||
} from '@/stores/sessionManagerStore';
|
||||
import {
|
||||
useIssueQueueIntegrationStore,
|
||||
selectSelectedIssueId,
|
||||
} from '@/stores/issueQueueIntegrationStore';
|
||||
import { useIssues } from '@/hooks/useIssues';
|
||||
import type { Issue } from '@/lib/api';
|
||||
import { TerminalTabBar } from './TerminalTabBar';
|
||||
import { TerminalInstance } from './TerminalInstance';
|
||||
|
||||
// ========== Priority Styles ==========
|
||||
|
||||
const PRIORITY_VARIANT: Record<Issue['priority'], 'destructive' | 'warning' | 'info' | 'secondary'> = {
|
||||
critical: 'destructive',
|
||||
high: 'warning',
|
||||
medium: 'info',
|
||||
low: 'secondary',
|
||||
};
|
||||
|
||||
const STATUS_COLORS: Record<Issue['status'], string> = {
|
||||
open: 'text-info',
|
||||
in_progress: 'text-warning',
|
||||
resolved: 'text-success',
|
||||
closed: 'text-muted-foreground',
|
||||
completed: 'text-success',
|
||||
};
|
||||
|
||||
// ========== Issue Detail Preview ==========
|
||||
|
||||
function IssueDetailPreview({ issue }: { issue: Issue }) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<div className="max-w-lg mx-auto space-y-4">
|
||||
{/* Header hint */}
|
||||
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">
|
||||
{formatMessage({ id: 'terminalDashboard.workbench.issuePreview' })}
|
||||
</p>
|
||||
|
||||
{/* Title + Status */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-start gap-2">
|
||||
<CircleDot className={cn('w-4 h-4 shrink-0 mt-0.5', STATUS_COLORS[issue.status] ?? 'text-muted-foreground')} />
|
||||
<h3 className="text-base font-semibold text-foreground leading-snug">
|
||||
{issue.title}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 pl-6">
|
||||
<Badge variant={PRIORITY_VARIANT[issue.priority]} className="text-[10px] px-1.5 py-0">
|
||||
{issue.priority}
|
||||
</Badge>
|
||||
<span className="text-[10px] text-muted-foreground font-mono">{issue.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Context / Description */}
|
||||
{issue.context && (
|
||||
<div className="rounded-md border border-border bg-muted/20 p-3">
|
||||
<p className="text-xs text-foreground/80 leading-relaxed whitespace-pre-wrap">
|
||||
{issue.context}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Metadata rows */}
|
||||
<div className="space-y-1.5 text-xs text-muted-foreground">
|
||||
{issue.labels && issue.labels.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Tag className="w-3.5 h-3.5 shrink-0" />
|
||||
<div className="flex items-center gap-1 flex-wrap">
|
||||
{issue.labels.map((label) => (
|
||||
<span key={label} className="px-1.5 py-0.5 rounded bg-muted text-[10px]">
|
||||
{label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{issue.assignee && (
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="w-3.5 h-3.5 shrink-0" />
|
||||
<span>{issue.assignee}</span>
|
||||
</div>
|
||||
)}
|
||||
{issue.createdAt && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-3.5 h-3.5 shrink-0" />
|
||||
<span>{new Date(issue.createdAt).toLocaleString()}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Hint */}
|
||||
<p className="text-[10px] text-muted-foreground/60 pt-2">
|
||||
{formatMessage({ id: 'terminalDashboard.workbench.issuePreviewHint' })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Component ==========
|
||||
|
||||
export function TerminalWorkbench() {
|
||||
const { formatMessage } = useIntl();
|
||||
const activeTerminalId = useSessionManagerStore(selectSessionManagerActiveTerminalId);
|
||||
const selectedIssueId = useIssueQueueIntegrationStore(selectSelectedIssueId);
|
||||
const { issues } = useIssues();
|
||||
|
||||
// Find selected issue for preview
|
||||
const selectedIssue = useMemo(() => {
|
||||
if (!selectedIssueId) return null;
|
||||
return issues.find((i) => i.id === selectedIssueId) ?? null;
|
||||
}, [selectedIssueId, issues]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
@@ -30,11 +146,14 @@ export function TerminalWorkbench() {
|
||||
<div className="flex-1 min-h-0">
|
||||
<TerminalInstance sessionId={activeTerminalId} />
|
||||
</div>
|
||||
) : selectedIssue ? (
|
||||
/* Issue detail preview when no terminal but issue is selected */
|
||||
<IssueDetailPreview issue={selectedIssue} />
|
||||
) : (
|
||||
/* Empty state when no terminal is selected */
|
||||
/* Compact empty state */
|
||||
<div className="flex-1 flex items-center justify-center text-muted-foreground">
|
||||
<div className="text-center">
|
||||
<Terminal className="h-10 w-10 mx-auto mb-3 opacity-50" />
|
||||
<Terminal className="h-6 w-6 mx-auto mb-1.5 opacity-30" />
|
||||
<p className="text-sm font-medium">
|
||||
{formatMessage({ id: 'terminalDashboard.workbench.noTerminal' })}
|
||||
</p>
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
"noSelection": "Select an item to view details",
|
||||
"associationChain": "Association Chain"
|
||||
},
|
||||
"bottomPanel": {
|
||||
"queueTab": "Queue",
|
||||
"inspectorTab": "Inspector",
|
||||
"collapse": "Collapse panel",
|
||||
"expand": "Expand panel"
|
||||
},
|
||||
"sessionTree": {
|
||||
"createGroup": "New Group",
|
||||
"groupNamePrompt": "Enter group name",
|
||||
@@ -67,7 +73,9 @@
|
||||
},
|
||||
"workbench": {
|
||||
"noTerminal": "No terminal selected",
|
||||
"noTerminalHint": "Select a session from the tab bar or create a new one"
|
||||
"noTerminalHint": "Select a session from the tab bar or create a new one",
|
||||
"issuePreview": "Issue Preview",
|
||||
"issuePreviewHint": "Select a terminal session or send this issue to the queue to begin work"
|
||||
},
|
||||
"placeholder": {
|
||||
"sessionTree": "Session groups will appear here",
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
"noSelection": "选择一个项目以查看详情",
|
||||
"associationChain": "关联链路"
|
||||
},
|
||||
"bottomPanel": {
|
||||
"queueTab": "队列",
|
||||
"inspectorTab": "检查器",
|
||||
"collapse": "折叠面板",
|
||||
"expand": "展开面板"
|
||||
},
|
||||
"sessionTree": {
|
||||
"createGroup": "新建分组",
|
||||
"groupNamePrompt": "输入分组名称",
|
||||
@@ -67,7 +73,9 @@
|
||||
},
|
||||
"workbench": {
|
||||
"noTerminal": "未选择终端",
|
||||
"noTerminalHint": "从标签栏选择会话或创建新会话"
|
||||
"noTerminalHint": "从标签栏选择会话或创建新会话",
|
||||
"issuePreview": "问题预览",
|
||||
"issuePreviewHint": "选择终端会话或将此问题发送到队列以开始工作"
|
||||
},
|
||||
"placeholder": {
|
||||
"sessionTree": "会话分组将在此显示",
|
||||
|
||||
@@ -44,6 +44,14 @@ export function TaskListTab({ session, onTaskClick }: TaskListTabProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const tasks = session.tasks || [];
|
||||
|
||||
// Detect if session tasks support status tracking (new format has explicit status/status_history in raw data)
|
||||
const hasStatusTracking = tasks.some((t) => {
|
||||
const raw = (t as unknown as Record<string, unknown>)._raw as Record<string, unknown> | undefined;
|
||||
const source = (raw?._raw as Record<string, unknown>) || raw;
|
||||
return source ? (source.status !== undefined || source.status_history !== undefined) : false;
|
||||
});
|
||||
|
||||
const completed = tasks.filter((t) => t.status === 'completed').length;
|
||||
const inProgress = tasks.filter((t) => t.status === 'in_progress').length;
|
||||
const pending = tasks.filter((t) => t.status === 'pending').length;
|
||||
@@ -165,18 +173,20 @@ export function TaskListTab({ session, onTaskClick }: TaskListTabProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Stats Bar with Bulk Actions */}
|
||||
<TaskStatsBar
|
||||
completed={completed}
|
||||
inProgress={inProgress}
|
||||
pending={pending}
|
||||
onMarkAllPending={handleMarkAllPending}
|
||||
onMarkAllInProgress={handleMarkAllInProgress}
|
||||
onMarkAllCompleted={handleMarkAllCompleted}
|
||||
isLoadingPending={isLoadingPending}
|
||||
isLoadingInProgress={isLoadingInProgress}
|
||||
isLoadingCompleted={isLoadingCompleted}
|
||||
/>
|
||||
{/* Stats Bar with Bulk Actions (only for tasks with status tracking) */}
|
||||
{hasStatusTracking && (
|
||||
<TaskStatsBar
|
||||
completed={completed}
|
||||
inProgress={inProgress}
|
||||
pending={pending}
|
||||
onMarkAllPending={handleMarkAllPending}
|
||||
onMarkAllInProgress={handleMarkAllInProgress}
|
||||
onMarkAllCompleted={handleMarkAllCompleted}
|
||||
isLoadingPending={isLoadingPending}
|
||||
isLoadingInProgress={isLoadingInProgress}
|
||||
isLoadingCompleted={isLoadingCompleted}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Tasks List */}
|
||||
{localTasks.length === 0 ? (
|
||||
@@ -236,12 +246,14 @@ export function TaskListTab({ session, onTaskClick }: TaskListTabProps) {
|
||||
|
||||
{/* Right: Status and Meta info */}
|
||||
<div className="flex flex-col items-end gap-2 flex-shrink-0">
|
||||
{/* Row 1: Status dropdown */}
|
||||
<TaskStatusDropdown
|
||||
currentStatus={task.status as TaskStatus}
|
||||
onStatusChange={(newStatus) => handleTaskStatusChange(task.task_id, newStatus)}
|
||||
size="sm"
|
||||
/>
|
||||
{/* Row 1: Status dropdown (only for tasks with status tracking) */}
|
||||
{hasStatusTracking && (
|
||||
<TaskStatusDropdown
|
||||
currentStatus={task.status as TaskStatus}
|
||||
onStatusChange={(newStatus) => handleTaskStatusChange(task.task_id, newStatus)}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Row 2: Meta info */}
|
||||
<div className="flex items-center gap-3 flex-wrap justify-end text-xs text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user