diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 0000000..18e9cf2 --- /dev/null +++ b/.claude/CLAUDE.md @@ -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/` diff --git a/.claude/workflows/gitea-orchestration.js b/.claude/workflows/gitea-orchestration.js index 93c8a5b..990c79b 100644 --- a/.claude/workflows/gitea-orchestration.js +++ b/.claude/workflows/gitea-orchestration.js @@ -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}`) + } + + 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 API 关闭 Issue - log(`关闭 Issue: ${issueId}`) +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} 标记数据 + */ +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} completed - 已完成任务 + * @param {Array} 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) { diff --git a/.claude/workflows/helpers/comment-markers.js b/.claude/workflows/helpers/comment-markers.js new file mode 100644 index 0000000..1cbd685 --- /dev/null +++ b/.claude/workflows/helpers/comment-markers.js @@ -0,0 +1,281 @@ +/** + * Comment 标记生成器 + * + * 根据 v0.5 设计文档中的 Comment 标记约定,生成标准的完成标记 + */ + +/** + * 生成 Execute Sub Agent 完成标记 + * + * @param {Object} data - 完成数据 + * @param {Array} 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} data.findings - 发现的问题 + * @param {Array} 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} 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} data.completed - 已完成的任务 + * @param {Array} 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 +} diff --git a/.claude/workflows/helpers/comment-parser.js b/.claude/workflows/helpers/comment-parser.js new file mode 100644 index 0000000..b86efb8 --- /dev/null +++ b/.claude/workflows/helpers/comment-parser.js @@ -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} 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} comments - Comments 数组 + * @param {string} markerType - 标记类型 + * @returns {boolean} 是否存在 + */ +export function hasMarker(comments, markerType) { + return findLatestMarker(comments, markerType) !== null +} + +/** + * 获取所有标记的摘要 + * + * @param {Array} 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 +} diff --git a/.claude/workflows/helpers/consistency-checker.js b/.claude/workflows/helpers/consistency-checker.js new file mode 100644 index 0000000..1de34c1 --- /dev/null +++ b/.claude/workflows/helpers/consistency-checker.js @@ -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 +} diff --git a/.claude/workflows/helpers/gitea-mcp-adapter.js b/.claude/workflows/helpers/gitea-mcp-adapter.js new file mode 100644 index 0000000..f234489 --- /dev/null +++ b/.claude/workflows/helpers/gitea-mcp-adapter.js @@ -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} 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} 更新后的 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} 关闭后的 Issue 信息 + */ + async closeIssue(issueNumber) { + return this.updateIssue(issueNumber, { state: 'closed' }) + } + + /** + * 读取 Issue Comments + * + * @param {number} issueNumber - Issue 编号 + * @returns {Promise} 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} 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} 分支信息 + */ + 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} 分支列表 + */ + 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} 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} 合并后的 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} 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} 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} 文件信息 + */ + 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} 文件内容 + */ + 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} 提交列表 + */ + 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} 操作结果 + */ + 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() diff --git a/.claude/workflows/helpers/linus-triad.js b/.claude/workflows/helpers/linus-triad.js new file mode 100644 index 0000000..668ba87 --- /dev/null +++ b/.claude/workflows/helpers/linus-triad.js @@ -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} 决策结果 + */ + 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} 决策结果 + */ +export async function askLinusTriad(taskDescription, options = {}) { + const decision = new LinusTriadDecision() + return await decision.ask(taskDescription, options) +} + +/** + * 检查任务是否通过 Linus 三问 + * + * @param {string} taskDescription - 任务描述 + * @returns {Promise} 是否通过 + */ +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 +} diff --git a/.claude/workflows/helpers/requirement-tracker.js b/.claude/workflows/helpers/requirement-tracker.js new file mode 100644 index 0000000..cdb6109 --- /dev/null +++ b/.claude/workflows/helpers/requirement-tracker.js @@ -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 +} diff --git a/.claude/workflows/helpers/superpowers-integration.js b/.claude/workflows/helpers/superpowers-integration.js new file mode 100644 index 0000000..933364b --- /dev/null +++ b/.claude/workflows/helpers/superpowers-integration.js @@ -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} 规划结果 + */ + 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} 执行结果 + */ + 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} 审查结果 + */ + 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} 调试结果 + */ + 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} 完成结果 + */ + 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} 完整工作流结果 + */ + 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 +}