feat: add Terminal Dashboard components and state management

- Implemented DashboardToolbar for managing panel toggles and layout presets.
- Created FloatingPanel for a generic sliding panel interface.
- Developed TerminalGrid for rendering a recursive layout of terminal panes.
- Added TerminalPane to encapsulate individual terminal instances with toolbar actions.
- Introduced layout utilities for managing Allotment layout trees.
- Established Zustand store for terminal grid state management, supporting pane operations and layout resets.
This commit is contained in:
catlog22
2026-02-14 22:13:45 +08:00
parent 37d19ada75
commit 75558dc411
28 changed files with 3375 additions and 2598 deletions

View File

@@ -169,7 +169,7 @@ TaskCreate({ subject: "DISCUSS-005: 执行计划与MVP范围讨论", description
TaskUpdate({ taskId: discuss5Id, owner: "discussant", addBlockedBy: [draft4Id] })
// QUALITY-001: Readiness Check (blockedBy DISCUSS-005)
TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${specSessionFolder}\n输入: 全部文档\n输出: ${specSessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(25%) + 一致性(25%) + 可追溯性(25%) + 深度(25%)`, activeForm: "质量检查中" })
TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${specSessionFolder}\n输入: 全部文档\n输出: ${specSessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(20%) + 一致性(20%) + 可追溯性(20%) + 深度(20%) + 需求覆盖率(20%)`, activeForm: "质量检查中" })
TaskUpdate({ taskId: qualityId, owner: "reviewer", addBlockedBy: [discuss5Id] })
// DISCUSS-006: 最终签收 (blockedBy QUALITY-001)
@@ -217,7 +217,7 @@ Receive teammate messages and make dispatch decisions. **Before each decision: `
| Received Message | Action |
|-----------------|--------|
| Analyst: research_ready | Read discovery-context.json → team_msg log → TaskUpdate RESEARCH completed (auto-unblocks DISCUSS-001) |
| Analyst: research_ready | Read discovery-context.json → **用户确认检查点** team_msg log → TaskUpdate RESEARCH completed (auto-unblocks DISCUSS-001) |
| Discussant: discussion_ready | Read discussion.md → judge if revision needed → unblock next DRAFT task |
| Discussant: discussion_blocked | Intervene → AskUserQuestion for user decision → write decision to discussion record → manually unblock |
| Writer: draft_ready | Read document summary → team_msg log → TaskUpdate DRAFT completed (auto-unblocks next DISCUSS) |
@@ -242,6 +242,46 @@ Receive teammate messages and make dispatch decisions. **Before each decision: `
When DISCUSS-006 completes in full-lifecycle mode, PLAN-001 is auto-unblocked via the dependency chain.
#### Research Confirmation Checkpoint
When receiving `research_ready` from analyst, confirm extracted requirements with user before unblocking:
```javascript
if (msgType === 'research_ready') {
const discoveryContext = JSON.parse(Read(`${specSessionFolder}/discovery-context.json`))
const dimensions = discoveryContext.seed_analysis?.exploration_dimensions || []
const constraints = discoveryContext.seed_analysis?.constraints || []
const problemStatement = discoveryContext.seed_analysis?.problem_statement || ''
// Present extracted requirements for user confirmation
AskUserQuestion({
questions: [{
question: `研究阶段提取到以下需求,请确认是否完整:\n\n**问题定义**: ${problemStatement}\n**探索维度**: ${dimensions.join('、')}\n**约束条件**: ${constraints.join('、')}\n\n是否有遗漏?`,
header: "需求确认",
multiSelect: false,
options: [
{ label: "确认完整", description: "提取的需求已覆盖所有关键点,继续推进" },
{ label: "需要补充", description: "有遗漏的需求,我来补充" },
{ label: "需要重新研究", description: "提取方向有偏差,重新执行研究" }
]
}]
})
if (userChoice === '需要补充') {
// User provides additional requirements via free text
// Merge into discovery-context.json, then unblock DISCUSS-001
discoveryContext.seed_analysis.user_supplements = userInput
Write(`${specSessionFolder}/discovery-context.json`, JSON.stringify(discoveryContext, null, 2))
} else if (userChoice === '需要重新研究') {
// Reset RESEARCH-001 to pending, notify analyst
TaskUpdate({ taskId: researchId, status: 'pending' })
team_msg({ type: 'fix_required', summary: 'User requests re-research with revised scope' })
return // Do not unblock DISCUSS-001
}
// '确认完整' → proceed normally: TaskUpdate RESEARCH completed
}
```
#### Discussion Blocked Handling
```javascript

View File

@@ -51,17 +51,18 @@ Each discussion round analyzes from 4 perspectives:
| **Technical** | Feasibility, tech debt, performance, security, maintainability | Tech Lead |
| **Quality** | Completeness, testability, consistency, standards compliance | QA Lead |
| **Risk** | Risk identification, dependency analysis, assumption validation, failure modes | Risk Analyst |
| **Coverage** | Requirement completeness vs original intent, scope drift, gap detection | Requirements Analyst |
## Discussion Round Configuration
| Round | Artifact | Key Perspectives | Focus |
|-------|----------|-----------------|-------|
| DISCUSS-001 | discovery-context | product + risk | Scope confirmation, direction |
| DISCUSS-002 | product-brief | product + technical + quality | Positioning, feasibility |
| DISCUSS-003 | requirements | quality + product | Completeness, priority |
| DISCUSS-001 | discovery-context | product + risk + **coverage** | Scope confirmation, direction, initial coverage check |
| DISCUSS-002 | product-brief | product + technical + quality + **coverage** | Positioning, feasibility, requirement coverage |
| DISCUSS-003 | requirements | quality + product + **coverage** | Completeness, priority, gap detection |
| DISCUSS-004 | architecture | technical + risk | Tech choices, security |
| DISCUSS-005 | epics | product + technical + quality | MVP scope, estimation |
| DISCUSS-006 | readiness-report | all 4 perspectives | Final sign-off |
| DISCUSS-005 | epics | product + technical + quality + **coverage** | MVP scope, estimation, requirement tracing |
| DISCUSS-006 | readiness-report | all 5 perspectives | Final sign-off |
## Execution (5-Phase)
@@ -91,12 +92,12 @@ const roundMatch = task.subject.match(/DISCUSS-(\d+)/)
const roundNumber = roundMatch ? parseInt(roundMatch[1]) : 0
const roundConfig = {
1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk'], label: '范围讨论' },
2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality'], label: 'Brief评审' },
3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product'], label: '需求讨论' },
1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk', 'coverage'], label: '范围讨论' },
2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality', 'coverage'], label: 'Brief评审' },
3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product', 'coverage'], label: '需求讨论' },
4: { artifact: 'architecture/_index.md', type: 'md', outputFile: 'discuss-004-architecture.md', perspectives: ['technical', 'risk'], label: '架构讨论' },
5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality'], label: 'Epics讨论' },
6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk'], label: '最终签收' }
5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality', 'coverage'], label: 'Epics讨论' },
6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk', 'coverage'], label: '最终签收' }
}
const config = roundConfig[roundNumber]
@@ -112,8 +113,9 @@ Launch parallel CLI analyses for each required perspective:
- **Technical Perspective** (codex): Feasibility, complexity, architecture decisions, tech debt risks. Rate 1-5.
- **Quality Perspective** (claude): Completeness, testability, consistency, ambiguity detection. Rate 1-5.
- **Risk Perspective** (gemini): Risk identification, dependency analysis, assumption validation, failure modes. Rate risk level.
- **Coverage Perspective** (gemini): Compare current artifact against original requirements in discovery-context.json. Identify covered_requirements[], partial_requirements[], missing_requirements[], scope_creep[]. Rate coverage 1-5. **If missing_requirements is non-empty, flag as critical divergence.**
Each CLI call produces structured critique with: strengths[], weaknesses[], suggestions[], rating.
Each CLI call produces structured critique with: strengths[], weaknesses[], suggestions[], rating. Coverage perspective additionally outputs: covered_requirements[], missing_requirements[], scope_creep[].
### Phase 4: Consensus Synthesis
@@ -131,6 +133,17 @@ const synthesis = {
// Extract convergent themes (items mentioned positively by 2+ perspectives)
// Extract divergent views (items where perspectives conflict)
// Check coverage gaps from coverage perspective (if present)
const coverageResult = perspectiveResults.find(p => p.perspective === 'coverage')
if (coverageResult?.missing_requirements?.length > 0) {
synthesis.coverage_gaps = coverageResult.missing_requirements
synthesis.divergent_views.push({
topic: 'requirement_coverage_gap',
description: `${coverageResult.missing_requirements.length} requirements from discovery-context not covered: ${coverageResult.missing_requirements.join(', ')}`,
severity: 'high',
source: 'coverage'
})
}
// Check for unresolvable conflicts
const criticalDivergences = synthesis.divergent_views.filter(d => d.severity === 'high')
if (criticalDivergences.length > 0) synthesis.consensus_reached = false

View File

@@ -222,7 +222,7 @@ function verifyRequirements(plan, fileContents) {
```javascript
if (reviewMode === 'spec') {
const scores = { completeness: 0, consistency: 0, traceability: 0, depth: 0 }
const scores = { completeness: 0, consistency: 0, traceability: 0, depth: 0, requirementCoverage: 0 }
// Completeness (25%): all sections present with content
function scoreCompleteness(docs) {
@@ -297,19 +297,55 @@ if (reviewMode === 'spec') {
return { score: Math.max(0, score), issues }
}
// Requirement Coverage (20%): original requirements → document mapping
function scoreRequirementCoverage(docs) {
let score = 100
const issues = []
if (!docs.discoveryContext) {
return { score: 0, issues: ['discovery-context.json missing, cannot verify requirement coverage'] }
}
const context = typeof docs.discoveryContext === 'string' ? JSON.parse(docs.discoveryContext) : docs.discoveryContext
const dimensions = context.seed_analysis?.exploration_dimensions || []
const constraints = context.seed_analysis?.constraints || []
const userSupplements = context.seed_analysis?.user_supplements || ''
const allRequirements = [...dimensions, ...constraints]
if (userSupplements) allRequirements.push(userSupplements)
if (allRequirements.length === 0) {
return { score: 100, issues: [] } // No requirements to check
}
const allDocContent = [docs.productBrief, docs.requirementsIndex, docs.architectureIndex, docs.epicsIndex,
...docs.requirements, ...docs.adrs, ...docs.epics].filter(Boolean).join('\n').toLowerCase()
let covered = 0
for (const req of allRequirements) {
const keywords = req.toLowerCase().split(/[\s,;]+/).filter(w => w.length > 2)
const isCovered = keywords.some(kw => allDocContent.includes(kw))
if (isCovered) { covered++ }
else { issues.push(`Requirement not covered in documents: "${req}"`) }
}
score = Math.round((covered / allRequirements.length) * 100)
return { score, issues }
}
const completenessResult = scoreCompleteness(documents)
const consistencyResult = scoreConsistency(documents)
const traceabilityResult = scoreTraceability(documents)
const depthResult = scoreDepth(documents)
const coverageResult = scoreRequirementCoverage(documents)
scores.completeness = completenessResult.score
scores.consistency = consistencyResult.score
scores.traceability = traceabilityResult.score
scores.depth = depthResult.score
scores.requirementCoverage = coverageResult.score
const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth) / 4
const qualityGate = overallScore >= 80 ? 'PASS' : overallScore >= 60 ? 'REVIEW' : 'FAIL'
const allSpecIssues = [...completenessResult.issues, ...consistencyResult.issues, ...traceabilityResult.issues, ...depthResult.issues]
const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth + scores.requirementCoverage) / 5
const qualityGate = (overallScore >= 80 && scores.requirementCoverage >= 70) ? 'PASS' :
(overallScore < 60 || scores.requirementCoverage < 50) ? 'FAIL' : 'REVIEW'
const allSpecIssues = [...completenessResult.issues, ...consistencyResult.issues, ...traceabilityResult.issues, ...depthResult.issues, ...coverageResult.issues]
}
```
@@ -346,10 +382,11 @@ version: 1
## Quality Scores
| Dimension | Score | Weight |
|-----------|-------|--------|
| Completeness | ${scores.completeness}% | 25% |
| Consistency | ${scores.consistency}% | 25% |
| Traceability | ${scores.traceability}% | 25% |
| Depth | ${scores.depth}% | 25% |
| Completeness | ${scores.completeness}% | 20% |
| Consistency | ${scores.consistency}% | 20% |
| Traceability | ${scores.traceability}% | 20% |
| Depth | ${scores.depth}% | 20% |
| Requirement Coverage | ${scores.requirementCoverage}% | 20% |
| **Overall** | **${overallScore.toFixed(1)}%** | **100%** |
## Quality Gate: ${qualityGate}
@@ -448,7 +485,7 @@ if (reviewMode === 'spec') {
operation: "log", team: teamName,
from: "reviewer", to: "coordinator",
type: qualityGate === 'FAIL' ? "fix_required" : "quality_result",
summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth})`,
summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth}/覆盖率${scores.requirementCoverage})`,
data: { gate: qualityGate, score: overallScore, issues: allSpecIssues }
})
@@ -468,6 +505,7 @@ if (reviewMode === 'spec') {
| 一致性 | ${scores.consistency}% |
| 可追溯性 | ${scores.traceability}% |
| 深度 | ${scores.depth}% |
| 需求覆盖率 | ${scores.requirementCoverage}% |
### 问题列表 (${allSpecIssues.length})
${allSpecIssues.map(i => '- ' + i).join('\n') || '无问题'}