mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-27 09:13:07 +08:00
feat(team-tech-debt): add plan approval gate, worktree execution, PR merge, and parallel multi-perspective scanning
Pipeline enhancements: - Plan Approval Gate: user reviews remediation plan after TDPLAN (approve/revise/abort) - Worktree Execution: TDFIX and TDVAL run in isolated git worktree with dedicated branch - PR Merge: auto commit, push, and create PR via gh after validation passes - Parallel Fan-out: triple-layer scanning (subagent explore + CLI dimensions + multi-perspective Gemini) - Multi-perspective Gemini: auto-detect security/performance/quality/architecture angles - Fan-in aggregation: cross-reference dedup with severity boosting for multi-source findings
This commit is contained in:
@@ -155,6 +155,11 @@ const TEAM_CONFIG = {
|
||||
name: "tech-debt",
|
||||
sessionDir: ".workflow/.team/TD-{slug}-{date}/",
|
||||
sharedMemory: "shared-memory.json",
|
||||
worktree: {
|
||||
basePath: ".worktrees",
|
||||
branchPrefix: "tech-debt/TD-",
|
||||
autoCleanup: true // Remove worktree after PR creation
|
||||
},
|
||||
debtDimensions: ["code", "architecture", "testing", "dependency", "documentation"],
|
||||
priorityMatrix: {
|
||||
highImpact_lowCost: "立即修复 (Quick Win)",
|
||||
@@ -194,13 +199,44 @@ const TEAM_CONFIG = {
|
||||
|
||||
```
|
||||
Scan Mode (仅扫描评估):
|
||||
TDSCAN-001(多维度扫描) → TDEVAL-001(量化评估) → 报告
|
||||
TDSCAN-001(并行多维度扫描+多视角Gemini分析) → TDEVAL-001(量化评估) → 报告
|
||||
|
||||
Remediate Mode (完整闭环):
|
||||
TDSCAN-001(扫描) → TDEVAL-001(评估) → TDPLAN-001(规划) → TDFIX-001(修复) → TDVAL-001(验证)
|
||||
TDSCAN-001(并行扫描) → TDEVAL-001(评估) → TDPLAN-001(规划) → [Plan Approval] → [Create Worktree] → TDFIX-001(修复,worktree) → TDVAL-001(验证,worktree) → [Commit+PR] → 报告
|
||||
|
||||
Targeted Mode (定向修复):
|
||||
TDPLAN-001(规划) → TDFIX-001(修复) → TDVAL-001(验证)
|
||||
TDPLAN-001(规划) → [Plan Approval] → [Create Worktree] → TDFIX-001(修复,worktree) → TDVAL-001(验证,worktree) → [Commit+PR] → 报告
|
||||
```
|
||||
|
||||
### TDSCAN Parallel Fan-out Architecture
|
||||
|
||||
```
|
||||
TDSCAN-001 内部并行架构:
|
||||
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ Scanner Worker │
|
||||
│ │
|
||||
│ Fan-out A: Subagent Exploration (并行 cli-explore) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │structure │ │patterns │ │deps │ │
|
||||
│ │角度 │ │角度 │ │角度 │ │
|
||||
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
|
||||
│ └────────────┼────────────┘ │
|
||||
│ ↓ merge │
|
||||
│ Fan-out B: CLI Dimension Analysis (并行 gemini) │
|
||||
│ ┌──────┐┌────────┐┌───────┐┌──────┐┌─────┐ │
|
||||
│ │code ││arch ││testing││deps ││docs │ │
|
||||
│ └──┬───┘└───┬────┘└──┬────┘└──┬───┘└──┬──┘ │
|
||||
│ └────────┼────────┼────────┘ │ │
|
||||
│ ↓ merge │ │
|
||||
│ Fan-out C: Multi-Perspective Gemini (并行) │
|
||||
│ ┌────────┐┌──────────┐┌───────┐┌─────────┐ │
|
||||
│ │security││perform. ││quality││architect│ │
|
||||
│ └───┬────┘└────┬─────┘└──┬────┘└────┬────┘ │
|
||||
│ └──────────┼─────────┘ │ │
|
||||
│ ↓ Fan-in aggregate │
|
||||
│ debt-inventory.json │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Mode Auto-Detection
|
||||
@@ -228,7 +264,7 @@ TDFIX → TDVAL → (if regression or quality drop) → TDFIX-fix → TDVAL-2
|
||||
```
|
||||
.workflow/.team/TD-{slug}-{YYYY-MM-DD}/
|
||||
├── team-session.json
|
||||
├── shared-memory.json # 债务清单 / 评估矩阵 / 治理方案 / 修复结果 / 验证结果
|
||||
├── shared-memory.json # 债务清单 / 评估矩阵 / 治理方案 / 修复结果 / 验证结果 / worktree 信息
|
||||
├── scan/ # Scanner output
|
||||
│ └── debt-inventory.json
|
||||
├── assessment/ # Assessor output
|
||||
@@ -239,6 +275,15 @@ TDFIX → TDVAL → (if regression or quality drop) → TDFIX-fix → TDVAL-2
|
||||
│ └── fix-log.json
|
||||
└── validation/ # Validator output
|
||||
└── validation-report.json
|
||||
|
||||
# shared-memory.json worktree 字段(TDFIX 前由 coordinator 写入):
|
||||
# {
|
||||
# ...
|
||||
# "worktree": {
|
||||
# "path": ".worktrees/TD-{slug}-{date}",
|
||||
# "branch": "tech-debt/TD-{slug}-{date}"
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
## Coordinator Spawn Template
|
||||
|
||||
@@ -51,6 +51,7 @@ const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||
|
||||
let fixVerifyIteration = 0
|
||||
const MAX_FIX_VERIFY_ITERATIONS = 3
|
||||
let worktreeCreated = false
|
||||
|
||||
// 获取 pipeline 阶段列表(按创建顺序 = 依赖顺序)
|
||||
const allTasks = TaskList()
|
||||
@@ -113,7 +114,90 @@ for (const stageTask of pipelineTasks) {
|
||||
})
|
||||
}
|
||||
|
||||
// 5. 阶段间质量检查(仅 TDVAL 阶段)
|
||||
// 5. Plan Approval Gate(TDPLAN 完成后,进入 TDFIX 前)
|
||||
if (stagePrefix === 'TDPLAN' && taskState.status === 'completed') {
|
||||
// 读取治理方案
|
||||
let planContent = ''
|
||||
try { planContent = Read(`${sessionFolder}/plan/remediation-plan.md`) } catch {}
|
||||
if (!planContent) {
|
||||
try { planContent = JSON.stringify(JSON.parse(Read(`${sessionFolder}/plan/remediation-plan.json`)), null, 2) } catch {}
|
||||
}
|
||||
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "plan_approval",
|
||||
summary: `[coordinator] 治理方案已生成,等待审批`
|
||||
})
|
||||
|
||||
if (!autoYes) {
|
||||
// 输出方案摘要供用户审阅
|
||||
// 注意: 方案内容通过 AskUserQuestion 的描述呈现
|
||||
const approval = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `治理方案已生成,请审阅后决定:\n\n${planContent ? planContent.slice(0, 2000) : '(方案文件未找到,请查看 ' + sessionFolder + '/plan/)'}${planContent && planContent.length > 2000 ? '\n\n... (已截断,完整方案见 ' + sessionFolder + '/plan/)' : ''}`,
|
||||
header: "Plan Review",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "批准执行", description: "按此方案创建 worktree 并执行修复" },
|
||||
{ label: "修订方案", description: "重新规划(重新 spawn planner)" },
|
||||
{ label: "终止", description: "停止流水线,不执行修复" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
const planDecision = approval["Plan Review"]
|
||||
if (planDecision === "修订方案") {
|
||||
// 重新创建 TDPLAN 任务并 spawn planner
|
||||
const revisedTask = TaskCreate({
|
||||
subject: `TDPLAN-revised: 修订治理方案`,
|
||||
description: `session: ${sessionFolder}\n需求: ${taskDescription}\n用户要求修订方案`,
|
||||
activeForm: "Revising remediation plan"
|
||||
})
|
||||
TaskUpdate({ taskId: revisedTask.id, owner: 'planner', status: 'pending' })
|
||||
// 将修订任务插入到当前位置之后重新执行
|
||||
pipelineTasks.splice(pipelineTasks.indexOf(stageTask) + 1, 0, {
|
||||
id: revisedTask.id,
|
||||
subject: `TDPLAN-revised`,
|
||||
description: revisedTask.description
|
||||
})
|
||||
continue // 跳到下一阶段(即刚插入的修订任务)
|
||||
} else if (planDecision === "终止") {
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "shutdown",
|
||||
summary: `[coordinator] 用户终止流水线(方案审批阶段)`
|
||||
})
|
||||
break // 退出 pipeline 循环
|
||||
}
|
||||
// "批准执行" → 继续
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Worktree Creation(TDFIX 之前,方案已批准)
|
||||
if (stagePrefix === 'TDFIX' && !worktreeCreated) {
|
||||
const branchName = `tech-debt/TD-${sessionSlug}-${sessionDate}`
|
||||
const worktreePath = `.worktrees/TD-${sessionSlug}-${sessionDate}`
|
||||
|
||||
// 创建 worktree 和新分支
|
||||
Bash(`git worktree add -b "${branchName}" "${worktreePath}"`)
|
||||
|
||||
// 安装依赖(如有 package.json)
|
||||
Bash(`cd "${worktreePath}" && npm install --ignore-scripts 2>/dev/null || true`)
|
||||
|
||||
// 存入 shared memory
|
||||
sharedMemory.worktree = { path: worktreePath, branch: branchName }
|
||||
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||
|
||||
worktreeCreated = true
|
||||
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "worktree_created",
|
||||
summary: `[coordinator] Worktree 已创建: ${worktreePath} (branch: ${branchName})`
|
||||
})
|
||||
}
|
||||
|
||||
// 7. 阶段间质量检查(仅 TDVAL 阶段)
|
||||
if (stagePrefix === 'TDVAL') {
|
||||
const needsFixVerify = evaluateValidationResult(sessionFolder)
|
||||
if (needsFixVerify && fixVerifyIteration < MAX_FIX_VERIFY_ITERATIONS) {
|
||||
@@ -130,6 +214,20 @@ for (const stageTask of pipelineTasks) {
|
||||
|
||||
```javascript
|
||||
function buildWorkerPrompt(stageTask, workerConfig, sessionFolder, taskDescription) {
|
||||
const stagePrefix = stageTask.subject.match(/^(TD\w+)-/)?.[1] || 'TD'
|
||||
|
||||
// Worktree 注入(TDFIX 和 TDVAL 阶段)
|
||||
let worktreeSection = ''
|
||||
if (sharedMemory.worktree && (stagePrefix === 'TDFIX' || stagePrefix === 'TDVAL')) {
|
||||
worktreeSection = `
|
||||
## Worktree(强制)
|
||||
- Worktree 路径: ${sharedMemory.worktree.path}
|
||||
- 分支: ${sharedMemory.worktree.branch}
|
||||
- **所有文件读取、修改、命令执行必须在 worktree 路径下进行**
|
||||
- 使用 \`cd "${sharedMemory.worktree.path}" && ...\` 前缀执行所有 Bash 命令
|
||||
- 禁止在主工作树中修改任何文件`
|
||||
}
|
||||
|
||||
return `你是 team "${teamName}" 的 ${workerConfig.role.toUpperCase()}。
|
||||
|
||||
## ⚠️ 首要指令(MUST)
|
||||
@@ -142,9 +240,9 @@ Skill(skill="team-tech-debt", args="${workerConfig.skillArgs}")
|
||||
- 任务: ${stageTask.subject}
|
||||
- 描述: ${stageTask.description || taskDescription}
|
||||
- Session: ${sessionFolder}
|
||||
|
||||
${worktreeSection}
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 ${stageTask.subject.match(/^(TD\w+)-/)?.[1] || 'TD'}-* 前缀的任务
|
||||
- 你只能处理 ${stagePrefix}-* 前缀的任务
|
||||
- 所有输出必须带 [${workerConfig.role}] 标识前缀
|
||||
- 仅与 coordinator 通信,不得直接联系其他 worker
|
||||
|
||||
@@ -238,13 +336,66 @@ function evaluateValidationResult(sessionFolder) {
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Result Processing
|
||||
### Step 3: Result Processing + PR Creation
|
||||
|
||||
```javascript
|
||||
// 汇总所有结果
|
||||
const finalSharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||
const allFinalTasks = TaskList()
|
||||
const workerTasks = allFinalTasks.filter(t => t.owner && t.owner !== 'coordinator')
|
||||
|
||||
// PR 创建(worktree 执行模式下,验证通过后)
|
||||
if (finalSharedMemory.worktree && finalSharedMemory.validation_results?.passed) {
|
||||
const { path: wtPath, branch } = finalSharedMemory.worktree
|
||||
|
||||
// Commit all changes in worktree
|
||||
Bash(`cd "${wtPath}" && git add -A && git commit -m "$(cat <<'EOF'
|
||||
tech-debt: ${taskDescription}
|
||||
|
||||
Automated tech debt cleanup via team-tech-debt pipeline.
|
||||
Mode: ${pipelineMode}
|
||||
Items fixed: ${finalSharedMemory.fix_results?.items_fixed || 0}
|
||||
Debt score: ${finalSharedMemory.debt_score_before} → ${finalSharedMemory.debt_score_after}
|
||||
EOF
|
||||
)"`)
|
||||
|
||||
// Push + Create PR
|
||||
Bash(`cd "${wtPath}" && git push -u origin "${branch}"`)
|
||||
|
||||
const prTitle = `Tech Debt: ${taskDescription.slice(0, 50)}`
|
||||
Bash(`cd "${wtPath}" && gh pr create --title "${prTitle}" --body "$(cat <<'EOF'
|
||||
## Tech Debt Cleanup
|
||||
|
||||
**Mode**: ${pipelineMode}
|
||||
**Items fixed**: ${finalSharedMemory.fix_results?.items_fixed || 0}
|
||||
**Debt score**: ${finalSharedMemory.debt_score_before} → ${finalSharedMemory.debt_score_after}
|
||||
|
||||
### Validation
|
||||
- Tests: ${finalSharedMemory.validation_results?.checks?.test_suite?.status || 'N/A'}
|
||||
- Types: ${finalSharedMemory.validation_results?.checks?.type_check?.status || 'N/A'}
|
||||
- Lint: ${finalSharedMemory.validation_results?.checks?.lint_check?.status || 'N/A'}
|
||||
|
||||
### Session
|
||||
${sessionFolder}
|
||||
EOF
|
||||
)"`)
|
||||
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "pr_created",
|
||||
summary: `[coordinator] PR 已创建: branch ${branch}`
|
||||
})
|
||||
|
||||
// Cleanup worktree
|
||||
Bash(`git worktree remove "${wtPath}" 2>/dev/null || true`)
|
||||
} else if (finalSharedMemory.worktree && !finalSharedMemory.validation_results?.passed) {
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "quality_gate",
|
||||
summary: `[coordinator] 验证未通过,worktree 保留于 ${finalSharedMemory.worktree.path},请手动检查`
|
||||
})
|
||||
}
|
||||
|
||||
const summary = {
|
||||
total_tasks: workerTasks.length,
|
||||
completed_tasks: workerTasks.filter(t => t.status === 'completed').length,
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
| Type | Direction | Trigger | Description |
|
||||
|------|-----------|---------|-------------|
|
||||
| `mode_selected` | coordinator → all | 模式确定 | scan/remediate/targeted |
|
||||
| `plan_approval` | coordinator → user | TDPLAN 完成 | 呈现治理方案供审批(批准/修订/终止) |
|
||||
| `worktree_created` | coordinator → user | TDFIX 前 | Worktree 和分支已创建 |
|
||||
| `pr_created` | coordinator → user | TDVAL 通过 | PR 已创建,worktree 已清理 |
|
||||
| `quality_gate` | coordinator → user | 质量评估 | 通过/不通过/有条件通过 |
|
||||
| `task_unblocked` | coordinator → worker | 依赖解除 | 任务可执行 |
|
||||
| `error` | coordinator → user | 协调错误 | 阻塞性问题 |
|
||||
@@ -190,9 +193,9 @@ Read("commands/monitor.md")
|
||||
|----------|--------|--------|
|
||||
| TDSCAN-001 | scanner | → 启动 TDEVAL |
|
||||
| TDEVAL-001 | assessor | → 启动 TDPLAN |
|
||||
| TDPLAN-001 | planner | → 启动 TDFIX |
|
||||
| TDFIX-001 | executor | → 启动 TDVAL |
|
||||
| TDVAL-001 | validator | → 评估质量门控 |
|
||||
| TDPLAN-001 | planner | → [Plan Approval Gate] → [Create Worktree] → 启动 TDFIX |
|
||||
| TDFIX-001 | executor (worktree) | → 启动 TDVAL |
|
||||
| TDVAL-001 | validator (worktree) | → 评估质量门控 → [Commit+PR] |
|
||||
|
||||
**Fix-Verify 循环**(TDVAL 阶段发现回归时):
|
||||
```javascript
|
||||
@@ -209,12 +212,64 @@ if (regressionFound && fixVerifyIteration < 3) {
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Report + Debt Reduction Metrics
|
||||
### Phase 5: Report + Debt Reduction Metrics + PR
|
||||
|
||||
```javascript
|
||||
// 读取 shared memory 汇总结果
|
||||
const memory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||
|
||||
// PR 创建(worktree 执行模式下,验证通过后)
|
||||
if (memory.worktree && memory.validation_results?.passed) {
|
||||
const { path: wtPath, branch } = memory.worktree
|
||||
|
||||
// Commit all changes in worktree
|
||||
Bash(`cd "${wtPath}" && git add -A && git commit -m "$(cat <<'EOF'
|
||||
tech-debt: ${taskDescription}
|
||||
|
||||
Automated tech debt cleanup via team-tech-debt pipeline.
|
||||
Mode: ${pipelineMode}
|
||||
EOF
|
||||
)"`)
|
||||
|
||||
// Push + Create PR
|
||||
Bash(`cd "${wtPath}" && git push -u origin "${branch}"`)
|
||||
|
||||
const prBody = `## Tech Debt Cleanup
|
||||
|
||||
**Mode**: ${pipelineMode}
|
||||
**Items fixed**: ${memory.fix_results?.items_fixed || 0}
|
||||
**Debt score**: ${memory.debt_score_before} → ${memory.debt_score_after}
|
||||
|
||||
### Validation
|
||||
- Tests: ${memory.validation_results?.checks?.test_suite?.status || 'N/A'}
|
||||
- Types: ${memory.validation_results?.checks?.type_check?.status || 'N/A'}
|
||||
- Lint: ${memory.validation_results?.checks?.lint_check?.status || 'N/A'}
|
||||
|
||||
### Session
|
||||
${sessionFolder}`
|
||||
|
||||
Bash(`cd "${wtPath}" && gh pr create --title "Tech Debt: ${taskDescription.slice(0, 50)}" --body "$(cat <<'EOF'
|
||||
${prBody}
|
||||
EOF
|
||||
)"`)
|
||||
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "pr_created",
|
||||
summary: `[coordinator] PR 已创建: branch ${branch}`
|
||||
})
|
||||
|
||||
// Cleanup worktree
|
||||
Bash(`git worktree remove "${wtPath}" 2>/dev/null || true`)
|
||||
} else if (memory.worktree && !memory.validation_results?.passed) {
|
||||
// 验证未通过,保留 worktree 供手动检查
|
||||
mcp__ccw-tools__team_msg({
|
||||
operation: "log", team: teamName, from: "coordinator",
|
||||
to: "user", type: "quality_gate",
|
||||
summary: `[coordinator] 验证未通过,worktree 保留于 ${memory.worktree.path},请手动检查`
|
||||
})
|
||||
}
|
||||
|
||||
const report = {
|
||||
mode: pipelineMode,
|
||||
debt_items_found: memory.debt_inventory?.length || 0,
|
||||
|
||||
@@ -49,6 +49,10 @@ function sortBatches(batches) {
|
||||
// 按类型分组并排序
|
||||
const sortedBatches = sortBatches(batches)
|
||||
|
||||
// Worktree 路径(从 shared memory 加载)
|
||||
const worktreePath = sharedMemory.worktree?.path || null
|
||||
const cmdPrefix = worktreePath ? `cd "${worktreePath}" && ` : ''
|
||||
|
||||
// 每批最大 items 数
|
||||
const MAX_ITEMS_PER_BATCH = 10
|
||||
|
||||
@@ -97,7 +101,7 @@ for (const [batchName, actions] of Object.entries(finalBatches)) {
|
||||
description: `Tech debt cleanup: ${batchName} (${actions.length} items)`,
|
||||
prompt: `## Goal
|
||||
${prompt}
|
||||
|
||||
${worktreePath ? `\n## Worktree(强制)\n- 工作目录: ${worktreePath}\n- **所有文件操作必须在 ${worktreePath} 下进行**\n- 读文件: Read("${worktreePath}/path/to/file")\n- Bash 命令: cd "${worktreePath}" && ...\n- 禁止修改主工作树\n` : ''}
|
||||
## Items to Fix
|
||||
${actions.map(a => `### ${a.debt_id}: ${a.action}
|
||||
- File: ${a.file || 'N/A'}
|
||||
@@ -123,9 +127,9 @@ ${fileList.map(f => `- ${f}`).join('\n')}`
|
||||
status: 'completed'
|
||||
}
|
||||
|
||||
// 检查文件是否被修改
|
||||
// 检查文件是否被修改(在 worktree 中执行)
|
||||
for (const file of fileList) {
|
||||
const modified = Bash(`git diff --name-only -- "${file}" 2>/dev/null`).trim()
|
||||
const modified = Bash(`${cmdPrefix}git diff --name-only -- "${file}" 2>/dev/null`).trim()
|
||||
if (modified) {
|
||||
fixResults.files_modified.push(file)
|
||||
}
|
||||
|
||||
@@ -102,6 +102,10 @@ const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim() ||
|
||||
let sharedMemory = {}
|
||||
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}
|
||||
|
||||
// 加载 worktree 路径(由 coordinator 写入 shared memory)
|
||||
const worktreePath = sharedMemory.worktree?.path || null
|
||||
const worktreeBranch = sharedMemory.worktree?.branch || null
|
||||
|
||||
// 加载治理方案
|
||||
let plan = {}
|
||||
try {
|
||||
@@ -137,7 +141,7 @@ function groupActionsByType(actions) {
|
||||
Read("commands/remediate.md")
|
||||
```
|
||||
|
||||
**核心策略**: 分批委派 code-developer 执行修复
|
||||
**核心策略**: 分批委派 code-developer 执行修复(在 worktree 中操作)
|
||||
|
||||
```javascript
|
||||
const fixResults = {
|
||||
@@ -157,7 +161,7 @@ for (const [batchType, actions] of Object.entries(batches)) {
|
||||
description: `Fix tech debt batch: ${batchType} (${actions.length} items)`,
|
||||
prompt: `## Goal
|
||||
Execute tech debt cleanup for ${batchType} items.
|
||||
|
||||
${worktreePath ? `\n## Worktree(强制)\n- 工作目录: ${worktreePath}\n- **所有文件读取和修改必须在 ${worktreePath} 下进行**\n- 读文件时使用 ${worktreePath}/path/to/file\n- Bash 命令使用 cd "${worktreePath}" && ... 前缀\n` : ''}
|
||||
## Actions
|
||||
${actions.map(a => `- [${a.debt_id}] ${a.action} (file: ${a.file})`).join('\n')}
|
||||
|
||||
@@ -183,12 +187,13 @@ ${actions.map(a => `- [${a.debt_id}] ${a.action} (file: ${a.file})`).join('\n')}
|
||||
### Phase 4: Self-Validation
|
||||
|
||||
```javascript
|
||||
// 基本语法检查
|
||||
const syntaxResult = Bash(`npx tsc --noEmit 2>&1 || python -m py_compile *.py 2>&1 || echo "skip"`)
|
||||
// 基本语法检查(在 worktree 中执行)
|
||||
const cmdPrefix = worktreePath ? `cd "${worktreePath}" && ` : ''
|
||||
const syntaxResult = Bash(`${cmdPrefix}npx tsc --noEmit 2>&1 || python -m py_compile *.py 2>&1 || echo "skip"`)
|
||||
const hasSyntaxErrors = /error/i.test(syntaxResult) && !/skip/.test(syntaxResult)
|
||||
|
||||
// 基本 lint 检查
|
||||
const lintResult = Bash(`npx eslint --no-error-on-unmatched-pattern src/ 2>&1 || echo "skip"`)
|
||||
const lintResult = Bash(`${cmdPrefix}npx eslint --no-error-on-unmatched-pattern src/ 2>&1 || echo "skip"`)
|
||||
const hasLintErrors = /error/i.test(lintResult) && !/skip/.test(lintResult)
|
||||
|
||||
// 更新修复统计
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Command: scan-debt
|
||||
|
||||
> 多维度 CLI Fan-out 技术债务扫描。从代码质量、架构、测试、依赖、文档 5 个维度并行分析代码,发现技术债务。
|
||||
> 三层并行 Fan-out 技术债务扫描。Subagent 结构探索 + CLI 维度分析 + 多视角 Gemini 深度分析,三层并行执行后 Fan-in 聚合。
|
||||
|
||||
## When to Use
|
||||
|
||||
- Phase 3 of Scanner
|
||||
- 需要对代码库进行多维度技术债务扫描
|
||||
- 复杂度为 Medium 或 High 时使用 CLI Fan-out
|
||||
- 复杂度为 Medium 或 High 时使用并行 Fan-out
|
||||
|
||||
**Trigger conditions**:
|
||||
- TDSCAN-* 任务进入 Phase 3
|
||||
@@ -17,10 +17,14 @@
|
||||
|
||||
### Delegation Mode
|
||||
|
||||
**Mode**: CLI Fan-out
|
||||
**Mode**: Triple Fan-out + Fan-in
|
||||
**Subagent**: `cli-explore-agent`(并行结构探索)
|
||||
**CLI Tool**: `gemini` (primary)
|
||||
**CLI Mode**: `analysis`
|
||||
**Parallel Dimensions**: 3-5(根据复杂度)
|
||||
**Parallel Layers**:
|
||||
- Fan-out A: 2-3 并行 subagent(结构探索)
|
||||
- Fan-out B: 3-5 并行 CLI(维度分析)
|
||||
- Fan-out C: 2-4 并行 CLI(多视角 Gemini)
|
||||
|
||||
### Decision Logic
|
||||
|
||||
@@ -30,13 +34,15 @@ if (complexity === 'Low') {
|
||||
// ACE 搜索 + Grep 内联分析(不使用 CLI)
|
||||
mode = 'inline'
|
||||
} else if (complexity === 'Medium') {
|
||||
// CLI Fan-out: 3 个核心维度
|
||||
mode = 'cli-fanout'
|
||||
// 双层 Fan-out: subagent 探索 + CLI 3 维度
|
||||
mode = 'dual-fanout'
|
||||
activeDimensions = ['code', 'testing', 'dependency']
|
||||
exploreAngles = ['structure', 'patterns']
|
||||
} else {
|
||||
// CLI Fan-out: 所有 5 个维度
|
||||
mode = 'cli-fanout'
|
||||
activeDimensions = dimensions
|
||||
// 三层 Fan-out: subagent 探索 + CLI 5 维度 + 多视角 Gemini
|
||||
mode = 'triple-fanout'
|
||||
activeDimensions = dimensions // all 5
|
||||
exploreAngles = ['structure', 'patterns', 'dependencies']
|
||||
}
|
||||
```
|
||||
|
||||
@@ -57,20 +63,84 @@ const changedFiles = Bash(`git diff --name-only HEAD~10 2>/dev/null || echo ""`)
|
||||
const fileContext = changedFiles.length > 0
|
||||
? changedFiles.map(f => `@${f}`).join(' ')
|
||||
: `@${scanScope}`
|
||||
|
||||
// 多视角检测(从 role.md Phase 2 传入)
|
||||
// perspectives = detectPerspectives(task.description)
|
||||
```
|
||||
|
||||
### Step 2: Execute Strategy
|
||||
|
||||
```javascript
|
||||
if (mode === 'inline') {
|
||||
// 快速内联扫描
|
||||
// 快速内联扫描(Low 复杂度)
|
||||
const aceResults = mcp__ace-tool__search_context({
|
||||
project_root_path: projectRoot,
|
||||
query: "code smells, TODO/FIXME, deprecated APIs, complex functions, dead code, missing tests, circular imports"
|
||||
})
|
||||
// 解析 ACE 结果并分类到维度
|
||||
} else {
|
||||
// CLI Fan-out: 每个维度一个 CLI 调用
|
||||
// === 三层并行 Fan-out ===
|
||||
// A、B、C 三层同时启动,互不依赖
|
||||
|
||||
// ─── Fan-out A: Subagent 并行探索(codebase 结构理解)───
|
||||
executeExploreAngles(exploreAngles)
|
||||
|
||||
// ─── Fan-out B: CLI 维度分析(并行 gemini)───
|
||||
executeDimensionAnalysis(activeDimensions)
|
||||
|
||||
// ─── Fan-out C: 多视角 Gemini 深度分析(并行)───
|
||||
if (mode === 'triple-fanout') {
|
||||
executePerspectiveAnalysis(perspectives)
|
||||
}
|
||||
|
||||
// 等待所有 Fan-out 完成(hook 回调通知)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2a: Fan-out A — Subagent Exploration
|
||||
|
||||
> 并行启动 cli-explore-agent 探索代码库结构,为后续分析提供上下文。
|
||||
> 每个角度独立执行,不互相依赖。
|
||||
|
||||
```javascript
|
||||
function executeExploreAngles(angles) {
|
||||
const explorePrompts = {
|
||||
'structure': `Explore the codebase structure and module organization.
|
||||
Focus on: directory layout, module boundaries, entry points, build configuration.
|
||||
Project root: ${projectRoot}
|
||||
Report: module map, key entry files, build system type, framework detection.`,
|
||||
|
||||
'patterns': `Explore coding patterns and conventions used in this codebase.
|
||||
Focus on: naming conventions, import patterns, error handling patterns, state management, design patterns.
|
||||
Project root: ${projectRoot}
|
||||
Report: dominant patterns, anti-patterns found, consistency assessment.`,
|
||||
|
||||
'dependencies': `Explore dependency graph and inter-module relationships.
|
||||
Focus on: import/require chains, circular dependencies, external dependency usage, shared utilities.
|
||||
Project root: ${projectRoot}
|
||||
Report: dependency hotspots, tightly-coupled modules, dependency depth analysis.`
|
||||
}
|
||||
|
||||
// 并行启动所有探索角度(每个 cli-explore-agent 独立执行)
|
||||
for (const angle of angles) {
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
description: `Explore: ${angle}`,
|
||||
prompt: explorePrompts[angle] || `Explore from ${angle} perspective. Project: ${projectRoot}`
|
||||
})
|
||||
}
|
||||
|
||||
// 所有 subagent 返回后,探索结果已可用
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2b: Fan-out B — CLI Dimension Analysis
|
||||
|
||||
> 每个维度独立的 gemini CLI 分析,全部并行启动。
|
||||
|
||||
```javascript
|
||||
function executeDimensionAnalysis(activeDimensions) {
|
||||
const dimensionPrompts = {
|
||||
'code': `PURPOSE: Identify code quality debt - complexity, duplication, code smells
|
||||
TASK: • Find functions with cyclomatic complexity > 10 • Detect code duplication (>20 lines) • Identify code smells (God class, long method, feature envy) • Find TODO/FIXME/HACK comments • Detect dead code and unused exports
|
||||
@@ -108,6 +178,7 @@ EXPECTED: Documentation debt with severity, file:line, type (missing/stale/incom
|
||||
CONSTRAINTS: Focus on public interfaces and critical paths`
|
||||
}
|
||||
|
||||
// 并行启动所有维度分析
|
||||
for (const dimension of activeDimensions) {
|
||||
const prompt = dimensionPrompts[dimension]
|
||||
if (!prompt) continue
|
||||
@@ -116,47 +187,153 @@ CONSTRAINTS: Focus on public interfaces and critical paths`
|
||||
run_in_background: true
|
||||
})
|
||||
}
|
||||
|
||||
// 等待所有 CLI 完成(hook 回调通知)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Result Processing
|
||||
### Step 2c: Fan-out C — Multi-Perspective Gemini Analysis
|
||||
|
||||
> 多视角深度分析,每个视角关注不同质量维度。
|
||||
> 视角由 `detectPerspectives()` 自动检测,或在 High 复杂度下全量启用。
|
||||
> 与 Fan-out B(维度分析)的区别:维度分析按"代码/测试/依赖"横切,视角分析按"安全/性能/质量/架构"纵切,交叉覆盖。
|
||||
|
||||
```javascript
|
||||
// 聚合所有维度的结果
|
||||
const allFindings = []
|
||||
function executePerspectiveAnalysis(perspectives) {
|
||||
const perspectivePrompts = {
|
||||
'security': `PURPOSE: Security-focused analysis of codebase to identify vulnerability debt
|
||||
TASK: • Find injection vulnerabilities (SQL, command, XSS, LDAP) • Check authentication/authorization weaknesses • Identify hardcoded secrets or credentials • Detect insecure data handling (sensitive data exposure) • Find missing input validation on trust boundaries • Check for outdated crypto or insecure hash functions
|
||||
MODE: analysis
|
||||
CONTEXT: ${fileContext}
|
||||
EXPECTED: Security findings with: severity (critical/high/medium/low), CWE/OWASP reference, file:line, remediation suggestion
|
||||
CONSTRAINTS: Focus on exploitable vulnerabilities, not theoretical risks`,
|
||||
|
||||
// 从 CLI 输出解析结果
|
||||
'performance': `PURPOSE: Performance-focused analysis to identify performance debt
|
||||
TASK: • Find N+1 query patterns in database calls • Detect unnecessary re-renders or recomputations • Identify missing caching opportunities • Find synchronous blocking in async contexts • Detect memory leak patterns (event listener accumulation, unclosed resources) • Check for unoptimized loops or O(n²) algorithms on large datasets
|
||||
MODE: analysis
|
||||
CONTEXT: ${fileContext}
|
||||
EXPECTED: Performance findings with: severity, impact estimate (latency/memory/CPU), file:line, optimization suggestion
|
||||
CONSTRAINTS: Focus on measurable impact, not micro-optimizations`,
|
||||
|
||||
'code-quality': `PURPOSE: Code quality deep analysis beyond surface-level linting
|
||||
TASK: • Identify functions violating single responsibility principle • Find overly complex conditional chains (>3 nesting levels) • Detect hidden temporal coupling between functions • Find magic numbers and unexplained constants • Identify error handling anti-patterns (empty catch, swallowed errors) • Detect feature envy (methods that access other classes more than their own)
|
||||
MODE: analysis
|
||||
CONTEXT: ${fileContext}
|
||||
EXPECTED: Quality findings with: severity, code smell category, file:line, refactoring suggestion with pattern name
|
||||
CONSTRAINTS: Focus on maintainability impact, skip style-only issues`,
|
||||
|
||||
'architecture': `PURPOSE: Architecture-level analysis of system design debt
|
||||
TASK: • Identify layering violations (skip-layer calls, reverse dependencies) • Find God modules/classes with >5 distinct responsibilities • Detect missing domain boundaries (business logic in UI/API layer) • Check for abstraction leaks (implementation details in interfaces) • Identify duplicated business logic across modules • Find tightly coupled modules that should be independent
|
||||
MODE: analysis
|
||||
CONTEXT: ${fileContext}
|
||||
EXPECTED: Architecture findings with: severity, affected modules, coupling metric, suggested restructuring
|
||||
CONSTRAINTS: Focus on structural issues affecting scalability and team autonomy`
|
||||
}
|
||||
|
||||
// 并行启动所有视角分析
|
||||
for (const perspective of perspectives) {
|
||||
const prompt = perspectivePrompts[perspective]
|
||||
if (!prompt) continue
|
||||
|
||||
Bash(`ccw cli -p "${prompt}" --tool gemini --mode analysis --rule analysis-review-architecture`, {
|
||||
run_in_background: true
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Fan-in Result Processing
|
||||
|
||||
> 三层 Fan-out 结果聚合:探索结果提供上下文,维度分析 + 视角分析交叉去重。
|
||||
|
||||
```javascript
|
||||
// ─── 3a: 聚合探索结果(来自 Fan-out A)───
|
||||
const exploreContext = {
|
||||
structure: exploreResults['structure'] || {},
|
||||
patterns: exploreResults['patterns'] || {},
|
||||
dependencies: exploreResults['dependencies'] || {}
|
||||
}
|
||||
|
||||
// ─── 3b: 聚合维度分析结果(来自 Fan-out B)───
|
||||
const dimensionFindings = []
|
||||
for (const dimension of activeDimensions) {
|
||||
const findings = parseCliOutput(cliResults[dimension])
|
||||
for (const finding of findings) {
|
||||
finding.dimension = dimension
|
||||
allFindings.push(finding)
|
||||
finding.source = 'dimension-analysis'
|
||||
dimensionFindings.push(finding)
|
||||
}
|
||||
}
|
||||
|
||||
// 去重:相同 file:line 的发现合并
|
||||
function deduplicateFindings(findings) {
|
||||
const seen = new Set()
|
||||
const unique = []
|
||||
for (const f of findings) {
|
||||
const key = `${f.file}:${f.line}:${f.dimension}`
|
||||
if (!seen.has(key)) {
|
||||
seen.add(key)
|
||||
unique.push(f)
|
||||
// ─── 3c: 聚合视角分析结果(来自 Fan-out C)───
|
||||
const perspectiveFindings = []
|
||||
if (mode === 'triple-fanout') {
|
||||
for (const perspective of perspectives) {
|
||||
const findings = parseCliOutput(cliResults[perspective])
|
||||
for (const finding of findings) {
|
||||
finding.perspective = perspective
|
||||
finding.source = 'perspective-analysis'
|
||||
// 映射视角到最近维度(用于统一归类)
|
||||
finding.dimension = finding.dimension || mapPerspectiveToDimension(perspective)
|
||||
perspectiveFindings.push(finding)
|
||||
}
|
||||
}
|
||||
return unique
|
||||
}
|
||||
|
||||
// ─── 3d: 合并 + 交叉去重 ───
|
||||
const allFindings = [...dimensionFindings, ...perspectiveFindings]
|
||||
|
||||
function deduplicateFindings(findings) {
|
||||
const seen = new Map() // key → finding (保留严重性更高的)
|
||||
for (const f of findings) {
|
||||
const key = `${f.file}:${f.line}`
|
||||
const existing = seen.get(key)
|
||||
if (!existing) {
|
||||
seen.set(key, f)
|
||||
} else {
|
||||
// 同一位置多角度发现 → 合并,提升严重性
|
||||
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 }
|
||||
if ((severityOrder[f.severity] || 3) < (severityOrder[existing.severity] || 3)) {
|
||||
existing.severity = f.severity
|
||||
}
|
||||
// 记录交叉引用(被多个视角/维度发现的条目更可信)
|
||||
existing.crossRefs = existing.crossRefs || []
|
||||
existing.crossRefs.push({ source: f.source, perspective: f.perspective, dimension: f.dimension })
|
||||
}
|
||||
}
|
||||
return [...seen.values()]
|
||||
}
|
||||
|
||||
// 视角 → 维度映射
|
||||
function mapPerspectiveToDimension(perspective) {
|
||||
const map = {
|
||||
'security': 'code',
|
||||
'performance': 'code',
|
||||
'code-quality': 'code',
|
||||
'architecture': 'architecture'
|
||||
}
|
||||
return map[perspective] || 'code'
|
||||
}
|
||||
|
||||
const deduped = deduplicateFindings(allFindings)
|
||||
|
||||
// 按严重性排序
|
||||
// ─── 3e: 按严重性排序(交叉引用的条目优先)───
|
||||
deduped.sort((a, b) => {
|
||||
// 被多角度发现的条目 → 优先级提升
|
||||
const aBoost = (a.crossRefs?.length || 0) > 0 ? -0.5 : 0
|
||||
const bBoost = (b.crossRefs?.length || 0) > 0 ? -0.5 : 0
|
||||
const order = { critical: 0, high: 1, medium: 2, low: 3 }
|
||||
return (order[a.severity] || 3) - (order[b.severity] || 3)
|
||||
return ((order[a.severity] || 3) + aBoost) - ((order[b.severity] || 3) + bBoost)
|
||||
})
|
||||
|
||||
// ─── 3f: 用探索上下文增强发现(可选)───
|
||||
// 利用 Fan-out A 的结构探索结果标注模块归属
|
||||
for (const finding of deduped) {
|
||||
if (finding.file && exploreContext.structure?.modules) {
|
||||
const module = exploreContext.structure.modules.find(m =>
|
||||
finding.file.startsWith(m.path)
|
||||
)
|
||||
if (module) finding.module = module.name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output Format
|
||||
@@ -164,12 +341,13 @@ deduped.sort((a, b) => {
|
||||
```
|
||||
## Debt Scan Results
|
||||
|
||||
### Dimensions Scanned: [list]
|
||||
### Scan Mode: [inline|dual-fanout|triple-fanout]
|
||||
### Complexity: [Low|Medium|High]
|
||||
### Perspectives: [security, performance, code-quality, architecture]
|
||||
|
||||
### Findings by Dimension
|
||||
#### Code Quality ([count])
|
||||
- [file:line] [severity] - [description]
|
||||
- [file:line] [severity] - [description] [crossRefs: N perspectives]
|
||||
|
||||
#### Architecture ([count])
|
||||
- [module] [severity] - [description]
|
||||
@@ -183,6 +361,16 @@ deduped.sort((a, b) => {
|
||||
#### Documentation ([count])
|
||||
- [file:line] [severity] - [description]
|
||||
|
||||
### Multi-Perspective Highlights
|
||||
#### Security Findings ([count])
|
||||
- [file:line] [severity] - [CWE-xxx] [description]
|
||||
|
||||
#### Performance Findings ([count])
|
||||
- [file:line] [severity] - [impact] [description]
|
||||
|
||||
### Cross-Referenced Items (多角度交叉验证)
|
||||
- [file:line] confirmed by [N] sources - [description]
|
||||
|
||||
### Total Debt Items: [count]
|
||||
```
|
||||
|
||||
@@ -192,7 +380,9 @@ deduped.sort((a, b) => {
|
||||
|----------|------------|
|
||||
| CLI tool unavailable | Fall back to ACE search + Grep inline analysis |
|
||||
| CLI returns empty for a dimension | Note incomplete dimension, continue others |
|
||||
| Too many findings (>100) | Prioritize critical/high, summarize medium/low |
|
||||
| Timeout on CLI call | Use partial results, note incomplete dimensions |
|
||||
| Subagent explore fails | Skip explore context, proceed with CLI analysis only |
|
||||
| Too many findings (>100) | Prioritize critical/high + cross-referenced, summarize rest |
|
||||
| Timeout on CLI call | Use partial results, note incomplete dimensions/perspectives |
|
||||
| Agent/CLI failure | Retry once, then fallback to inline execution |
|
||||
| Timeout (>5 min per dimension) | Report partial results, notify scanner |
|
||||
| Perspective analysis timeout | Use dimension-only results, note missing perspectives |
|
||||
| All Fan-out layers fail | Fall back to ACE inline scan (guaranteed minimum) |
|
||||
|
||||
@@ -69,13 +69,14 @@ Bash(`echo '${JSON.stringify({ from: "scanner", to: "coordinator", type: "scan_c
|
||||
|
||||
| Agent Type | Used By | Purpose |
|
||||
|------------|---------|---------|
|
||||
| `cli-explore-agent` | scan-debt.md | 代码库结构探索 |
|
||||
| `cli-explore-agent` | scan-debt.md | 并行代码库结构探索(多角度 fan-out) |
|
||||
|
||||
### CLI Capabilities
|
||||
|
||||
| CLI Tool | Mode | Used By | Purpose |
|
||||
|----------|------|---------|---------|
|
||||
| `gemini` | analysis | scan-debt.md | 多维度代码分析 |
|
||||
| `gemini` | analysis | scan-debt.md | 多维度代码分析(dimension fan-out) |
|
||||
| `gemini` | analysis | scan-debt.md | 多视角深度分析(perspective fan-out) |
|
||||
|
||||
## Execution (5-Phase)
|
||||
|
||||
@@ -116,6 +117,19 @@ const hasGoMod = Bash(`test -f go.mod && echo "yes" || echo "no"`).trim() === 'y
|
||||
// 5 个扫描维度
|
||||
const dimensions = ["code", "architecture", "testing", "dependency", "documentation"]
|
||||
|
||||
// 多视角 Gemini 分析(自动检测任务关键词)
|
||||
function detectPerspectives(desc) {
|
||||
const perspectives = []
|
||||
if (/security|auth|inject|xss|漏洞|安全/.test(desc)) perspectives.push("security")
|
||||
if (/performance|speed|optimize|memory|性能|优化/.test(desc)) perspectives.push("performance")
|
||||
if (/quality|clean|maintain|debt|质量|代码/.test(desc)) perspectives.push("code-quality")
|
||||
if (/architect|pattern|structure|架构|结构/.test(desc)) perspectives.push("architecture")
|
||||
// 默认至少 2 个视角
|
||||
if (perspectives.length === 0) perspectives.push("code-quality", "architecture")
|
||||
return perspectives
|
||||
}
|
||||
const perspectives = detectPerspectives(task.description)
|
||||
|
||||
// 评估复杂度
|
||||
function assessComplexity(desc) {
|
||||
let score = 0
|
||||
@@ -134,7 +148,7 @@ const complexity = assessComplexity(task.description)
|
||||
Read("commands/scan-debt.md")
|
||||
```
|
||||
|
||||
**核心策略**: 按维度并行执行 CLI 分析
|
||||
**核心策略**: 三层并行 Fan-out(subagent 探索 + CLI 维度分析 + 多视角 Gemini)
|
||||
|
||||
```javascript
|
||||
if (complexity === 'Low') {
|
||||
@@ -144,11 +158,11 @@ if (complexity === 'Low') {
|
||||
query: "code smells, TODO/FIXME, deprecated APIs, complex functions, missing tests"
|
||||
})
|
||||
} else {
|
||||
// CLI Fan-out: 每个维度一个 CLI 调用
|
||||
for (const dimension of dimensions) {
|
||||
Bash(`ccw cli -p "..." --tool gemini --mode analysis`, { run_in_background: true })
|
||||
}
|
||||
// 等待所有 CLI 完成
|
||||
// Fan-out A: 并行 subagent 探索(codebase 结构理解)
|
||||
// Fan-out B: 每个维度一个 CLI 调用(并行 gemini 分析)
|
||||
// Fan-out C: 多视角 Gemini 深度分析(并行 perspective 分析)
|
||||
// → Fan-in: 聚合 + 去重 + 交叉引用
|
||||
Read("commands/scan-debt.md")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -51,25 +51,29 @@ const modifiedFiles = fixLog.files_modified || []
|
||||
// 获取原始债务分数
|
||||
const debtScoreBefore = sharedMemory.debt_score_before || 0
|
||||
|
||||
// 检测可用的验证工具
|
||||
const hasNpm = Bash(`which npm 2>/dev/null && echo "yes" || echo "no"`).trim() === 'yes'
|
||||
const hasTsc = Bash(`which npx 2>/dev/null && npx tsc --version 2>/dev/null && echo "yes" || echo "no"`).includes('yes')
|
||||
const hasEslint = Bash(`npx eslint --version 2>/dev/null && echo "yes" || echo "no"`).includes('yes')
|
||||
const hasPytest = Bash(`which pytest 2>/dev/null && echo "yes" || echo "no"`).trim() === 'yes'
|
||||
// Worktree 路径(从 shared memory 加载)
|
||||
const worktreePath = sharedMemory.worktree?.path || null
|
||||
const cmdPrefix = worktreePath ? `cd "${worktreePath}" && ` : ''
|
||||
|
||||
// 检测可用的验证工具(在 worktree 中检测)
|
||||
const hasNpm = Bash(`${cmdPrefix}which npm 2>/dev/null && echo "yes" || echo "no"`).trim() === 'yes'
|
||||
const hasTsc = Bash(`${cmdPrefix}which npx 2>/dev/null && npx tsc --version 2>/dev/null && echo "yes" || echo "no"`).includes('yes')
|
||||
const hasEslint = Bash(`${cmdPrefix}npx eslint --version 2>/dev/null && echo "yes" || echo "no"`).includes('yes')
|
||||
const hasPytest = Bash(`${cmdPrefix}which pytest 2>/dev/null && echo "yes" || echo "no"`).trim() === 'yes'
|
||||
```
|
||||
|
||||
### Step 2: Execute Strategy
|
||||
|
||||
```javascript
|
||||
// === Check 1: Test Suite ===
|
||||
// === Check 1: Test Suite(worktree 中执行) ===
|
||||
let testOutput = ''
|
||||
let testsPassed = true
|
||||
let testRegressions = 0
|
||||
|
||||
if (hasNpm) {
|
||||
testOutput = Bash(`npm test 2>&1 || true`)
|
||||
testOutput = Bash(`${cmdPrefix}npm test 2>&1 || true`)
|
||||
} else if (hasPytest) {
|
||||
testOutput = Bash(`python -m pytest 2>&1 || true`)
|
||||
testOutput = Bash(`${cmdPrefix}python -m pytest 2>&1 || true`)
|
||||
} else {
|
||||
testOutput = 'no-test-runner'
|
||||
}
|
||||
@@ -79,17 +83,17 @@ if (testOutput !== 'no-test-runner') {
|
||||
testRegressions = testsPassed ? 0 : (testOutput.match(/(\d+) failed/)?.[1] || 1) * 1
|
||||
}
|
||||
|
||||
// === Check 2: Type Checking ===
|
||||
// === Check 2: Type Checking(worktree 中执行) ===
|
||||
let typeErrors = 0
|
||||
if (hasTsc) {
|
||||
const tscOutput = Bash(`npx tsc --noEmit 2>&1 || true`)
|
||||
const tscOutput = Bash(`${cmdPrefix}npx tsc --noEmit 2>&1 || true`)
|
||||
typeErrors = (tscOutput.match(/error TS/g) || []).length
|
||||
}
|
||||
|
||||
// === Check 3: Linting ===
|
||||
// === Check 3: Linting(worktree 中执行) ===
|
||||
let lintErrors = 0
|
||||
if (hasEslint && modifiedFiles.length > 0) {
|
||||
const lintOutput = Bash(`npx eslint --no-error-on-unmatched-pattern ${modifiedFiles.join(' ')} 2>&1 || true`)
|
||||
const lintOutput = Bash(`${cmdPrefix}npx eslint --no-error-on-unmatched-pattern ${modifiedFiles.join(' ')} 2>&1 || true`)
|
||||
lintErrors = (lintOutput.match(/(\d+) error/)?.[0]?.match(/\d+/)?.[0] || 0) * 1
|
||||
}
|
||||
|
||||
@@ -103,7 +107,7 @@ CONTEXT: ${modifiedFiles.map(f => `@${f}`).join(' ')}
|
||||
EXPECTED: Quality comparison with: metrics_before, metrics_after, improvement_score (0-100), new_issues_found
|
||||
CONSTRAINTS: Focus on the specific changes, not overall project quality`
|
||||
|
||||
Bash(`ccw cli -p "${prompt}" --tool gemini --mode analysis --rule analysis-review-code-quality`, {
|
||||
Bash(`ccw cli -p "${prompt}" --tool gemini --mode analysis --rule analysis-review-code-quality${worktreePath ? ' --cd "' + worktreePath + '"' : ''}`, {
|
||||
run_in_background: true
|
||||
})
|
||||
// 等待 CLI 完成,解析质量改善分数
|
||||
@@ -137,7 +141,7 @@ if (totalRegressions > 0 && totalRegressions <= 3) {
|
||||
description: `Fix ${totalRegressions} regressions from debt cleanup`,
|
||||
prompt: `## Goal
|
||||
Fix regressions introduced by tech debt cleanup.
|
||||
|
||||
${worktreePath ? `\n## Worktree(强制)\n- 工作目录: ${worktreePath}\n- **所有文件操作必须在 ${worktreePath} 下进行**\n- Bash 命令使用 cd "${worktreePath}" && ... 前缀\n` : ''}
|
||||
## Regressions
|
||||
${regressionDetails.join('\n')}
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim() ||
|
||||
let sharedMemory = {}
|
||||
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}
|
||||
|
||||
// 加载 worktree 路径(由 coordinator 写入 shared memory)
|
||||
const worktreePath = sharedMemory.worktree?.path || null
|
||||
const cmdPrefix = worktreePath ? `cd "${worktreePath}" && ` : ''
|
||||
|
||||
const debtInventory = sharedMemory.debt_inventory || []
|
||||
const fixResults = sharedMemory.fix_results || {}
|
||||
const debtScoreBefore = sharedMemory.debt_score_before || debtInventory.length
|
||||
@@ -121,7 +125,7 @@ const modifiedFiles = fixLog.files_modified || []
|
||||
Read("commands/verify.md")
|
||||
```
|
||||
|
||||
**核心策略**: 4 层验证
|
||||
**核心策略**: 4 层验证(所有命令在 worktree 中执行)
|
||||
|
||||
```javascript
|
||||
const validationResults = {
|
||||
@@ -131,24 +135,24 @@ const validationResults = {
|
||||
quality_analysis: { status: 'pending', improvement: 0 }
|
||||
}
|
||||
|
||||
// 1. 测试套件
|
||||
const testResult = Bash(`npm test 2>&1 || npx vitest run 2>&1 || python -m pytest 2>&1 || echo "no-tests"`)
|
||||
// 1. 测试套件(worktree 中执行)
|
||||
const testResult = Bash(`${cmdPrefix}npm test 2>&1 || ${cmdPrefix}npx vitest run 2>&1 || ${cmdPrefix}python -m pytest 2>&1 || echo "no-tests"`)
|
||||
const testsPassed = !/FAIL|error|failed/i.test(testResult) || /no-tests/.test(testResult)
|
||||
validationResults.test_suite = {
|
||||
status: testsPassed ? 'PASS' : 'FAIL',
|
||||
regressions: testsPassed ? 0 : (testResult.match(/(\d+) failed/)?.[1] || 1) * 1
|
||||
}
|
||||
|
||||
// 2. 类型检查
|
||||
const typeResult = Bash(`npx tsc --noEmit 2>&1 || echo "skip"`)
|
||||
// 2. 类型检查(worktree 中执行)
|
||||
const typeResult = Bash(`${cmdPrefix}npx tsc --noEmit 2>&1 || echo "skip"`)
|
||||
const typeErrors = (typeResult.match(/error TS/g) || []).length
|
||||
validationResults.type_check = {
|
||||
status: typeErrors === 0 || /skip/.test(typeResult) ? 'PASS' : 'FAIL',
|
||||
errors: typeErrors
|
||||
}
|
||||
|
||||
// 3. Lint 检查
|
||||
const lintResult = Bash(`npx eslint --no-error-on-unmatched-pattern ${modifiedFiles.join(' ')} 2>&1 || echo "skip"`)
|
||||
// 3. Lint 检查(worktree 中执行)
|
||||
const lintResult = Bash(`${cmdPrefix}npx eslint --no-error-on-unmatched-pattern ${modifiedFiles.join(' ')} 2>&1 || echo "skip"`)
|
||||
const lintErrors = (lintResult.match(/\d+ error/)?.[0]?.match(/\d+/)?.[0] || 0) * 1
|
||||
validationResults.lint_check = {
|
||||
status: lintErrors === 0 || /skip/.test(lintResult) ? 'PASS' : 'FAIL',
|
||||
|
||||
Reference in New Issue
Block a user