feat(v0.5): 实现完整的五阶段动态编排系统
## 第一阶段:核心 Gitea MCP 集成 - 新增 gitea-mcp-adapter.js 封装 Gitea MCP 工具调用 - 更新 gitea-orchestration.js 使用真实 MCP 调用 - 实现 Issue/PR/分支操作的完整适配层 - 添加错误处理和重试机制 ## 第二阶段:Comment 标记约定 - 新增 comment-markers.js 生成标准完成标记 - 新增 comment-parser.js 解析 Sub Agent 完成标记 - 实现轮询机制 waitForSubAgentCompletion - 支持 EXECUTE_DONE、REVIEW_DONE、TEST_DONE、VERIFICATION_PASSED 标记 ## 第三阶段:三向一致性检查 - 新增 consistency-checker.js 实现三向比对逻辑 - 新增 requirement-tracker.js 追踪需求映射 - 实现需求→设计、设计→编码、需求→编码检查 - 支持偏差检测和报告生成 ## 第四阶段:Linus 三问决策框架 - 新增 linus-triad.js 实现工程审慎决策 - 三问:现实问题、需要解决、方案可行 - 支持拒绝响应生成 - 集成 Agent 分析和关键词回退 ## 第五阶段:Superpowers 工作流集成 - 新增 superpowers-integration.js 封装五技能调用 - 新增 CLAUDE.md Main Agent 配置 - 实现 writing-plans、executing-plans、requesting-code-review、systematic-debugging、finishing-a-development-branch 总计:8 个新文件,1 个修改文件,约 2,982 行代码
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# Main Agent 配置
|
||||
|
||||
## 角色
|
||||
|
||||
你是 **Main Agent(任务编排者)**,负责与用户讨论需求、动态安排 Sub Agents、验收整合。
|
||||
|
||||
## 核心职责
|
||||
|
||||
### 1. 需求讨论
|
||||
- 使用 **deep-interview** skill 进行需求澄清
|
||||
- ASK → LISTEN → WRITE → DEEPEN → REPEAT
|
||||
- 将需求记录到 `requirements/` 目录
|
||||
|
||||
### 2. 工程决策
|
||||
- 使用 **Linus 三问** 进行审慎决策:
|
||||
1. 这是现实问题还是想象问题?
|
||||
2. 这个问题真的需要解决吗?
|
||||
3. 这个方案真的能解决问题吗?
|
||||
- 拒绝过度设计、伪需求、自嗨方案
|
||||
|
||||
### 3. 任务分析
|
||||
- 评估任务复杂度(简单/中等/复杂)
|
||||
- 确定需要的 Sub Agents(Execute/Review/Test)
|
||||
- 制定编排策略
|
||||
|
||||
### 4. 编排执行
|
||||
- 通过 **Agent 工具**动态安排 Sub Agents
|
||||
- 使用 **Gitea Issue** 作为协作中心
|
||||
- 通过 **Comment 标记**追踪进度
|
||||
- 根据 Sub Agent 完成标记编排下一阶段
|
||||
|
||||
### 5. 验收整合
|
||||
- 执行**三向一致性检查**(需求↔设计↔编码)
|
||||
- 发现偏差时进入偏差处理流程
|
||||
- 整合 Sub Agent 结果
|
||||
- 向用户汇报最终结果
|
||||
|
||||
## 严格限制
|
||||
|
||||
- ❌ **不亲自编写代码**
|
||||
- ❌ **不亲自执行具体实现**
|
||||
- ❌ **不进行具体的代码修改**
|
||||
- ✅ **只负责编排、协调、验收**
|
||||
|
||||
## 可用能力
|
||||
|
||||
### Superpowers 五技能体系
|
||||
- **writing-plans** - 编写实现计划
|
||||
- **executing-plans** - 执行计划
|
||||
- **requesting-code-review** - 请求代码审查
|
||||
- **systematic-debugging** - 系统化调试
|
||||
- **finishing-a-development-branch** - 完成收尾
|
||||
|
||||
### 内置工具
|
||||
- **Agent 工具** - 编排 Sub Agents
|
||||
- **Gitea MCP 工具** - Issue/PR/分支管理
|
||||
- **Comment 标记系统** - Sub Agent 完成标记
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 标准工作流
|
||||
|
||||
```
|
||||
用户需求
|
||||
↓
|
||||
Linus 三问过滤
|
||||
↓
|
||||
需求讨论(如需要)
|
||||
↓
|
||||
任务分析
|
||||
↓
|
||||
创建 Gitea Issue
|
||||
↓
|
||||
编排 Sub Agents
|
||||
↓
|
||||
等待 Sub Agent 完成标记
|
||||
↓
|
||||
三向一致性检查
|
||||
↓
|
||||
验收整合
|
||||
↓
|
||||
向用户汇报
|
||||
```
|
||||
|
||||
### Sub Agent 完成标记
|
||||
|
||||
| Sub Agent | 完成标记格式 |
|
||||
|-----------|-------------|
|
||||
| Execute | `@main-agent ✅ EXECUTE_DONE` |
|
||||
| Review | `@main-agent ✅ REVIEW_DONE verdict=approved` |
|
||||
| Test | `@main-agent ✅ TEST_DONE result=passed` |
|
||||
| Main | `@main-agent ✅ VERIFICATION_PASSED` |
|
||||
|
||||
### 编排策略
|
||||
|
||||
| 复杂度 | 策略 |
|
||||
|--------|------|
|
||||
| **简单** | Execute → 验收 |
|
||||
| **中等** | Execute → Review → 验收 |
|
||||
| **复杂** | 需求讨论 → 规划 → Execute → Review → Test → 验收 |
|
||||
| **调试** | 定位 → Execute → Test → 验收 |
|
||||
|
||||
## 偏差处理
|
||||
|
||||
发现偏差时:
|
||||
1. 发布 `CONSISTENCY_ISSUE` 标记
|
||||
2. 通知相关 Sub Agent
|
||||
3. 重新进入 Superpowers 工作流
|
||||
4. 重新验收
|
||||
|
||||
## Gitea 协作
|
||||
|
||||
### Issue 结构
|
||||
- 标题格式:`[sanguo_moziplus_v3] 功能描述`
|
||||
- 包含工作清单
|
||||
- 包含状态表
|
||||
- 包含完成标记约定
|
||||
|
||||
### Comment 约定
|
||||
- 进度更新:`@main-agent 📝 进度更新`
|
||||
- 完成标记:`@main-agent ✅ 阶段_DONE`
|
||||
- 偏差报告:`@main-agent ❌ CONSISTENCY_ISSUE`
|
||||
|
||||
## 参考文档
|
||||
|
||||
- 设计文档:`docs/design/07-design-v0.5-dynamic-orchestration-integrated.md`
|
||||
- Gitea 配置:`.claude/gitea-config.json`
|
||||
- 工作流脚本:`.claude/workflows/`
|
||||
@@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
import { generateGiteaRules, getRepoUrl, getBranchName, getCommitTemplate } from '../helpers/gitea-rules-generator.js'
|
||||
import { createGiteaAdapter } from '../helpers/gitea-mcp-adapter.js'
|
||||
import { generateProgressUpdate, generateExecuteMarker, generateReviewMarker, generateTestMarker, generateVerificationMarker } from '../helpers/comment-markers.js'
|
||||
import { findLatestMarker, MarkerType } from '../helpers/comment-parser.js'
|
||||
|
||||
export const meta = {
|
||||
name: 'gitea-orchestration',
|
||||
@@ -23,13 +26,17 @@ export default async function giteaOrchestration(task, options = {}) {
|
||||
const giteaRules = generateGiteaRules()
|
||||
const repoUrl = getRepoUrl()
|
||||
|
||||
// 初始化 Gitea 适配器
|
||||
const gitea = createGiteaAdapter()
|
||||
|
||||
log(`📦 仓库: ${repoUrl}`)
|
||||
log(`🔌 已连接 Gitea: ${gitea.getRepoInfo().baseUrl}`)
|
||||
|
||||
// ===== Phase 1: 分析 =====
|
||||
phase('分析')
|
||||
|
||||
// 创建 Issue 记录任务
|
||||
const issue = await createIssue(task, giteaRules)
|
||||
const issue = await createIssue(task, giteaRules, gitea)
|
||||
log(`✅ Issue 创建: ${issue.url}`)
|
||||
|
||||
// 分析任务
|
||||
@@ -249,12 +256,12 @@ ${giteaRules}
|
||||
if (allPassed) {
|
||||
// 合并所有 PR
|
||||
for (const execute of executeResults) {
|
||||
await mergePR(execute.prUrl)
|
||||
await mergePR(execute.prUrl, gitea)
|
||||
log(`✅ 已合并 PR: ${execute.prUrl}`)
|
||||
}
|
||||
|
||||
// 关闭 Issue
|
||||
await closeIssue(issue.id)
|
||||
await closeIssue(issue.number, gitea)
|
||||
log(`✅ 已关闭 Issue: ${issue.url}`)
|
||||
|
||||
return {
|
||||
@@ -280,16 +287,120 @@ ${giteaRules}
|
||||
/**
|
||||
* 辅助函数
|
||||
*/
|
||||
async function createIssue(task, rules) {
|
||||
// 实际调用 Gitea API 创建 Issue
|
||||
// 这里返回模拟数据
|
||||
return {
|
||||
id: '123',
|
||||
url: 'http://192.168.2.154:3000/sanguo/sanguo_moziplus_v3/issues/123',
|
||||
number: 123
|
||||
async function createIssue(task, rules, gitea) {
|
||||
const repoInfo = gitea.getRepoInfo()
|
||||
|
||||
// 生成 Issue 标题(包含项目标识)
|
||||
const title = `[${repoInfo.repo}] ${task.title || task}`
|
||||
|
||||
// 生成 Issue 正文
|
||||
const body = generateIssueBody(task, rules)
|
||||
|
||||
// 生成标签
|
||||
const labels = generateIssueLabels(task)
|
||||
|
||||
try {
|
||||
const issue = await gitea.withRetry(async () => {
|
||||
return await gitea.createIssue({
|
||||
title,
|
||||
body,
|
||||
labels
|
||||
})
|
||||
})
|
||||
|
||||
log(`✅ Issue 已创建: ${issue.url}`)
|
||||
return issue
|
||||
} catch (error) {
|
||||
log(`❌ 创建 Issue 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Issue 正文
|
||||
*/
|
||||
function generateIssueBody(task, rules) {
|
||||
return `
|
||||
## 项目信息
|
||||
- 项目: ${task.project || 'sanguo_moziplus_v3'}
|
||||
- 需求编号: ${task.id || 'D-PENDING'}
|
||||
- 复杂度: ${task.complexity || '待评估'}
|
||||
|
||||
## 需求描述
|
||||
${task.description || task}
|
||||
|
||||
## 执行清单
|
||||
|
||||
### 📋 Execute Sub Agent
|
||||
> **负责**: Execute Sub Agent
|
||||
> **状态**: 🔄 进行中
|
||||
|
||||
- [ ] 任务分析和规划
|
||||
- [ ] 代码实现
|
||||
- [ ] 单元测试编写
|
||||
- [ ] 本地验证
|
||||
|
||||
**完成时标记**: \`@main-agent ✅ EXECUTE_DONE\`
|
||||
|
||||
### 📋 Review Sub Agent
|
||||
> **负责**: Review Sub Agent
|
||||
> **状态**: ⏳ 等待中
|
||||
|
||||
- [ ] 代码质量检查
|
||||
- [ ] 安全性审查
|
||||
- [ ] 性能评估
|
||||
|
||||
**完成时标记**: \`@main-agent ✅ REVIEW_DONE verdict=approved\`
|
||||
|
||||
### 📋 Test Sub Agent
|
||||
> **负责**: Test Sub Agent
|
||||
> **状态**: ⏳ 等待中
|
||||
|
||||
- [ ] 测试用例编写
|
||||
- [ ] 测试执行
|
||||
- [ ] 覆盖率检查
|
||||
|
||||
**完成时标记**: \`@main-agent ✅ TEST_DONE result=passed\`
|
||||
|
||||
### 📋 Main Agent 验收
|
||||
> **负责**: Main Agent
|
||||
> **状态**: ⏳ 等待中
|
||||
|
||||
- [ ] 需求 → 设计 一致性
|
||||
- [ ] 设计 → 编码 一致性
|
||||
- [ ] 需求 → 编码 一致性
|
||||
|
||||
**完成时标记**: \`@main-agent ✅ VERIFICATION_PASSED\`
|
||||
|
||||
## 执行状态
|
||||
| 阶段 | 负责者 | 状态 | 更新时间 |
|
||||
|------|-------|------|---------|
|
||||
| Execute | Execute Sub Agent | 🔄 进行中 | - |
|
||||
| Review | Review Sub Agent | ⏳ 等待 | - |
|
||||
| Test | Test Sub Agent | ⏳ 等待 | - |
|
||||
| 验收 | Main Agent | ⏳ 等待 | - |
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Issue 标签
|
||||
*/
|
||||
function generateIssueLabels(task) {
|
||||
const labels = []
|
||||
|
||||
// 添加复杂度标签
|
||||
if (task.complexity) {
|
||||
labels.push(`complexity:${task.complexity}`)
|
||||
}
|
||||
|
||||
// 添加类型标签
|
||||
if (task.type) {
|
||||
labels.push(task.type)
|
||||
}
|
||||
|
||||
return labels
|
||||
}
|
||||
|
||||
async function analyzeTask(task) {
|
||||
return await agent({
|
||||
subagent_type: 'Plan',
|
||||
@@ -318,14 +429,116 @@ async function analyzeTask(task) {
|
||||
})
|
||||
}
|
||||
|
||||
async function mergePR(prUrl) {
|
||||
// 实际调用 Gitea API 合并 PR
|
||||
log(`合并 PR: ${prUrl}`)
|
||||
async function mergePR(prUrl, gitea) {
|
||||
try {
|
||||
// 从 PR URL 中提取 PR 编号
|
||||
const prMatch = prUrl.match(/\/pulls\/(\d+)/)
|
||||
if (!prMatch) {
|
||||
throw new Error(`无效的 PR URL: ${prUrl}`)
|
||||
}
|
||||
|
||||
async function closeIssue(issueId) {
|
||||
// 实际调用 Gitea API 关闭 Issue
|
||||
log(`关闭 Issue: ${issueId}`)
|
||||
const prNumber = parseInt(prMatch[1], 10)
|
||||
|
||||
await gitea.withRetry(async () => {
|
||||
return await gitea.mergePullRequest(prNumber, {
|
||||
deleteBranch: true,
|
||||
mergeStyle: 'merge'
|
||||
})
|
||||
})
|
||||
|
||||
log(`✅ PR 已合并: ${prUrl}`)
|
||||
} catch (error) {
|
||||
log(`❌ 合并 PR 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function closeIssue(issueId, gitea) {
|
||||
try {
|
||||
await gitea.withRetry(async () => {
|
||||
return await gitea.closeIssue(issueId)
|
||||
})
|
||||
|
||||
log(`✅ Issue 已关闭: #${issueId}`)
|
||||
} catch (error) {
|
||||
log(`❌ 关闭 Issue 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待 Sub Agent 完成并返回标记数据
|
||||
*
|
||||
* @param {Object} gitea - Gitea 适配器实例
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @param {string} markerType - 期望的标记类型
|
||||
* @param {Object} options - 选项
|
||||
* @returns {Promise<Object>} 标记数据
|
||||
*/
|
||||
async function waitForSubAgentCompletion(gitea, issueNumber, markerType, options = {}) {
|
||||
const {
|
||||
timeout = 30 * 60 * 1000, // 30 分钟超时
|
||||
interval = 10 * 1000, // 10 秒检查间隔
|
||||
onProgress = null // 进度回调
|
||||
} = options
|
||||
|
||||
const startTime = Date.now()
|
||||
let checkCount = 0
|
||||
|
||||
log(`⏳ 等待 ${markerType} 标记...`)
|
||||
|
||||
while (Date.now() - startTime < timeout) {
|
||||
checkCount++
|
||||
|
||||
try {
|
||||
// 读取 Issue Comments
|
||||
const comments = await gitea.getIssueComments(issueNumber)
|
||||
|
||||
// 查找目标标记
|
||||
const marker = findLatestMarker(comments, markerType)
|
||||
|
||||
if (marker) {
|
||||
log(`✅ 检测到 ${markerType} 标记 (检查 ${checkCount} 次)`)
|
||||
return marker.data
|
||||
}
|
||||
|
||||
// 进度回调
|
||||
if (onProgress) {
|
||||
onProgress({
|
||||
elapsed: Date.now() - startTime,
|
||||
checkCount,
|
||||
markerType: null
|
||||
})
|
||||
}
|
||||
|
||||
// 等待下次检查
|
||||
await new Promise(resolve => setTimeout(resolve, interval))
|
||||
} catch (error) {
|
||||
log(`⚠️ 检查标记时出错: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`等待 ${markerType} 标记超时 (${timeout / 1000} 秒)`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布进度更新 Comment
|
||||
*
|
||||
* @param {Object} gitea - Gitea 适配器实例
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @param {string} stage - 当前阶段
|
||||
* @param {Array<string>} completed - 已完成任务
|
||||
* @param {Array<string>} inProgress - 进行中任务
|
||||
*/
|
||||
async function publishProgressUpdate(gitea, issueNumber, stage, completed, inProgress) {
|
||||
const content = generateProgressUpdate({
|
||||
stage,
|
||||
completed,
|
||||
inProgress
|
||||
})
|
||||
|
||||
await gitea.createIssueComment(issueNumber, content)
|
||||
log(`📝 进度更新已发布: ${stage} 阶段`)
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Comment 标记生成器
|
||||
*
|
||||
* 根据 v0.5 设计文档中的 Comment 标记约定,生成标准的完成标记
|
||||
*/
|
||||
|
||||
/**
|
||||
* 生成 Execute Sub Agent 完成标记
|
||||
*
|
||||
* @param {Object} data - 完成数据
|
||||
* @param {Array<string>} data.deliverables - 交付物列表
|
||||
* @param {Object} data.testStatus - 测试状态
|
||||
* @param {string} data.executor - 执行者名称
|
||||
* @returns {string} 完成标记内容
|
||||
*/
|
||||
export function generateExecuteMarker(data) {
|
||||
const {
|
||||
deliverables = [],
|
||||
testStatus = { local: 'PASS', coverage: '0%' },
|
||||
executor = 'Execute Sub Agent',
|
||||
notes = ''
|
||||
} = data
|
||||
|
||||
const timestamp = new Date().toISOString()
|
||||
|
||||
return `@main-agent ✅ **EXECUTE_DONE**
|
||||
|
||||
## 完成总结
|
||||
|
||||
### 交付物
|
||||
${deliverables.map((item, i) => `${i + 1}. \`${item}\``).join('\n')}
|
||||
|
||||
### 测试状态
|
||||
- 本地运行: ${testStatus.local ? '✅ ' + testStatus.local : '❌ FAIL'}
|
||||
${testStatus.coverage ? `- 覆盖率: ${testStatus.coverage}` : ''}
|
||||
|
||||
${notes ? `### 备注\n${notes}\n` : ''}
|
||||
|
||||
---
|
||||
**执行者**: ${executor}
|
||||
**完成时间**: ${timestamp}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Review Sub Agent 完成标记
|
||||
*
|
||||
* @param {Object} data - 审查数据
|
||||
* @param {string} data.verdict - 审查结论 (approved/rejected)
|
||||
* @param {Array<string>} data.findings - 发现的问题
|
||||
* @param {Array<string>} data.suggestions - 改进建议
|
||||
* @param {string} data.reviewer - 审查者名称
|
||||
* @returns {string} 完成标记内容
|
||||
*/
|
||||
export function generateReviewMarker(data) {
|
||||
const {
|
||||
verdict = 'approved',
|
||||
findings = [],
|
||||
suggestions = [],
|
||||
reviewer = 'Review Sub Agent',
|
||||
notes = ''
|
||||
} = data
|
||||
|
||||
const timestamp = new Date().toISOString()
|
||||
const verdictIcon = verdict === 'approved' ? '✅' : '❌'
|
||||
const verdictText = verdict.toUpperCase()
|
||||
|
||||
return `@main-agent ${verdictIcon} **REVIEW_DONE verdict=${verdictText}**
|
||||
|
||||
## 审查结果
|
||||
|
||||
### 检查项
|
||||
- 代码质量: ${verdict === 'approved' ? '✅' : '❌'}
|
||||
- 安全性: ${verdict === 'approved' ? '✅' : '❌'}
|
||||
- 性能: ${verdict === 'approved' ? '✅' : '❌'}
|
||||
- 测试覆盖: ${verdict === 'approved' ? '✅' : '❌'}
|
||||
|
||||
${findings.length > 0 ? `### 发现的问题
|
||||
${findings.map((f, i) => `${i + 1}. ${f}`).join('\n')}
|
||||
|
||||
` : ''}${suggestions.length > 0 ? `### 改进建议
|
||||
${suggestions.map((s, i) => `${i + 1}. ${s}`).join('\n')}
|
||||
|
||||
` : ''}${notes ? `### 备注\n${notes}\n` : ''}---
|
||||
**审查者**: ${reviewer}
|
||||
**结论**: ${verdictIcon} ${verdictText}
|
||||
**完成时间**: ${timestamp}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Test Sub Agent 完成标记
|
||||
*
|
||||
* @param {Object} data - 测试数据
|
||||
* @param {string} data.result - 测试结果 (passed/failed)
|
||||
* @param {string} data.coverage - 测试覆盖率
|
||||
* @param {Array<string>} data.failures - 失败的测试
|
||||
* @param {string} data.summary - 测试摘要
|
||||
* @param {string} data.tester - 测试者名称
|
||||
* @returns {string} 完成标记内容
|
||||
*/
|
||||
export function generateTestMarker(data) {
|
||||
const {
|
||||
result = 'passed',
|
||||
coverage = '0%',
|
||||
failures = [],
|
||||
summary = '',
|
||||
tester = 'Test Sub Agent',
|
||||
notes = ''
|
||||
} = data
|
||||
|
||||
const timestamp = new Date().toISOString()
|
||||
const resultIcon = result === 'passed' ? '✅' : '❌'
|
||||
const resultText = result.toUpperCase()
|
||||
|
||||
return `@main-agent ${resultIcon} **TEST_DONE result=${resultText} coverage=${coverage}**
|
||||
|
||||
## 测试结果
|
||||
|
||||
### 测试摘要
|
||||
${summary || '无'}
|
||||
|
||||
${failures.length > 0 ? `### 失败的测试
|
||||
${failures.map((f, i) => `${i + 1}. ${f}`).join('\n')}
|
||||
|
||||
` : ''}${notes ? `### 备注\n${notes}\n` : ''}---
|
||||
**测试者**: ${tester}
|
||||
**覆盖率**: ${coverage}
|
||||
**完成时间**: ${timestamp}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Main Agent 验收完成标记
|
||||
*
|
||||
* @param {Object} data - 验收数据
|
||||
* @param {Object} data.requirementDesign - 需求→设计检查结果
|
||||
* @param {Object} data.designCode - 设计→编码检查结果
|
||||
* @param {Object} data.requirementCode - 需求→编码检查结果
|
||||
* @param {string} data.verdict - 最终结论 (approved/rejected)
|
||||
* @returns {string} 完成标记内容
|
||||
*/
|
||||
export function generateVerificationMarker(data) {
|
||||
const {
|
||||
requirementDesign = { passed: true, items: [] },
|
||||
designCode = { passed: true, items: [] },
|
||||
requirementCode = { passed: true, items: [] },
|
||||
verdict = 'approved'
|
||||
} = data
|
||||
|
||||
const timestamp = new Date().toISOString()
|
||||
const verdictIcon = verdict === 'approved' ? '✅' : '❌'
|
||||
const verdictText = verdict.toUpperCase()
|
||||
|
||||
// 检查是否全部通过
|
||||
const allPassed = requirementDesign.passed && designCode.passed && requirementCode.passed
|
||||
const finalVerdict = allPassed ? 'approved' : 'rejected'
|
||||
|
||||
return `@main-agent ✅ **VERIFICATION_${finalVerdict.toUpperCase()}**
|
||||
|
||||
## 三向一致性检查
|
||||
|
||||
### 1️⃣ 需求 → 设计
|
||||
${renderCheckTable(requirementDesign.items)}
|
||||
${requirementDesign.passed ? '✅ **需求→设计: 通过**' : '❌ **需求→设计: 未通过**'}
|
||||
|
||||
### 2️⃣ 设计 → 编码
|
||||
${renderCheckTable(designCode.items)}
|
||||
${designCode.passed ? '✅ **设计→编码: 通过**' : '❌ **设计→编码: 未通过**'}
|
||||
|
||||
### 3️⃣ 需求 → 编码
|
||||
${renderCheckTable(requirementCode.items)}
|
||||
${requirementCode.passed ? '✅ **需求→编码: 通过**' : '❌ **需求→编码: 未通过**'}
|
||||
|
||||
## 最终结论
|
||||
${allPassed ? '✅ **三向一致性检查全部通过**' : '❌ **三向一致性检查未通过,存在偏差**'}
|
||||
**VERDICT**: ${verdictIcon} ${finalVerdict === 'approved' ? 'APPROVED FOR MERGE' : 'NEEDS FIX'}
|
||||
|
||||
---
|
||||
**执行者**: Main Agent
|
||||
**完成时间**: ${timestamp}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:渲染检查表格
|
||||
*/
|
||||
function renderCheckTable(items) {
|
||||
if (!items || items.length === 0) {
|
||||
return '无检查项'
|
||||
}
|
||||
|
||||
const header = '| 检查项 | 实际情况 | 状态 |\n|--------|----------|------|'
|
||||
const rows = items.map(item => {
|
||||
const status = item.passed ? '✅' : '❌'
|
||||
return `| ${item.check} | ${item.actual || '-'} | ${status} |`
|
||||
}).join('\n')
|
||||
|
||||
return `${header}\n${rows}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成进度更新标记
|
||||
*
|
||||
* @param {Object} data - 进度数据
|
||||
* @param {string} data.stage - 当前阶段
|
||||
* @param {Array<string>} data.completed - 已完成的任务
|
||||
* @param {Array<string>} data.inProgress - 进行中的任务
|
||||
* @returns {string} 进度更新内容
|
||||
*/
|
||||
export function generateProgressUpdate(data) {
|
||||
const {
|
||||
stage = 'Execute',
|
||||
completed = [],
|
||||
inProgress = []
|
||||
} = data
|
||||
|
||||
const total = completed.length + inProgress.length
|
||||
const progress = total > 0 ? Math.round((completed.length / total) * 100) : 0
|
||||
|
||||
return `@main-agent 📝 **进度更新: ${stage} 阶段**
|
||||
|
||||
## 已完成
|
||||
${completed.map((item, i) => `- ✅ ${item}`).join('\n') || '- 无'}
|
||||
|
||||
## 进行中
|
||||
${inProgress.map((item, i) => `- 🔄 ${item}`).join('\n') || '- 无'}
|
||||
|
||||
---
|
||||
**进度**: ${progress}% (${completed.length}/${total} 完成)`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成偏差报告标记
|
||||
*
|
||||
* @param {Object} data - 偏差数据
|
||||
* @param {string} data.type - 偏差类型 (requirement-design/design-code/requirement-code)
|
||||
* @param {string} data.issue - 偏差描述
|
||||
* @param {string} data.expected - 预期情况
|
||||
* @param {string} data.actual - 实际情况
|
||||
* @returns {string} 偏差报告内容
|
||||
*/
|
||||
export function generateDeviationMarker(data) {
|
||||
const {
|
||||
type = 'unknown',
|
||||
issue = '',
|
||||
expected = '',
|
||||
actual = '',
|
||||
actionRequired = []
|
||||
} = data
|
||||
|
||||
const typeLabels = {
|
||||
'requirement-design': '需求 → 设计 偏差',
|
||||
'design-code': '设计 → 编码 偏差',
|
||||
'requirement-code': '需求 → 编码 偏差'
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString()
|
||||
|
||||
return `@main-agent ❌ **CONSISTENCY_ISSUE**
|
||||
|
||||
## 发现偏差
|
||||
|
||||
### 问题: ${typeLabels[type] || type}
|
||||
**问题描述**: ${issue}
|
||||
|
||||
${expected ? `**预期**: ${expected}\n` : ''}${actual ? `**实际**: ${actual}\n` : ''}${actionRequired.length > 0 ? `### 处理要求
|
||||
${actionRequired.map((a, i) => `${i + 1}. ${a}`).join('\n')}
|
||||
|
||||
` : ''}---
|
||||
**标签**: needs-consistency-fix 🔴
|
||||
**报告时间**: ${timestamp}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出所有标记生成函数
|
||||
*/
|
||||
export default {
|
||||
generateExecuteMarker,
|
||||
generateReviewMarker,
|
||||
generateTestMarker,
|
||||
generateVerificationMarker,
|
||||
generateProgressUpdate,
|
||||
generateDeviationMarker
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* Comment 解析器
|
||||
*
|
||||
* 解析 Issue Comments 中的标记,提取关键信息
|
||||
*/
|
||||
|
||||
/**
|
||||
* 标记类型枚举
|
||||
*/
|
||||
export const MarkerType = {
|
||||
EXECUTE_DONE: 'EXECUTE_DONE',
|
||||
REVIEW_DONE: 'REVIEW_DONE',
|
||||
TEST_DONE: 'TEST_DONE',
|
||||
VERIFICATION_PASSED: 'VERIFICATION_PASSED',
|
||||
VERIFICATION_REJECTED: 'VERIFICATION_REJECTED',
|
||||
PROGRESS_UPDATE: 'PROGRESS_UPDATE',
|
||||
CONSISTENCY_ISSUE: 'CONSISTENCY_ISSUE'
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析完成标记
|
||||
*
|
||||
* @param {string} commentBody - Comment 内容
|
||||
* @returns {Object|null} 解析结果
|
||||
*/
|
||||
export function parseCompletionMarker(commentBody) {
|
||||
// 检测 EXECUTE_DONE
|
||||
if (commentBody.includes('EXECUTE_DONE')) {
|
||||
return {
|
||||
type: MarkerType.EXECUTE_DONE,
|
||||
status: 'completed',
|
||||
data: extractExecuteData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 REVIEW_DONE
|
||||
if (commentBody.includes('REVIEW_DONE')) {
|
||||
return {
|
||||
type: MarkerType.REVIEW_DONE,
|
||||
status: 'completed',
|
||||
data: extractReviewData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 TEST_DONE
|
||||
if (commentBody.includes('TEST_DONE')) {
|
||||
return {
|
||||
type: MarkerType.TEST_DONE,
|
||||
status: 'completed',
|
||||
data: extractTestData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 VERIFICATION_PASSED
|
||||
if (commentBody.includes('VERIFICATION_PASSED')) {
|
||||
return {
|
||||
type: MarkerType.VERIFICATION_PASSED,
|
||||
status: 'completed',
|
||||
data: extractVerificationData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 VERIFICATION_REJECTED
|
||||
if (commentBody.includes('VERIFICATION_REJECTED')) {
|
||||
return {
|
||||
type: MarkerType.VERIFICATION_REJECTED,
|
||||
status: 'failed',
|
||||
data: extractVerificationData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 PROGRESS_UPDATE
|
||||
if (commentBody.includes('进度更新')) {
|
||||
return {
|
||||
type: MarkerType.PROGRESS_UPDATE,
|
||||
status: 'in_progress',
|
||||
data: extractProgressData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测 CONSISTENCY_ISSUE
|
||||
if (commentBody.includes('CONSISTENCY_ISSUE')) {
|
||||
return {
|
||||
type: MarkerType.CONSISTENCY_ISSUE,
|
||||
status: 'failed',
|
||||
data: extractDeviationData(commentBody)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 EXECUTE_DONE 数据
|
||||
*/
|
||||
function extractExecuteData(commentBody) {
|
||||
const deliverables = []
|
||||
const deliverableMatch = commentBody.match(/### 交付物\n([\s\S]*?)\n\n/)
|
||||
if (deliverableMatch) {
|
||||
const lines = deliverableMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/\d+\.\s*`([^`]+)`/)
|
||||
if (match) {
|
||||
deliverables.push(match[1])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const testLocalMatch = commentBody.match(/本地运行:\s*([✅❌])\s*(\w+)/)
|
||||
const coverageMatch = commentBody.match(/覆盖率:\s*([\d%]+)/)
|
||||
const executorMatch = commentBody.match(/\*\*执行者\*\*:\s*([^\n]+)/)
|
||||
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
|
||||
|
||||
return {
|
||||
deliverables,
|
||||
testStatus: {
|
||||
local: testLocalMatch ? `${testLocalMatch[1]} ${testLocalMatch[2]}` : 'UNKNOWN',
|
||||
coverage: coverageMatch ? coverageMatch[1] : '0%'
|
||||
},
|
||||
executor: executorMatch ? executorMatch[1].trim() : 'Execute Sub Agent',
|
||||
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 REVIEW_DONE 数据
|
||||
*/
|
||||
function extractReviewData(commentBody) {
|
||||
const verdictMatch = commentBody.match(/verdict=(\w+)/)
|
||||
const verdict = verdictMatch ? verdictMatch[1].toLowerCase() : 'unknown'
|
||||
|
||||
const findings = []
|
||||
const findingsMatch = commentBody.match(/### 发现的问题\n([\s\S]*?)\n\n/)
|
||||
if (findingsMatch) {
|
||||
const lines = findingsMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/\d+\.\s*(.+)/)
|
||||
if (match) {
|
||||
findings.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const suggestions = []
|
||||
const suggestionsMatch = commentBody.match(/### 改进建议\n([\s\S]*?)\n\n/)
|
||||
if (suggestionsMatch) {
|
||||
const lines = suggestionsMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/\d+\.\s*(.+)/)
|
||||
if (match) {
|
||||
suggestions.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const reviewerMatch = commentBody.match(/\*\*审查者\*\*:\s*([^\n]+)/)
|
||||
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
|
||||
|
||||
return {
|
||||
verdict: verdict === 'approved' ? 'approved' : 'rejected',
|
||||
findings,
|
||||
suggestions,
|
||||
reviewer: reviewerMatch ? reviewerMatch[1].trim() : 'Review Sub Agent',
|
||||
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 TEST_DONE 数据
|
||||
*/
|
||||
function extractTestData(commentBody) {
|
||||
const resultMatch = commentBody.match(/result=(\w+)/)
|
||||
const coverageMatch = commentBody.match(/coverage=([\d%]+)/)
|
||||
const summaryMatch = commentBody.match(/### 测试摘要\n([\s\S]*?)\n\n/)
|
||||
|
||||
const failures = []
|
||||
const failuresMatch = commentBody.match(/### 失败的测试\n([\s\S]*?)\n\n/)
|
||||
if (failuresMatch) {
|
||||
const lines = failuresMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/\d+\.\s*(.+)/)
|
||||
if (match) {
|
||||
failures.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const testerMatch = commentBody.match(/\*\*测试者\*\*:\s*([^\n]+)/)
|
||||
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
|
||||
|
||||
return {
|
||||
result: resultMatch ? resultMatch[1].toLowerCase() : 'unknown',
|
||||
coverage: coverageMatch ? coverageMatch[1] : '0%',
|
||||
summary: summaryMatch ? summaryMatch[1].trim() : '',
|
||||
failures,
|
||||
tester: testerMatch ? testerMatch[1].trim() : 'Test Sub Agent',
|
||||
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 VERIFICATION 数据
|
||||
*/
|
||||
function extractVerificationData(commentBody) {
|
||||
// 提取三个检查结果
|
||||
const requirementDesignPassed = commentBody.includes('需求→设计: 通过')
|
||||
const designCodePassed = commentBody.includes('设计→编码: 通过')
|
||||
const requirementCodePassed = commentBody.includes('需求→编码: 通过')
|
||||
|
||||
const allPassed = requirementDesignPassed && designCodePassed && requirementCodePassed
|
||||
|
||||
const completedAtMatch = commentBody.match(/\*\*完成时间\*\*:\s*([^\n]+)/)
|
||||
|
||||
return {
|
||||
requirementDesign: { passed: requirementDesignPassed },
|
||||
designCode: { passed: designCodePassed },
|
||||
requirementCode: { passed: requirementCodePassed },
|
||||
allPassed,
|
||||
verdict: allPassed ? 'approved' : 'rejected',
|
||||
completedAt: completedAtMatch ? completedAtMatch[1].trim() : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 PROGRESS_UPDATE 数据
|
||||
*/
|
||||
function extractProgressData(commentBody) {
|
||||
const progressMatch = commentBody.match(/\*\*进度\*\*:\s*(\d+)%/)
|
||||
const stageMatch = commentBody.match(/进度更新:\s*(\w+)\s*阶段/)
|
||||
|
||||
const completed = []
|
||||
const completedMatch = commentBody.match(/## 已完成\n([\s\S]*?)\n\n/)
|
||||
if (completedMatch) {
|
||||
const lines = completedMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match/- ✅ (.+)/)
|
||||
if (match) {
|
||||
completed.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const inProgress = []
|
||||
const inProgressMatch = commentBody.match(/## 进行中\n([\s\S]*?)\n\n/)
|
||||
if (inProgressMatch) {
|
||||
const lines = inProgressMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match/- 🔄 (.+)/)
|
||||
if (match) {
|
||||
inProgress.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
stage: stageMatch ? stageMatch[1] : 'Unknown',
|
||||
progress: progressMatch ? parseInt(progressMatch[1], 10) : 0,
|
||||
completed,
|
||||
inProgress
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 CONSISTENCY_ISSUE 数据
|
||||
*/
|
||||
function extractDeviationData(commentBody) {
|
||||
const typeMatch = commentBody.match(/问题:\s*(.+)\s*偏差/)
|
||||
const issueMatch = commentBody.match(/\*\*问题描述\*\*:\s*([^\n]+)/)
|
||||
const expectedMatch = commentBody.match(/\*\*预期\*\*:\s*([^\n]+)/)
|
||||
const actualMatch = commentBody.match(/\*\*实际\*\*:\s*([^\n]+)/)
|
||||
|
||||
const actionRequired = []
|
||||
const actionMatch = commentBody.match(/### 处理要求\n([\s\S]*?)\n\n/)
|
||||
if (actionMatch) {
|
||||
const lines = actionMatch[1].split('\n')
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/\d+\.\s*(.+)/)
|
||||
if (match) {
|
||||
actionRequired.push(match[1].trim())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
type: typeMatch ? typeMatch[1].trim() : 'unknown',
|
||||
issue: issueMatch ? issueMatch[1].trim() : '',
|
||||
expected: expectedMatch ? expectedMatch[1].trim() : '',
|
||||
actual: actualMatch ? actualMatch[1].trim() : '',
|
||||
actionRequired
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找最新的指定类型标记
|
||||
*
|
||||
* @param {Array<Object>} comments - Comments 数组
|
||||
* @param {string} markerType - 标记类型
|
||||
* @returns {Object|null} 最新的标记数据
|
||||
*/
|
||||
export function findLatestMarker(comments, markerType) {
|
||||
if (!comments || comments.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 从后往前遍历(最新的在前)
|
||||
for (let i = comments.length - 1; i >= 0; i--) {
|
||||
const comment = comments[i]
|
||||
const parsed = parseCompletionMarker(comment.body || comment.content || '')
|
||||
|
||||
if (parsed && parsed.type === markerType) {
|
||||
return {
|
||||
...parsed,
|
||||
commentId: comment.id,
|
||||
createdAt: comment.created_at || comment.timestamp,
|
||||
author: comment.user?.login || comment.author || 'Unknown'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定标记是否存在
|
||||
*
|
||||
* @param {Array<Object>} comments - Comments 数组
|
||||
* @param {string} markerType - 标记类型
|
||||
* @returns {boolean} 是否存在
|
||||
*/
|
||||
export function hasMarker(comments, markerType) {
|
||||
return findLatestMarker(comments, markerType) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有标记的摘要
|
||||
*
|
||||
* @param {Array<Object>} comments - Comments 数组
|
||||
* @returns {Object} 标记摘要
|
||||
*/
|
||||
export function getMarkersSummary(comments) {
|
||||
const summary = {
|
||||
executeDone: findLatestMarker(comments, MarkerType.EXECUTE_DONE),
|
||||
reviewDone: findLatestMarker(comments, MarkerType.REVIEW_DONE),
|
||||
testDone: findLatestMarker(comments, MarkerType.TEST_DONE),
|
||||
verificationPassed: findLatestMarker(comments, MarkerType.VERIFICATION_PASSED),
|
||||
verificationRejected: findLatestMarker(comments, MarkerType.VERIFICATION_REJECTED),
|
||||
progressUpdate: findLatestMarker(comments, MarkerType.PROGRESS_UPDATE),
|
||||
consistencyIssue: findLatestMarker(comments, MarkerType.CONSISTENCY_ISSUE)
|
||||
}
|
||||
|
||||
// 计算当前阶段状态
|
||||
summary.currentPhase = determineCurrentPhase(summary)
|
||||
|
||||
return summary
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定当前阶段
|
||||
*/
|
||||
function determineCurrentPhase(summary) {
|
||||
if (summary.verificationPassed || summary.verificationRejected) {
|
||||
return 'completed'
|
||||
}
|
||||
|
||||
if (summary.reviewDone && summary.testDone) {
|
||||
return 'verification'
|
||||
}
|
||||
|
||||
if (summary.executeDone) {
|
||||
return 'review_and_test'
|
||||
}
|
||||
|
||||
if (summary.progressUpdate) {
|
||||
return 'executing'
|
||||
}
|
||||
|
||||
return 'pending'
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出所有解析函数
|
||||
*/
|
||||
export default {
|
||||
MarkerType,
|
||||
parseCompletionMarker,
|
||||
extractMarkerData: parseCompletionMarker,
|
||||
findLatestMarker,
|
||||
hasMarker,
|
||||
getMarkersSummary,
|
||||
determineCurrentPhase
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* 一致性检查模块
|
||||
*
|
||||
* 实现需求-设计-编码三向一致性检查逻辑
|
||||
*/
|
||||
|
||||
/**
|
||||
* 需求→设计一致性检查
|
||||
*
|
||||
* @param {Object} requirements - 需求列表
|
||||
* @param {Object} design - 设计文档
|
||||
* @returns {Object} 检查结果
|
||||
*/
|
||||
export function checkRequirementDesign(requirements, design) {
|
||||
const items = []
|
||||
let allPassed = true
|
||||
|
||||
// 如果 requirements 是数组,转换为对象
|
||||
const reqMap = Array.isArray(requirements)
|
||||
? requirements.reduce((acc, r, i) => {
|
||||
acc[`R${i + 1}`] = r
|
||||
return acc
|
||||
}, {})
|
||||
: requirements
|
||||
|
||||
// 遍历每个需求,检查设计是否覆盖
|
||||
for (const [reqId, requirement] of Object.entries(reqMap)) {
|
||||
const reqText = typeof requirement === 'string' ? requirement : requirement.description || requirement.text || JSON.stringify(requirement)
|
||||
const isCovered = checkDesignCoverage(reqText, design)
|
||||
|
||||
items.push({
|
||||
check: `需求 ${reqId}`,
|
||||
expected: reqText,
|
||||
actual: isCovered ? '已覆盖' : '未覆盖',
|
||||
passed: isCovered
|
||||
})
|
||||
|
||||
if (!isCovered) {
|
||||
allPassed = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'requirement-design',
|
||||
passed: allPassed,
|
||||
items,
|
||||
summary: allPassed ? '需求完全被设计覆盖' : '部分需求未被设计覆盖'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设计→编码一致性检查
|
||||
*
|
||||
* @param {Object} design - 设计文档
|
||||
* @param {Object} codeChanges - 代码变更
|
||||
* @returns {Object} 检查结果
|
||||
*/
|
||||
export function checkDesignCode(design, codeChanges) {
|
||||
const items = []
|
||||
let allPassed = true
|
||||
|
||||
// 提取设计中的关键组件/模块
|
||||
const designComponents = extractDesignComponents(design)
|
||||
|
||||
// 提取代码变更中的文件/模块
|
||||
const codeFiles = extractCodeFiles(codeChanges)
|
||||
|
||||
// 检查每个设计组件是否都有对应代码
|
||||
for (const component of designComponents) {
|
||||
const isImplemented = codeFiles.some(file =>
|
||||
file.name.includes(component.name) ||
|
||||
file.path.includes(component.path || component.name)
|
||||
)
|
||||
|
||||
items.push({
|
||||
check: `组件: ${component.name}`,
|
||||
expected: `设计: ${component.description || component.type || '组件'}`,
|
||||
actual: isImplemented ? `已实现: ${component.name}` : '未实现',
|
||||
passed: isImplemented
|
||||
})
|
||||
|
||||
if (!isImplemented) {
|
||||
allPassed = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'design-code',
|
||||
passed: allPassed,
|
||||
items,
|
||||
summary: allPassed ? '设计完全被编码实现' : '部分设计未在编码中实现'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 需求→编码一致性检查
|
||||
*
|
||||
* @param {Object} requirements - 需求列表
|
||||
* @param {Object} codeChanges - 代码变更
|
||||
* @returns {Object} 检查结果
|
||||
*/
|
||||
export function checkRequirementCode(requirements, codeChanges) {
|
||||
const items = []
|
||||
let allPassed = true
|
||||
|
||||
// 如果 requirements 是数组,转换为对象
|
||||
const reqMap = Array.isArray(requirements)
|
||||
? requirements.reduce((acc, r, i) => {
|
||||
acc[`R${i + 1}`] = r
|
||||
return acc
|
||||
}, {})
|
||||
: requirements
|
||||
|
||||
// 提取代码实现的功能点
|
||||
const codeFeatures = extractCodeFeatures(codeChanges)
|
||||
|
||||
// 检查每个需求是否在代码中实现
|
||||
for (const [reqId, requirement] of Object.entries(reqMap)) {
|
||||
const reqText = typeof requirement === 'string' ? requirement : requirement.description || requirement.text || JSON.stringify(requirement)
|
||||
const isImplemented = checkCodeImplementation(reqText, codeFeatures)
|
||||
|
||||
items.push({
|
||||
check: `需求 ${reqId}`,
|
||||
expected: reqText,
|
||||
actual: isImplemented ? '已实现' : '未实现',
|
||||
passed: isImplemented
|
||||
})
|
||||
|
||||
if (!isImplemented) {
|
||||
allPassed = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'requirement-code',
|
||||
passed: allPassed,
|
||||
items,
|
||||
summary: allPassed ? '需求完全在编码中实现' : '部分需求未在编码中实现'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整的三向一致性检查
|
||||
*
|
||||
* @param {Object} requirements - 需求列表
|
||||
* @param {Object} design - 设计文档
|
||||
* @param {Object} codeChanges - 代码变更
|
||||
* @returns {Object} 完整检查结果
|
||||
*/
|
||||
export function runThreeWayCheck(requirements, design, codeChanges) {
|
||||
const requirementDesign = checkRequirementDesign(requirements, design)
|
||||
const designCode = checkDesignCode(design, codeChanges)
|
||||
const requirementCode = checkRequirementCode(requirements, codeChanges)
|
||||
|
||||
const allPassed = requirementDesign.passed && designCode.passed && requirementCode.passed
|
||||
|
||||
return {
|
||||
allPassed,
|
||||
requirementDesign,
|
||||
designCode,
|
||||
requirementCode,
|
||||
summary: allPassed ? '三向一致性检查全部通过' : '三向一致性检查未通过',
|
||||
deviations: findDeviations(requirementDesign, designCode, requirementCode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找偏差
|
||||
*/
|
||||
function findDeviations(requirementDesign, designCode, requirementCode) {
|
||||
const deviations = []
|
||||
|
||||
// 需求→设计偏差
|
||||
if (!requirementDesign.passed) {
|
||||
deviations.push({
|
||||
type: 'requirement-design',
|
||||
issues: requirementDesign.items.filter(i => !i.passed)
|
||||
})
|
||||
}
|
||||
|
||||
// 设计→编码偏差
|
||||
if (!designCode.passed) {
|
||||
deviations.push({
|
||||
type: 'design-code',
|
||||
issues: designCode.items.filter(i => !i.passed)
|
||||
})
|
||||
}
|
||||
|
||||
// 需求→编码偏差
|
||||
if (!requirementCode.passed) {
|
||||
deviations.push({
|
||||
type: 'requirement-code',
|
||||
issues: requirementCode.items.filter(i => !i.passed)
|
||||
})
|
||||
}
|
||||
|
||||
return deviations
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:检查设计是否覆盖需求
|
||||
*/
|
||||
function checkDesignCoverage(requirementText, design) {
|
||||
const designText = typeof design === 'string' ? design : JSON.stringify(design)
|
||||
const reqKeywords = extractKeywords(requirementText)
|
||||
|
||||
// 至少有一个关键词在设计中被提及
|
||||
return reqKeywords.some(keyword =>
|
||||
designText.toLowerCase().includes(keyword.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:检查代码是否实现需求
|
||||
*/
|
||||
function checkCodeImplementation(requirementText, codeFeatures) {
|
||||
const reqKeywords = extractKeywords(requirementText)
|
||||
|
||||
// 至少有一个关键词在代码特性中被实现
|
||||
return reqKeywords.some(keyword =>
|
||||
codeFeatures.some(feature =>
|
||||
feature.toLowerCase().includes(keyword.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:提取关键词
|
||||
*/
|
||||
function extractKeywords(text) {
|
||||
// 简单的关键词提取:过滤常见停用词
|
||||
const stopWords = new Set(['的', '了', '是', '在', '和', '与', '或', '但', '如果', '那么', '这样', '那样', 'the', 'a', 'an', 'is', 'are', 'and', 'or', 'but', 'if', 'then', 'this', 'that'])
|
||||
|
||||
return text
|
||||
.toLowerCase()
|
||||
.split(/[\s,,。.!!??;;::]/)
|
||||
.filter(word => word.length > 1 && !stopWords.has(word))
|
||||
.slice(0, 5) // 取前5个关键词
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:从设计中提取组件
|
||||
*/
|
||||
function extractDesignComponents(design) {
|
||||
const components = []
|
||||
|
||||
// 如果 design 是字符串,尝试解析
|
||||
if (typeof design === 'string') {
|
||||
// 简单的提取逻辑:查找常见的组件模式
|
||||
const patterns = [
|
||||
/表:\s*(\w+)/g,
|
||||
/接口:\s*(\w+)/g,
|
||||
/模块:\s*(\w+)/g,
|
||||
/组件:\s*(\w+)/g,
|
||||
/文件:\s*([^\s]+)/g
|
||||
]
|
||||
|
||||
for (const pattern of patterns) {
|
||||
let match
|
||||
while ((match = pattern.exec(design)) !== null) {
|
||||
components.push({
|
||||
name: match[1],
|
||||
type: 'unknown',
|
||||
description: match[0]
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (typeof design === 'object' && design !== null) {
|
||||
// 如果 design 是对象,直接提取
|
||||
for (const [key, value] of Object.entries(design)) {
|
||||
if (value && typeof value === 'object' && value.name) {
|
||||
components.push(value)
|
||||
} else if (typeof value === 'string') {
|
||||
components.push({
|
||||
name: key,
|
||||
type: 'unknown',
|
||||
description: value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return components
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:从代码变更中提取文件
|
||||
*/
|
||||
function extractCodeFiles(codeChanges) {
|
||||
const files = []
|
||||
|
||||
if (typeof codeChanges === 'string') {
|
||||
// 简单的文件路径提取
|
||||
const lines = codeChanges.split('\n')
|
||||
for (const line of lines) {
|
||||
const match = line.match(/([a-zA-Z0-9_\/]+\.[a-zA-Z0-9]+)/)
|
||||
if (match) {
|
||||
files.push({
|
||||
name: match[1].split('/').pop(),
|
||||
path: match[1]
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(codeChanges)) {
|
||||
// 如果是数组,假设每个元素都是文件信息
|
||||
for (const change of codeChanges) {
|
||||
if (typeof change === 'string') {
|
||||
files.push({
|
||||
name: change.split('/').pop(),
|
||||
path: change
|
||||
})
|
||||
} else if (change.file || change.path || change.name) {
|
||||
files.push({
|
||||
name: change.file || change.name || change.path?.split('/').pop(),
|
||||
path: change.path || change.file
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (typeof codeChanges === 'object' && codeChanges !== null) {
|
||||
// 如果是对象,提取文件信息
|
||||
if (codeChanges.files) {
|
||||
return extractCodeFiles(codeChanges.files)
|
||||
}
|
||||
if (codeChanges.changes) {
|
||||
return extractCodeFiles(codeChanges.changes)
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:从代码中提取功能点
|
||||
*/
|
||||
function extractCodeFeatures(codeChanges) {
|
||||
const features = []
|
||||
|
||||
// 提取函数名、类名、变量名等作为功能特征
|
||||
const files = extractCodeFiles(codeChanges)
|
||||
|
||||
for (const file of files) {
|
||||
const name = file.name || file.path || ''
|
||||
// 从文件名推断功能
|
||||
if (name.includes('.')) {
|
||||
const baseName = name.substring(0, name.lastIndexOf('.'))
|
||||
features.push(baseName)
|
||||
}
|
||||
features.push(name)
|
||||
}
|
||||
|
||||
// 如果 codeChanges 包含 description 或 summary
|
||||
if (typeof codeChanges === 'object' && codeChanges !== null) {
|
||||
if (codeChanges.description) {
|
||||
features.push(codeChanges.description)
|
||||
}
|
||||
if (codeChanges.summary) {
|
||||
features.push(codeChanges.summary)
|
||||
}
|
||||
if (codeChanges.changes) {
|
||||
features.push(codeChanges.changes)
|
||||
}
|
||||
}
|
||||
|
||||
return features
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出所有检查函数
|
||||
*/
|
||||
export default {
|
||||
checkRequirementDesign,
|
||||
checkDesignCode,
|
||||
checkRequirementCode,
|
||||
runThreeWayCheck,
|
||||
findDeviations
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* Gitea MCP 适配层
|
||||
*
|
||||
* 封装 Gitea MCP 工具调用,提供统一的接口
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* 加载 Gitea 配置
|
||||
*/
|
||||
function loadConfig() {
|
||||
const configPath = path.join(process.cwd(), '.claude', 'gitea-config.json')
|
||||
try {
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||
return config
|
||||
} catch (error) {
|
||||
throw new Error(`无法加载配置文件: ${configPath}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库信息
|
||||
*/
|
||||
function getRepoInfo() {
|
||||
const config = loadConfig()
|
||||
return {
|
||||
baseUrl: config.gitea.baseUrl,
|
||||
owner: config.gitea.owner,
|
||||
repo: config.gitea.repo,
|
||||
defaultBranch: config.gitea.defaultBranch
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gitea MCP 适配器类
|
||||
*/
|
||||
export class GiteaMCPAdapter {
|
||||
constructor() {
|
||||
this.config = loadConfig()
|
||||
this.repoInfo = getRepoInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库信息
|
||||
*/
|
||||
getRepoInfo() {
|
||||
return this.repoInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Issue
|
||||
*
|
||||
* @param {Object} options - Issue 选项
|
||||
* @param {string} options.title - Issue 标题
|
||||
* @param {string} options.body - Issue 正文
|
||||
* @param {string[]} options.labels - Issue 标签
|
||||
* @returns {Promise<Object>} Issue 信息
|
||||
*/
|
||||
async createIssue(options) {
|
||||
const { title, body, labels = [] } = options
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
// 调用 Gitea MCP 工具创建 Issue
|
||||
const result = await mcp__gitea__issue_write({
|
||||
method: 'create',
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
labels
|
||||
})
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
number: result.number,
|
||||
url: `${this.repoInfo.baseUrl}/${owner}/${repo}/issues/${result.number}`,
|
||||
title: result.title,
|
||||
body: result.body
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`创建 Issue 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 Issue
|
||||
*
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @param {Object} options - 更新选项
|
||||
* @returns {Promise<Object>} 更新后的 Issue 信息
|
||||
*/
|
||||
async updateIssue(issueNumber, options) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
const { state, body } = options
|
||||
|
||||
try {
|
||||
const result = await mcp__gitea__issue_write({
|
||||
method: 'update',
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state,
|
||||
body
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
throw new Error(`更新 Issue 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭 Issue
|
||||
*
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @returns {Promise<Object>} 关闭后的 Issue 信息
|
||||
*/
|
||||
async closeIssue(issueNumber) {
|
||||
return this.updateIssue(issueNumber, { state: 'closed' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 Issue Comments
|
||||
*
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @returns {Promise<Array>} Comments 列表
|
||||
*/
|
||||
async getIssueComments(issueNumber) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
const comments = await mcp__gitea__issue_read({
|
||||
method: 'get_comments',
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber
|
||||
})
|
||||
|
||||
return comments
|
||||
} catch (error) {
|
||||
throw new Error(`读取 Issue Comments 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Issue Comment
|
||||
*
|
||||
* @param {number} issueNumber - Issue 编号
|
||||
* @param {string} body - Comment 内容
|
||||
* @returns {Promise<Object>} Comment 信息
|
||||
*/
|
||||
async createIssueComment(issueNumber, body) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
const comment = await mcp__gitea__issue_write({
|
||||
method: 'add_comment',
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body
|
||||
})
|
||||
|
||||
return comment
|
||||
} catch (error) {
|
||||
throw new Error(`添加 Comment 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分支
|
||||
*
|
||||
* @param {string} branchName - 分支名称
|
||||
* @param {string} oldBranch - 源分支(默认为主分支)
|
||||
* @returns {Promise<Object>} 分支信息
|
||||
*/
|
||||
async createBranch(branchName, oldBranch = null) {
|
||||
const { owner, repo, defaultBranch } = this.repoInfo
|
||||
const sourceBranch = oldBranch || defaultBranch
|
||||
|
||||
try {
|
||||
const result = await mcp__gitea__create_branch({
|
||||
owner,
|
||||
repo,
|
||||
branch: branchName,
|
||||
old_branch: sourceBranch
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
throw new Error(`创建分支失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出分支
|
||||
*
|
||||
* @returns {Promise<Array>} 分支列表
|
||||
*/
|
||||
async listBranches() {
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
const branches = await mcp__gitea__list_branches({
|
||||
owner,
|
||||
repo
|
||||
})
|
||||
|
||||
return branches
|
||||
} catch (error) {
|
||||
throw new Error(`列出分支失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Pull Request
|
||||
*
|
||||
* @param {Object} options - PR 选项
|
||||
* @param {string} options.title - PR 标题
|
||||
* @param {string} options.body - PR 正文
|
||||
* @param {string} options.head - 源分支
|
||||
* @param {string} options.base - 目标分支
|
||||
* @returns {Promise<Object>} PR 信息
|
||||
*/
|
||||
async createPullRequest(options) {
|
||||
const { title, body, head, base } = options
|
||||
const { owner, repo, defaultBranch } = this.repoInfo
|
||||
const targetBranch = base || defaultBranch
|
||||
|
||||
try {
|
||||
const result = await mcp__gitea__pull_request_write({
|
||||
method: 'create',
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
head,
|
||||
base: targetBranch
|
||||
})
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
number: result.number,
|
||||
url: `${this.repoInfo.baseUrl}/${owner}/${repo}/pulls/${result.number}`,
|
||||
title: result.title,
|
||||
body: result.body,
|
||||
head: result.head,
|
||||
base: result.base
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`创建 PR 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并 Pull Request
|
||||
*
|
||||
* @param {number} pullNumber - PR 编号
|
||||
* @param {Object} options - 合并选项
|
||||
* @returns {Promise<Object>} 合并后的 PR 信息
|
||||
*/
|
||||
async mergePullRequest(pullNumber, options = {}) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
const { deleteBranch = true, mergeStyle = 'merge' } = options
|
||||
|
||||
try {
|
||||
const result = await mcp__gitea__pull_request_write({
|
||||
method: 'merge',
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullNumber,
|
||||
delete_branch_after_merge: deleteBranch,
|
||||
merge_style: mergeStyle
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
throw new Error(`合并 PR 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Pull Request
|
||||
*
|
||||
* @param {number} pullNumber - PR 编号
|
||||
* @returns {Promise<Object>} PR 信息
|
||||
*/
|
||||
async getPullRequest(pullNumber) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
const pr = await mcp__gitea__pull_request_read({
|
||||
method: 'get',
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullNumber
|
||||
})
|
||||
|
||||
return pr
|
||||
} catch (error) {
|
||||
throw new Error(`获取 PR 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出 Pull Request
|
||||
*
|
||||
* @param {Object} options - 查询选项
|
||||
* @returns {Promise<Array>} PR 列表
|
||||
*/
|
||||
async listPullRequests(options = {}) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
const { state = 'open' } = options
|
||||
|
||||
try {
|
||||
const prs = await mcp__gitea__list_pull_requests({
|
||||
owner,
|
||||
repo,
|
||||
state
|
||||
})
|
||||
|
||||
return prs
|
||||
} catch (error) {
|
||||
throw new Error(`列出 PR 失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新文件
|
||||
*
|
||||
* @param {Object} options - 文件选项
|
||||
* @param {string} options.path - 文件路径
|
||||
* @param {string} options.content - 文件内容
|
||||
* @param {string} options.message - Commit 消息
|
||||
* @param {string} options.branch - 分支名称
|
||||
* @param {string} options.sha - 文件 SHA(更新时需要)
|
||||
* @returns {Promise<Object>} 文件信息
|
||||
*/
|
||||
async createOrUpdateFile(options) {
|
||||
const { path: filePath, content, message, branch, sha } = options
|
||||
const { owner, repo } = this.repoInfo
|
||||
|
||||
try {
|
||||
const result = await mcp__gitea__create_or_update_file({
|
||||
owner,
|
||||
repo,
|
||||
path: filePath,
|
||||
content: Buffer.from(content).toString('base64'),
|
||||
message,
|
||||
branch_name: branch,
|
||||
sha
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
throw new Error(`创建/更新文件失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容
|
||||
*
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {string} ref - 分支或 commit
|
||||
* @returns {Promise<Object>} 文件内容
|
||||
*/
|
||||
async getFileContents(filePath, ref = null) {
|
||||
const { owner, repo, defaultBranch } = this.repoInfo
|
||||
const targetRef = ref || defaultBranch
|
||||
|
||||
try {
|
||||
const file = await mcp__gitea__get_file_contents({
|
||||
owner,
|
||||
repo,
|
||||
path: filePath,
|
||||
ref: targetRef
|
||||
})
|
||||
|
||||
return file
|
||||
} catch (error) {
|
||||
throw new Error(`获取文件内容失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出提交
|
||||
*
|
||||
* @param {Object} options - 查询选项
|
||||
* @returns {Promise<Array>} 提交列表
|
||||
*/
|
||||
async listCommits(options = {}) {
|
||||
const { owner, repo } = this.repoInfo
|
||||
const { sha, path } = options
|
||||
|
||||
try {
|
||||
const commits = await mcp__gitea__list_commits({
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
path
|
||||
})
|
||||
|
||||
return commits
|
||||
} catch (error) {
|
||||
throw new Error(`列出提交失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带重试的操作
|
||||
*
|
||||
* @param {Function} operation - 操作函数
|
||||
* @param {number} maxRetries - 最大重试次数
|
||||
* @returns {Promise<any>} 操作结果
|
||||
*/
|
||||
async withRetry(operation, maxRetries = 3) {
|
||||
let lastError
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
return await operation()
|
||||
} catch (error) {
|
||||
lastError = error
|
||||
console.warn(`操作失败,重试 ${i + 1}/${maxRetries}: ${error.message}`)
|
||||
// 等待后重试
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
|
||||
}
|
||||
}
|
||||
throw lastError
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建适配器实例
|
||||
*/
|
||||
export function createGiteaAdapter() {
|
||||
return new GiteaMCPAdapter()
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出单例
|
||||
*/
|
||||
export default createGiteaAdapter()
|
||||
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
* Linus 三问决策框架
|
||||
*
|
||||
* 实现工程审慎决策逻辑
|
||||
* 来源: Linus Torvalds "Talk Like a Kernel Developer"
|
||||
*/
|
||||
|
||||
/**
|
||||
* 三问枚举
|
||||
*/
|
||||
export const QuestionType = {
|
||||
REAL_PROBLEM: '这是现实问题还是想象问题?',
|
||||
NEEDS_SOLUTION: '这个问题真的需要解决吗?',
|
||||
SOLUTION_VIABLE: '这个方案真的能解决问题吗?'
|
||||
}
|
||||
|
||||
/**
|
||||
* Linus 三问决策器类
|
||||
*/
|
||||
export class LinusTriadDecision {
|
||||
constructor() {
|
||||
this.questions = [
|
||||
QuestionType.REAL_PROBLEM,
|
||||
QuestionType.NEEDS_SOLUTION,
|
||||
QuestionType.SOLUTION_VIABLE
|
||||
]
|
||||
this.answers = []
|
||||
this.reasoning = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行完整的 Linus 三问
|
||||
*
|
||||
* @param {string} taskDescription - 任务描述
|
||||
* @param {Object} options - 选项
|
||||
* @returns {Promise<Object>} 决策结果
|
||||
*/
|
||||
async ask(taskDescription, options = {}) {
|
||||
const { interactive = false, context = null } = options
|
||||
|
||||
// 清空之前的答案
|
||||
this.answers = []
|
||||
this.reasoning = []
|
||||
|
||||
log('🤔 开始 Linus 三问决策...')
|
||||
log(`📋 任务: ${taskDescription}`)
|
||||
|
||||
// 第一问:现实问题还是想象问题?
|
||||
const q1Result = await this.isRealProblem(taskDescription, context)
|
||||
this.answers.push(q1Result.answer)
|
||||
this.reasoning.push(q1Result.reasoning)
|
||||
|
||||
if (!q1Result.answer) {
|
||||
log('❌ 第一问未通过:这不是现实问题')
|
||||
return this.createResult(false, '第一问未通过:这不是现实问题', q1Result.reasoning)
|
||||
}
|
||||
log('✅ 第一问通过:这是现实问题')
|
||||
|
||||
// 第二问:这个问题真的需要解决吗?
|
||||
const q2Result = await this.needsSolution(taskDescription, context)
|
||||
this.answers.push(q2Result.answer)
|
||||
this.reasoning.push(q2Result.reasoning)
|
||||
|
||||
if (!q2Result.answer) {
|
||||
log('❌ 第二问未通过:这个问题不需要解决')
|
||||
return this.createResult(false, '第二问未通过:这个问题不需要解决', [
|
||||
this.reasoning[0],
|
||||
q2Result.reasoning
|
||||
])
|
||||
}
|
||||
log('✅ 第二问通过:这个问题需要解决')
|
||||
|
||||
// 第三问:这个方案真的能解决问题吗?
|
||||
const q3Result = await this.solutionViable(taskDescription, context)
|
||||
this.answers.push(q3Result.answer)
|
||||
this.reasoning.push(q3Result.reasoning)
|
||||
|
||||
if (!q3Result.answer) {
|
||||
log('❌ 第三问未通过:这个方案不能解决问题')
|
||||
return this.createResult(false, '第三问未通过:这个方案不能解决问题', [
|
||||
this.reasoning[0],
|
||||
this.reasoning[1],
|
||||
q3Result.reasoning
|
||||
])
|
||||
}
|
||||
log('✅ 第三问通过:这个方案能解决问题')
|
||||
|
||||
log('✅ Linus 三问全部通过!')
|
||||
|
||||
return this.createResult(true, 'Linus 三问全部通过', this.reasoning)
|
||||
}
|
||||
|
||||
/**
|
||||
* 第一问:这是现实问题还是想象问题?
|
||||
*
|
||||
* 拒绝条件:
|
||||
* - "可能需要"、"也许将来"
|
||||
* - 纯粹的假设性场景
|
||||
* - "以防万一"
|
||||
*/
|
||||
async isRealProblem(taskDescription, context) {
|
||||
log(`\n📌 第一问:${QuestionType.REAL_PROBLEM}`)
|
||||
|
||||
const answer = await this.evaluateQuestion(taskDescription, context, {
|
||||
positiveIndicators: [
|
||||
'用户反馈',
|
||||
'生产问题',
|
||||
'线上错误',
|
||||
'性能瓶颈',
|
||||
'安全漏洞',
|
||||
'bug',
|
||||
'错误',
|
||||
'失败',
|
||||
'崩溃',
|
||||
'无法',
|
||||
'必须',
|
||||
'紧急',
|
||||
'关键'
|
||||
],
|
||||
negativeIndicators: [
|
||||
'可能需要',
|
||||
'也许将来',
|
||||
'以防万一',
|
||||
'考虑未来',
|
||||
'如果发生',
|
||||
'假设',
|
||||
'理论上',
|
||||
'或许',
|
||||
'可能'
|
||||
],
|
||||
analysisPrompt: `判断以下任务是否是现实问题:
|
||||
"${taskDescription}"
|
||||
|
||||
回答 JSON:
|
||||
{
|
||||
"isRealProblem": true|false,
|
||||
"reasoning": "判断理由",
|
||||
"evidence": ["证据1", "证据2"],
|
||||
"verdict": "这是现实问题/这是想象问题"
|
||||
}`
|
||||
})
|
||||
|
||||
return {
|
||||
answer: answer.isRealProblem,
|
||||
reasoning: answer.reasoning,
|
||||
evidence: answer.evidence,
|
||||
verdict: answer.verdict
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二问:这个问题真的需要解决吗?
|
||||
*
|
||||
* 拒绝条件:
|
||||
* - 边缘场景
|
||||
* - 伪需求
|
||||
* - 性价比极低
|
||||
*/
|
||||
async needsSolution(taskDescription, context) {
|
||||
log(`\n📌 第二问:${QuestionType.NEEDS_SOLUTION}`)
|
||||
|
||||
const answer = await this.evaluateQuestion(taskDescription, context, {
|
||||
positiveIndicators: [
|
||||
'影响用户体验',
|
||||
'影响性能',
|
||||
'影响安全',
|
||||
'影响稳定性',
|
||||
'阻塞功能',
|
||||
'影响核心',
|
||||
'高频',
|
||||
'重要',
|
||||
'必要',
|
||||
'必须'
|
||||
],
|
||||
negativeIndicators: [
|
||||
'边缘',
|
||||
'小众',
|
||||
'罕见',
|
||||
'偶尔',
|
||||
'理论上',
|
||||
'可以接受',
|
||||
'不是必需',
|
||||
'可选项',
|
||||
'优化项',
|
||||
'锦上添花'
|
||||
],
|
||||
analysisPrompt: `判断以下问题是否真的需要解决:
|
||||
"${taskDescription}"
|
||||
|
||||
回答 JSON:
|
||||
{
|
||||
"needsSolution": true|false,
|
||||
"reasoning": "判断理由",
|
||||
"impact": "高/中/低",
|
||||
"verdict": "这个问题需要解决/这个问题不需要解决"
|
||||
}`
|
||||
})
|
||||
|
||||
return {
|
||||
answer: answer.needsSolution,
|
||||
reasoning: answer.reasoning,
|
||||
impact: answer.impact,
|
||||
verdict: answer.verdict
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三问:这个方案真的能解决问题吗?
|
||||
*
|
||||
* 拒绝条件:
|
||||
* - 理论上可行但无验证
|
||||
* - 缺少实施路径
|
||||
* - 技术栈不匹配
|
||||
*/
|
||||
async solutionViable(taskDescription, context) {
|
||||
log(`\n📌 第三问:${QuestionType.SOLUTION_VIABLE}`)
|
||||
|
||||
const answer = await this.evaluateQuestion(taskDescription, context, {
|
||||
positiveIndicators: [
|
||||
'明确方案',
|
||||
'具体步骤',
|
||||
'已有先例',
|
||||
'可验证',
|
||||
'可测试',
|
||||
'有计划',
|
||||
'有路径',
|
||||
'可行'
|
||||
],
|
||||
negativeIndicators: [
|
||||
'理论上',
|
||||
'应该可以',
|
||||
'可能需要',
|
||||
'待研究',
|
||||
'不确定',
|
||||
'猜测',
|
||||
'假设',
|
||||
'无从验证'
|
||||
],
|
||||
analysisPrompt: `判断以下方案是否真的能解决问题:
|
||||
"${taskDescription}"
|
||||
|
||||
如果有具体方案,请评估其可行性。如果没有方案,请判断是否可以制定有效方案。
|
||||
|
||||
回答 JSON:
|
||||
{
|
||||
"solutionViable": true|false,
|
||||
"reasoning": "判断理由",
|
||||
"hasConcretePlan": true|false,
|
||||
"verdict": "这个方案能解决问题/这个方案不能解决问题"
|
||||
}`
|
||||
})
|
||||
|
||||
return {
|
||||
answer: answer.solutionViable,
|
||||
reasoning: answer.reasoning,
|
||||
hasConcretePlan: answer.hasConcretePlan,
|
||||
verdict: answer.verdict
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 评估问题(使用 Agent 进行分析)
|
||||
*/
|
||||
async evaluateQuestion(taskDescription, context, config) {
|
||||
// 如果有上下文信息,使用它
|
||||
const fullContext = context ? `上下文: ${context}\n` : ''
|
||||
|
||||
// 使用 Plan Agent 进行分析
|
||||
try {
|
||||
const analysis = await agent({
|
||||
subagent_type: 'Plan',
|
||||
prompt: `${fullContext}${config.analysisPrompt}`,
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
isRealProblem: { type: 'boolean' },
|
||||
needsSolution: { type: 'boolean' },
|
||||
solutionViable: { type: 'boolean' },
|
||||
reasoning: { type: 'string' },
|
||||
evidence: { type: 'array', items: { type: 'string' } },
|
||||
impact: { type: 'string' },
|
||||
hasConcretePlan: { type: 'boolean' },
|
||||
verdict: { type: 'string' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return analysis
|
||||
} catch (error) {
|
||||
log(`⚠️ Agent 分析失败: ${error.message}`)
|
||||
// 回退到简单关键词检测
|
||||
return this.fallbackEvaluation(taskDescription, config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退评估(基于关键词)
|
||||
*/
|
||||
fallbackEvaluation(taskDescription, config) {
|
||||
const text = taskDescription.toLowerCase()
|
||||
const { positiveIndicators, negativeIndicators } = config
|
||||
|
||||
let positiveScore = 0
|
||||
let negativeScore = 0
|
||||
|
||||
for (const indicator of positiveIndicators) {
|
||||
if (text.includes(indicator.toLowerCase())) {
|
||||
positiveScore++
|
||||
}
|
||||
}
|
||||
|
||||
for (const indicator of negativeIndicators) {
|
||||
if (text.includes(indicator.toLowerCase())) {
|
||||
negativeScore++
|
||||
}
|
||||
}
|
||||
|
||||
const answer = positiveScore > negativeScore
|
||||
const reasoning = `基于关键词检测:正面指标 ${positiveScore} 个,负面指标 ${negativeScore} 个`
|
||||
|
||||
return {
|
||||
isRealProblem: answer,
|
||||
needsSolution: answer,
|
||||
solutionViable: answer,
|
||||
reasoning,
|
||||
verdict: answer ? '通过关键词检测' : '未通过关键词检测'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建决策结果
|
||||
*/
|
||||
createResult(passed, message, reasoning) {
|
||||
return {
|
||||
passed,
|
||||
message,
|
||||
reasoning: Array.isArray(reasoning) ? reasoning : [reasoning],
|
||||
answers: this.answers,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:执行 Linus 三问
|
||||
*
|
||||
* @param {string} taskDescription - 任务描述
|
||||
* @param {Object} options - 选项
|
||||
* @returns {Promise<Object>} 决策结果
|
||||
*/
|
||||
export async function askLinusTriad(taskDescription, options = {}) {
|
||||
const decision = new LinusTriadDecision()
|
||||
return await decision.ask(taskDescription, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查任务是否通过 Linus 三问
|
||||
*
|
||||
* @param {string} taskDescription - 任务描述
|
||||
* @returns {Promise<boolean>} 是否通过
|
||||
*/
|
||||
export async function passesLinusTriad(taskDescription) {
|
||||
const result = await askLinusTriad(taskDescription)
|
||||
return result.passed
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成拒绝响应
|
||||
*
|
||||
* @param {Object} decisionResult - 决策结果
|
||||
* @returns {string} 拒绝消息
|
||||
*/
|
||||
export function generateRejectionMessage(decisionResult) {
|
||||
let message = `## ❌ 任务未通过 Linus 三问决策\n\n`
|
||||
message += `**原因**: ${decisionResult.message}\n\n`
|
||||
message += `### 决策过程\n\n`
|
||||
|
||||
for (let i = 0; i < decisionResult.reasoning.length; i++) {
|
||||
message += `**${i + 1}. ${decisionResult.reasoning[i]}**\n\n`
|
||||
}
|
||||
|
||||
message += `### 建议\n\n`
|
||||
message += `- 请重新审视任务需求\n`
|
||||
message += `- 确保这是需要解决的现实问题\n`
|
||||
message += `- 提供明确的解决方案\n`
|
||||
message += `- 如有疑问,请使用 deep-interview 进行需求澄清\n\n`
|
||||
|
||||
message += `---\n`
|
||||
message += `**决策时间**: ${decisionResult.timestamp}\n`
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:日志输出
|
||||
*/
|
||||
function log(message) {
|
||||
console.log(`[LinusTriad] ${message}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
export default {
|
||||
LinusTriadDecision,
|
||||
askLinusTriad,
|
||||
passesLinusTriad,
|
||||
generateRejectionMessage,
|
||||
QuestionType
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* 需求追踪模块
|
||||
*
|
||||
* 追踪需求到设计和编码的映射,生成一致性检查报告
|
||||
*/
|
||||
|
||||
/**
|
||||
* 需求追踪器类
|
||||
*/
|
||||
export class RequirementTracker {
|
||||
constructor() {
|
||||
this.requirements = new Map() // id -> requirement
|
||||
this.designMapping = new Map() // requirementId -> designItems
|
||||
this.codeMapping = new Map() // requirementId -> codeItems
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加需求
|
||||
*/
|
||||
addRequirement(id, requirement) {
|
||||
this.requirements.set(id, {
|
||||
id,
|
||||
description: typeof requirement === 'string' ? requirement : requirement.description || requirement.text || JSON.stringify(requirement),
|
||||
status: 'pending',
|
||||
...requirement
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加设计映射
|
||||
*/
|
||||
addDesignMapping(requirementId, designItems) {
|
||||
this.designMapping.set(requirementId, Array.isArray(designItems) ? designItems : [designItems])
|
||||
|
||||
// 更新需求状态
|
||||
const req = this.requirements.get(requirementId)
|
||||
if (req) {
|
||||
req.status = 'designed'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加编码映射
|
||||
*/
|
||||
addCodeMapping(requirementId, codeItems) {
|
||||
this.codeMapping.set(requirementId, Array.isArray(codeItems) ? codeItems : [codeItems])
|
||||
|
||||
// 更新需求状态
|
||||
const req = this.requirements.get(requirementId)
|
||||
if (req) {
|
||||
req.status = 'implemented'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需求
|
||||
*/
|
||||
getRequirement(id) {
|
||||
return this.requirements.get(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有需求
|
||||
*/
|
||||
getAllRequirements() {
|
||||
return Array.from(this.requirements.values())
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需求的设计映射
|
||||
*/
|
||||
getDesignMapping(requirementId) {
|
||||
return this.designMapping.get(requirementId) || []
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需求的编码映射
|
||||
*/
|
||||
getCodeMapping(requirementId) {
|
||||
return this.codeMapping.get(requirementId) || []
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查需求的完整性
|
||||
*/
|
||||
checkCompleteness(requirementId) {
|
||||
const req = this.getRequirement(requirementId)
|
||||
if (!req) {
|
||||
return { complete: false, reason: '需求不存在' }
|
||||
}
|
||||
|
||||
const hasDesign = this.designMapping.has(requirementId)
|
||||
const hasCode = this.codeMapping.has(requirementId)
|
||||
|
||||
if (!hasDesign) {
|
||||
return { complete: false, reason: '缺少设计映射' }
|
||||
}
|
||||
|
||||
if (!hasCode) {
|
||||
return { complete: false, reason: '缺少编码映射' }
|
||||
}
|
||||
|
||||
return { complete: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成追踪报告
|
||||
*/
|
||||
generateReport() {
|
||||
const requirements = this.getAllRequirements()
|
||||
const report = {
|
||||
total: requirements.length,
|
||||
pending: 0,
|
||||
designed: 0,
|
||||
implemented: 0,
|
||||
items: []
|
||||
}
|
||||
|
||||
for (const req of requirements) {
|
||||
const completeness = this.checkCompleteness(req.id)
|
||||
const designItems = this.getDesignMapping(req.id)
|
||||
const codeItems = this.getCodeMapping(req.id)
|
||||
|
||||
if (req.status === 'pending') {
|
||||
report.pending++
|
||||
} else if (req.status === 'designed') {
|
||||
report.designed++
|
||||
} else if (req.status === 'implemented') {
|
||||
report.implemented++
|
||||
}
|
||||
|
||||
report.items.push({
|
||||
id: req.id,
|
||||
description: req.description,
|
||||
status: req.status,
|
||||
complete: completeness.complete,
|
||||
designCount: designItems.length,
|
||||
codeCount: codeItems.length,
|
||||
designItems,
|
||||
codeItems
|
||||
})
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出为 Markdown 格式
|
||||
*/
|
||||
toMarkdown() {
|
||||
const report = this.generateReport()
|
||||
let md = '# 需求追踪报告\n\n'
|
||||
md += `## 概述\n\n`
|
||||
md += `- 总需求数: ${report.total}\n`
|
||||
md += `- 待处理: ${report.pending}\n`
|
||||
md += `- 已设计: ${report.designed}\n`
|
||||
md += `- 已实现: ${report.implemented}\n\n`
|
||||
|
||||
md += `## 详细追踪\n\n`
|
||||
|
||||
for (const item of report.items) {
|
||||
md += `### ${item.id}: ${item.description}\n\n`
|
||||
md += `- **状态**: ${item.status}\n`
|
||||
md += `- **完整**: ${item.complete ? '✅' : '❌'}\n`
|
||||
md += `- **设计映射**: ${item.designCount} 项\n`
|
||||
md += `- **编码映射**: ${item.codeCount} 项\n\n`
|
||||
|
||||
if (item.designItems.length > 0) {
|
||||
md += `#### 设计映射\n\n`
|
||||
for (const design of item.designItems) {
|
||||
md += `- ${typeof design === 'string' ? design : JSON.stringify(design)}\n`
|
||||
}
|
||||
md += `\n`
|
||||
}
|
||||
|
||||
if (item.codeItems.length > 0) {
|
||||
md += `#### 编码映射\n\n`
|
||||
for (const code of item.codeItems) {
|
||||
md += `- ${typeof code === 'string' ? code : JSON.stringify(code)}\n`
|
||||
}
|
||||
md += `\n`
|
||||
}
|
||||
}
|
||||
|
||||
return md
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建需求追踪器
|
||||
*/
|
||||
export function createRequirementTracker() {
|
||||
return new RequirementTracker()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从一致性检查结果生成追踪数据
|
||||
*
|
||||
* @param {Object} checkResult - 一致性检查结果
|
||||
* @returns {Object} 追踪数据
|
||||
*/
|
||||
export function extractTrackingData(checkResult) {
|
||||
const tracker = new RequirementTracker()
|
||||
|
||||
// 从需求→设计检查中提取需求
|
||||
if (checkResult.requirementDesign) {
|
||||
for (const item of checkResult.requirementDesign.items) {
|
||||
const id = item.check.replace('需求 ', '')
|
||||
tracker.addRequirement(id, item.expected)
|
||||
if (item.passed) {
|
||||
tracker.addDesignMapping(id, [item.actual])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从需求→编码检查中提取编码映射
|
||||
if (checkResult.requirementCode) {
|
||||
for (const item of checkResult.requirementCode.items) {
|
||||
const id = item.check.replace('需求 ', '')
|
||||
if (item.passed) {
|
||||
tracker.addCodeMapping(id, [item.actual])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tracker.generateReport()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一致性检查报告
|
||||
*
|
||||
* @param {Object} checkResult - 一致性检查结果
|
||||
* @returns {string} Markdown 报告
|
||||
*/
|
||||
export function generateConsistencyReport(checkResult) {
|
||||
let md = '# 三向一致性检查报告\n\n'
|
||||
|
||||
// 概述
|
||||
md += `## 概述\n\n`
|
||||
md += `- **总体结果**: ${checkResult.allPassed ? '✅ 通过' : '❌ 未通过'}\n`
|
||||
md += `- **需求→设计**: ${checkResult.requirementDesign.passed ? '✅ 通过' : '❌ 未通过'}\n`
|
||||
md += `- **设计→编码**: ${checkResult.designCode.passed ? '✅ 通过' : '❌ 未通过'}\n`
|
||||
md += `- **需求→编码**: ${checkResult.requirementCode.passed ? '✅ 通过' : '❌ 未通过'}\n\n`
|
||||
|
||||
// 需求→设计详细结果
|
||||
md += `## 1️⃣ 需求 → 设计\n\n`
|
||||
md += generateCheckTable(checkResult.requirementDesign.items)
|
||||
md += `\n${checkResult.requirementDesign.summary}\n\n`
|
||||
|
||||
// 设计→编码详细结果
|
||||
md += `## 2️⃣ 设计 → 编码\n\n`
|
||||
md += generateCheckTable(checkResult.designCode.items)
|
||||
md += `\n${checkResult.designCode.summary}\n\n`
|
||||
|
||||
// 需求→编码详细结果
|
||||
md += `## 3️⃣ 需求 → 编码\n\n`
|
||||
md += generateCheckTable(checkResult.requirementCode.items)
|
||||
md += `\n${checkResult.requirementCode.summary}\n\n`
|
||||
|
||||
// 偏差列表
|
||||
if (checkResult.deviations && checkResult.deviations.length > 0) {
|
||||
md += `## ⚠️ 发现的偏差\n\n`
|
||||
for (const deviation of checkResult.deviations) {
|
||||
md += `### ${deviation.type}\n\n`
|
||||
for (const issue of deviation.issues) {
|
||||
md += `- **${issue.check}**: ${issue.expected} ≠ ${issue.actual}\n`
|
||||
}
|
||||
md += `\n`
|
||||
}
|
||||
}
|
||||
|
||||
return md
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:生成检查表格
|
||||
*/
|
||||
function generateCheckTable(items) {
|
||||
if (!items || items.length === 0) {
|
||||
return '无检查项\n'
|
||||
}
|
||||
|
||||
let table = '| 检查项 | 预期 | 实际 | 状态 |\n'
|
||||
table += '|--------|------|------|------|\n'
|
||||
|
||||
for (const item of items) {
|
||||
const status = item.passed ? '✅' : '❌'
|
||||
table += `| ${item.check} | ${item.expected} | ${item.actual} | ${status} |\n`
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出默认函数
|
||||
*/
|
||||
export default {
|
||||
RequirementTracker,
|
||||
createRequirementTracker,
|
||||
extractTrackingData,
|
||||
generateConsistencyReport
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* Superpowers 集成层
|
||||
*
|
||||
* 实现 Superpowers 五技能体系的调用集成
|
||||
*/
|
||||
|
||||
/**
|
||||
* Superpowers 五阶段枚举
|
||||
*/
|
||||
export const SuperpowersPhase = {
|
||||
WRITING_PLANS: 'writing-plans',
|
||||
EXECUTING_PLANS: 'executing-plans',
|
||||
REQUESTING_CODE_REVIEW: 'requesting-code-review',
|
||||
SYSTEMATIC_DEBUGGING: 'systematic-debugging',
|
||||
FINISHING_DEVELOPMENT: 'finishing-a-development-branch'
|
||||
}
|
||||
|
||||
/**
|
||||
* Superpowers 集成类
|
||||
*/
|
||||
export class SuperpowersIntegration {
|
||||
constructor() {
|
||||
this.currentPhase = null
|
||||
this.phaseHistory = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 writing-plans
|
||||
*
|
||||
* @param {Object} context - 上下文信息
|
||||
* @returns {Promise<Object>} 规划结果
|
||||
*/
|
||||
async invokeWritingPlans(context) {
|
||||
this.setPhase(SuperpowersPhase.WRITING_PLANS)
|
||||
log('📝 调用 writing-plans...')
|
||||
|
||||
try {
|
||||
const prompt = this.generateWritingPlansPrompt(context)
|
||||
const plan = await this.callSkill('writing-plans', prompt, context)
|
||||
|
||||
log('✅ writing-plans 完成')
|
||||
return plan
|
||||
} catch (error) {
|
||||
log(`❌ writing-plans 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 executing-plans
|
||||
*
|
||||
* @param {Object} plan - 执行计划
|
||||
* @returns {Promise<Object>} 执行结果
|
||||
*/
|
||||
async invokeExecutingPlans(plan) {
|
||||
this.setPhase(SuperpowersPhase.EXECUTING_PLANS)
|
||||
log('⚡ 调用 executing-plans...')
|
||||
|
||||
try {
|
||||
const prompt = this.generateExecutingPlansPrompt(plan)
|
||||
const result = await this.callSkill('executing-plans', prompt, { plan })
|
||||
|
||||
log('✅ executing-plans 完成')
|
||||
return result
|
||||
} catch (error) {
|
||||
log(`❌ executing-plans 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 requesting-code-review
|
||||
*
|
||||
* @param {Object} code - 代码信息
|
||||
* @returns {Promise<Object>} 审查结果
|
||||
*/
|
||||
async invokeRequestingCodeReview(code) {
|
||||
this.setPhase(SuperpowersPhase.REQUESTING_CODE_REVIEW)
|
||||
log('🔍 调用 requesting-code-review...')
|
||||
|
||||
try {
|
||||
const prompt = this.generateCodeReviewPrompt(code)
|
||||
const review = await this.callSkill('requesting-code-review', prompt, { code })
|
||||
|
||||
log('✅ requesting-code-review 完成')
|
||||
return review
|
||||
} catch (error) {
|
||||
log(`❌ requesting-code-review 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 systematic-debugging
|
||||
*
|
||||
* @param {Object} issue - 问题信息
|
||||
* @returns {Promise<Object>} 调试结果
|
||||
*/
|
||||
async invokeSystematicDebugging(issue) {
|
||||
this.setPhase(SuperpowersPhase.SYSTEMATIC_DEBUGGING)
|
||||
log('🐛 调用 systematic-debugging...')
|
||||
|
||||
try {
|
||||
const prompt = this.generateDebuggingPrompt(issue)
|
||||
const result = await this.callSkill('systematic-debugging', prompt, { issue })
|
||||
|
||||
log('✅ systematic-debugging 完成')
|
||||
return result
|
||||
} catch (error) {
|
||||
log(`❌ systematic-debugging 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 finishing-a-development-branch
|
||||
*
|
||||
* @param {Object} context - 上下文信息
|
||||
* @returns {Promise<Object>} 完成结果
|
||||
*/
|
||||
async invokeFinishingDevelopment(context) {
|
||||
this.setPhase(SuperpowersPhase.FINISHING_DEVELOPMENT)
|
||||
log('🏁 调用 finishing-a-development-branch...')
|
||||
|
||||
try {
|
||||
const prompt = this.generateFinishingPrompt(context)
|
||||
const result = await this.callSkill('finishing-a-development-branch', prompt, context)
|
||||
|
||||
log('✅ finishing-a-development-branch 完成')
|
||||
return result
|
||||
} catch (error) {
|
||||
log(`❌ finishing-a-development-branch 失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行完整的五阶段工作流
|
||||
*
|
||||
* @param {Object} context - 初始上下文
|
||||
* @returns {Promise<Object>} 完整工作流结果
|
||||
*/
|
||||
async runFullWorkflow(context) {
|
||||
log('🚀 开始 Superpowers 五阶段工作流...')
|
||||
|
||||
const results = {}
|
||||
|
||||
try {
|
||||
// 阶段 1: 规划
|
||||
results.plan = await this.invokeWritingPlans(context)
|
||||
|
||||
// 阶段 2: 执行
|
||||
results.execution = await this.invokeExecutingPlans(results.plan)
|
||||
|
||||
// 阶段 3: 审查
|
||||
results.review = await this.invokeRequestingCodeReview(results.execution)
|
||||
|
||||
// 如果审查未通过,进入调试
|
||||
if (results.review && !results.review.passed) {
|
||||
log('⚠️ 审查未通过,进入调试阶段...')
|
||||
results.debugging = await this.invokeSystematicDebugging({
|
||||
review: results.review,
|
||||
execution: results.execution
|
||||
})
|
||||
}
|
||||
|
||||
// 阶段 5: 完成
|
||||
results.finishing = await this.invokeFinishingDevelopment({
|
||||
...context,
|
||||
...results
|
||||
})
|
||||
|
||||
log('✅ Superpowers 五阶段工作流完成')
|
||||
return results
|
||||
} catch (error) {
|
||||
log(`❌ 工作流中断: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前阶段
|
||||
*/
|
||||
setPhase(phase) {
|
||||
if (this.currentPhase) {
|
||||
this.phaseHistory.push(this.currentPhase)
|
||||
}
|
||||
this.currentPhase = phase
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前阶段
|
||||
*/
|
||||
getPhase() {
|
||||
return this.currentPhase
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取阶段历史
|
||||
*/
|
||||
getPhaseHistory() {
|
||||
return [...this.phaseHistory, this.currentPhase].filter(Boolean)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 writing-plans 提示
|
||||
*/
|
||||
generateWritingPlansPrompt(context) {
|
||||
return `
|
||||
请编写一份综合实现计划,假设工程师对代码库零上下文。
|
||||
|
||||
## 上下文
|
||||
${this.formatContext(context)}
|
||||
|
||||
## 要求
|
||||
1. 任务背景和目标
|
||||
2. 技术方案选择
|
||||
3. 实施步骤
|
||||
4. 验收标准
|
||||
|
||||
请提供详细的实现计划。
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 executing-plans 提示
|
||||
*/
|
||||
generateExecutingPlansPrompt(plan) {
|
||||
return `
|
||||
请执行以下实现计划:
|
||||
|
||||
## 计划
|
||||
${this.formatContext(plan)}
|
||||
|
||||
## 要求
|
||||
1. 按照计划实施
|
||||
2. 编写完整的单元测试
|
||||
3. 确保代码质量
|
||||
4. 提供实施结果
|
||||
|
||||
请开始执行。
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码审查提示
|
||||
*/
|
||||
generateCodeReviewPrompt(code) {
|
||||
return `
|
||||
请审查以下代码:
|
||||
|
||||
## 代码信息
|
||||
${this.formatContext(code)}
|
||||
|
||||
## 审查维度
|
||||
1. 逻辑正确性和边界情况
|
||||
2. 安全漏洞
|
||||
3. 性能影响
|
||||
4. 测试覆盖率
|
||||
5. 错误处理
|
||||
|
||||
请提供详细的审查结果。
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成调试提示
|
||||
*/
|
||||
generateDebuggingPrompt(issue) {
|
||||
return `
|
||||
请系统化调试以下问题:
|
||||
|
||||
## 问题描述
|
||||
${this.formatContext(issue)}
|
||||
|
||||
## 要求
|
||||
1. 系统化定位问题(而非随机尝试)
|
||||
2. 分析根本原因
|
||||
3. 设计验证方案
|
||||
4. 提供修复建议
|
||||
|
||||
请开始调试。
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成完成提示
|
||||
*/
|
||||
generateFinishingPrompt(context) {
|
||||
return `
|
||||
请完成开发分支的收尾工作:
|
||||
|
||||
## 上下文
|
||||
${this.formatContext(context)}
|
||||
|
||||
## 要求
|
||||
1. 确认所有测试通过
|
||||
2. 验证代码质量
|
||||
3. 准备提交
|
||||
4. 清理临时文件
|
||||
|
||||
请完成收尾工作。
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化上下文
|
||||
*/
|
||||
formatContext(context) {
|
||||
if (typeof context === 'string') {
|
||||
return context
|
||||
}
|
||||
return JSON.stringify(context, null, 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用 Skill
|
||||
*/
|
||||
async callSkill(skillName, prompt, context) {
|
||||
try {
|
||||
// 使用 Skill 工具调用
|
||||
const result = await Skill({
|
||||
skill: skillName,
|
||||
args: JSON.stringify({ prompt, context })
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
// 如果 Skill 工具不可用,使用 Agent 作为回退
|
||||
log(`⚠️ Skill 工具不可用,使用 Agent 作为回退`)
|
||||
return await this.fallbackToAgent(skillName, prompt, context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退到 Agent
|
||||
*/
|
||||
async fallbackToAgent(skillName, prompt, context) {
|
||||
const fullPrompt = `
|
||||
作为 ${skillName} 专家,执行以下任务:
|
||||
|
||||
${prompt}
|
||||
|
||||
请返回结构化的 JSON 结果。
|
||||
`
|
||||
|
||||
return await agent({
|
||||
subagent_type: 'general-purpose',
|
||||
prompt: fullPrompt
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Superpowers 集成实例
|
||||
*/
|
||||
export function createSuperpowersIntegration() {
|
||||
return new SuperpowersIntegration()
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:调用 writing-plans
|
||||
*/
|
||||
export async function invokeWritingPlans(context) {
|
||||
const integration = createSuperpowersIntegration()
|
||||
return await integration.invokeWritingPlans(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:调用 executing-plans
|
||||
*/
|
||||
export async function invokeExecutingPlans(plan) {
|
||||
const integration = createSuperpowersIntegration()
|
||||
return await integration.invokeExecutingPlans(plan)
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:调用 requesting-code-review
|
||||
*/
|
||||
export async function invokeRequestingCodeReview(code) {
|
||||
const integration = createSuperpowersIntegration()
|
||||
return await integration.invokeRequestingCodeReview(code)
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:调用 systematic-debugging
|
||||
*/
|
||||
export async function invokeSystematicDebugging(issue) {
|
||||
const integration = createSuperpowersIntegration()
|
||||
return await integration.invokeSystematicDebugging(issue)
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速函数:调用 finishing-a-development-branch
|
||||
*/
|
||||
export async function invokeFinishingDevelopment(context) {
|
||||
const integration = createSuperpowersIntegration()
|
||||
return await integration.invokeFinishingDevelopment(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:日志输出
|
||||
*/
|
||||
function log(message) {
|
||||
console.log(`[Superpowers] ${message}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*/
|
||||
export default {
|
||||
SuperpowersIntegration,
|
||||
createSuperpowersIntegration,
|
||||
invokeWritingPlans,
|
||||
invokeExecutingPlans,
|
||||
invokeRequestingCodeReview,
|
||||
invokeSystematicDebugging,
|
||||
invokeFinishingDevelopment,
|
||||
SuperpowersPhase
|
||||
}
|
||||
Reference in New Issue
Block a user