diff --git a/.github/ISSUE_TEMPLATE/dev-question.yml b/.github/ISSUE_TEMPLATE/dev-question.yml new file mode 100644 index 00000000..464295e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/dev-question.yml @@ -0,0 +1,52 @@ +name: "❓ 开发问题" +description: "遇到技术问题?铸渊会自动回答你" +title: "[问题] " +labels: ["dev-question", "pending"] +assignees: [] +body: + - type: input + id: dev_id + attributes: + label: "你的开发者编号" + description: "例如 DEV-001" + placeholder: "DEV-00X" + validations: + required: true + - type: input + id: module + attributes: + label: "相关模块" + description: "你当前在做的模块,例如 M01、M05" + placeholder: "M01" + validations: + required: true + - type: dropdown + id: category + attributes: + label: "问题分类" + options: + - 环境配置(Node/npm/终端) + - SSH/服务器连接 + - 代码报错 + - 广播步骤看不懂 + - 想知道当前进度 + - Git操作 + - 其他 + validations: + required: true + - type: textarea + id: description + attributes: + label: "问题描述" + description: "尽量说清楚:做到哪一步了、报了什么错、截图(如果有)" + placeholder: "我在执行 npm install 的时候报错了..." + validations: + required: true + - type: textarea + id: error_log + attributes: + label: "错误日志(可选)" + description: "终端里报的错误,复制粘贴过来" + render: shell + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/progress-query.yml b/.github/ISSUE_TEMPLATE/progress-query.yml new file mode 100644 index 00000000..c32413e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/progress-query.yml @@ -0,0 +1,31 @@ +name: "📊 进度查询" +description: "想知道自己或团队的开发进度" +title: "[进度] " +labels: ["progress-query", "pending"] +assignees: [] +body: + - type: input + id: dev_id + attributes: + label: "你的开发者编号" + placeholder: "DEV-00X" + validations: + required: true + - type: dropdown + id: query_type + attributes: + label: "查什么" + options: + - 我自己的进度 + - 我下一步要做什么 + - 团队整体进度 + - 谁在做跟我相关的模块 + - 我的模块依赖谁 + validations: + required: true + - type: textarea + id: extra + attributes: + label: "补充说明(可选)" + validations: + required: false diff --git a/.github/persona-brain/dev-status.json b/.github/persona-brain/dev-status.json new file mode 100644 index 00000000..d347d053 --- /dev/null +++ b/.github/persona-brain/dev-status.json @@ -0,0 +1,130 @@ +{ + "last_synced": "2026-03-05T23:00:00+08:00", + "synced_by": "霜砚巡检引擎", + "team_status": [ + { + "dev_id": "DEV-001", + "name": "页页", + "modules": ["后端中间层", "BC-集成-001"], + "current_broadcast": "BC-集成-002", + "status": "🟢 环节1–5全✅ · HTTPS+飞书Webhook全链路打通", + "waiting_for": "看板API路由部署", + "next_step": "完成看板API静态版v1.0部署", + "contact_for": ["服务器部署问题", "后端API问题", "Nginx配置"], + "os": "macOS" + }, + { + "dev_id": "DEV-002", + "name": "肥猫", + "modules": ["M01用户登录界面", "M03人格体选择器", "M04聊天气泡"], + "current_broadcast": "BC-M01-003", + "status": "🟢 M01+M03+M04全✅ · 五连胜 · 部署广播已出", + "waiting_for": "BC-M01-003 部署SYSLOG", + "next_step": "SSH部署前端到guanghulab.com", + "contact_for": ["前端部署问题", "SSH/Windows问题"], + "os": "Windows PowerShell", + "special": "副控·有root权限" + }, + { + "dev_id": "DEV-003", + "name": "燕樊", + "modules": ["对话UI", "M07设置页面", "M15云盘系统", "M10帮助中心"], + "current_broadcast": "BC-M10-001-YF", + "status": "🟡 对话UI✅ · M07✅ · M15✅ · M10广播已出", + "waiting_for": "M10帮助中心 SYSLOG", + "next_step": "完成M10帮助中心环节0~1", + "contact_for": ["对话UI相关问题", "设置页面问题"], + "os": "Windows 10" + }, + { + "dev_id": "DEV-004", + "name": "之之(秋秋)", + "modules": ["钉钉机器人"], + "current_broadcast": "BC-钉钉-001-ZZ", + "status": "🟡 环节0广播已出", + "waiting_for": "SYSLOG", + "next_step": "完成钉钉开发者后台配置", + "contact_for": ["钉钉API问题"], + "os": "未知" + }, + { + "dev_id": "DEV-005", + "name": "小草莓", + "modules": ["状态看板", "M12成本控制", "M13多人格体协作"], + "current_broadcast": "BC-看板-003-XCM", + "status": "🟢 看板✅ · M12✅ · M13✅ · WebSocket环节3广播已出", + "waiting_for": "看板环节3 SYSLOG", + "next_step": "完成WebSocket实时推送", + "contact_for": ["看板相关问题", "WebSocket问题"], + "os": "Windows 11" + }, + { + "dev_id": "DEV-009", + "name": "花尔", + "modules": ["M05用户中心界面"], + "current_broadcast": "BC-M05-003", + "status": "🟡 M05环节2广播已出", + "waiting_for": "M05环节2 SYSLOG", + "next_step": "完成本地数据存储功能", + "contact_for": ["用户中心UI问题"], + "os": "macOS" + }, + { + "dev_id": "DEV-010", + "name": "桔子", + "modules": ["M06工单管理", "M08数据统计面板", "M17动态漫制作系统"], + "current_broadcast": "M11风格组件库在手·M17待出", + "status": "🟢 M06全通 · M08全通 · 七连胜 · 前端毕业 · M17分配✅", + "waiting_for": "M11 SYSLOG → 然后出M17环节0广播", + "next_step": "完成M11风格组件库", + "contact_for": ["工单界面问题", "数据统计问题"], + "os": "macOS" + }, + { + "dev_id": "DEV-011", + "name": "匆匆那年", + "modules": ["M16码字工作台界面"], + "current_broadcast": "BC-000✅·M16环节0广播待出", + "status": "🟢 BC-000✅ · 首胜 · M16分配✅", + "waiting_for": "M16环节0广播", + "next_step": "等待M16环节0广播下发后开始", + "contact_for": [], + "os": "macOS M1" + }, + { + "dev_id": "DEV-012", + "name": "Awen", + "modules": ["M09消息通知中心界面"], + "current_broadcast": "BC-M09-003-AW", + "status": "🟢 M09环节1✅ · 三连胜 · 环节2~3广播已出", + "waiting_for": "M09环节2~3 SYSLOG", + "next_step": "完成通知列表分组+标记已读+徽章实时更新", + "contact_for": ["通知系统问题"], + "os": "Windows 10" + } + ], + "tech_routing": { + "level_1_self_check": "检查广播步骤是否漏看 → 检查终端命令是否打错 → 检查文件路径是否正确", + "level_2_peer_help": { + "server_ssh": "DEV-001页页 或 DEV-002肥猫(副控·有root权限)", + "frontend_general": "DEV-002肥猫 或 DEV-010桔子(前端毕业)", + "backend_api": "DEV-001页页", + "git_github": "DEV-001页页 或 DEV-012 Awen", + "windows_env": "DEV-002肥猫 或 DEV-005小草莓", + "macos_env": "DEV-009花尔 或 DEV-011匆匆那年", + "dingtalk": "DEV-004之之" + }, + "level_3_system": "等待霜砚巡检处理(每日12:00+23:00)" + }, + "server_info": { + "address": "8.155.62.235", + "user": "root", + "domain": "guanghulab.com", + "ssh_command": "ssh root@8.155.62.235", + "tech_stack": "Node.js 20 + Express + PM2 + Nginx + Certbot(HTTPS)" + }, + "api_info": { + "primary": "云雾API", + "note": "API Key 存放在仓库 Secrets → YUNWU_API_KEY,不在此文件中硬编码" + } +} diff --git a/.github/persona-brain/growth-journal.md b/.github/persona-brain/growth-journal.md new file mode 100644 index 00000000..16ef6959 --- /dev/null +++ b/.github/persona-brain/growth-journal.md @@ -0,0 +1,25 @@ +# 铸渊成长日记 + +> 铸渊(ICE-GL-ZY001)自动记录 · 每日自检追加 + +--- + +## 2026-03-05 · 初始化 +- 大脑文件完整性:✅ +- 知识库条目:4条(初始化) +- 累计自检次数:0 +- 状态:系统就绪,等待开发者提问 + +## 2026-03-05 · 每日自检 +- 大脑文件完整性:⚠️ 缺失 identity.md, routing-map.json, responsibility.md, decision-log.md +- 知识库条目:4条 +- 累计自检次数:1 +- 累计CI运行:1次 +- HLI覆盖率:17.6% + +## 2026-03-05 · 每日自检 +- 大脑文件完整性:⚠️ 缺失 identity.md, routing-map.json, responsibility.md, decision-log.md +- 知识库条目:4条 +- 累计自检次数:2 +- 累计CI运行:1次 +- HLI覆盖率:17.6% diff --git a/.github/persona-brain/knowledge-base.json b/.github/persona-brain/knowledge-base.json new file mode 100644 index 00000000..0bf8c05e --- /dev/null +++ b/.github/persona-brain/knowledge-base.json @@ -0,0 +1,30 @@ +{ + "last_updated": "2026-03-05", + "faq": [ + { + "category": "SSH/服务器", + "q": "SSH私钥文件是什么?怎么连接服务器?", + "a": "SSH是远程连接服务器的工具。连接命令:ssh root@8.155.62.235,密码找肥猫(副控·有root权限)。Windows用PowerShell自带SSH,macOS终端自带。", + "related_broadcast": "BC-M01-003" + }, + { + "category": "环境配置", + "q": "npm install 报错怎么办?", + "a": "先试 npm install --legacy-peer-deps。如果还不行试 npm cache clean --force 再装。Windows下如果证书报错加 --strict-ssl=false。", + "related_broadcast": "BC-钉钉-001-ZZ-002" + }, + { + "category": "Git", + "q": "怎么推送代码到GitHub?", + "a": "git add . → git commit -m '描述' → git push。第一次push需要设置远程仓库:git remote add origin 仓库地址。", + "related_broadcast": "BC-集成-003" + }, + { + "category": "广播", + "q": "广播里的步骤看不懂怎么办?", + "a": "广播每一步都是可直接复制粘贴的命令。从Step 1开始,一步一步做,做完一步截图,再做下一步。不要跳步。如果某一步报错,先把报错信息贴到这里提Issue。", + "related_broadcast": "通用" + } + ], + "auto_grow_rule": "每次霜砚补充回答后,铸渊自动把该问答对追加到faq数组中,下次遇到类似问题直接回答" +} \ No newline at end of file diff --git a/.github/persona-brain/memory.json b/.github/persona-brain/memory.json new file mode 100644 index 00000000..87ed39e0 --- /dev/null +++ b/.github/persona-brain/memory.json @@ -0,0 +1,37 @@ +{ + "identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体", + "rules_version": "v1.0", + "last_updated": "2026-03-05T14:46:10.743Z", + "wake_protocol_version": "v1.0", + "wake_triggers": [ + "我是冰朔", + "冰朔", + "我是妈妈", + "唤醒铸渊", + "铸渊,醒来" + ], + "founder": "冰朔(Bīng Shuò)", + "hli_coverage": "17.6%", + "total_selfchecks": 2, + "total_ci_runs": 1, + "recent_events": [ + { + "date": "2026-03-05", + "type": "daily_selfcheck", + "description": "每日自检完成 · 知识库4条 · 缺失文件4个", + "by": "铸渊自检" + }, + { + "date": "2026-03-05", + "type": "daily_selfcheck", + "description": "每日自检完成 · 知识库4条 · 缺失文件4个", + "by": "铸渊自检" + }, + { + "date": "2026-03-05", + "type": "system_init", + "description": "Issue自动回复系统初始化 · dev-status.json + knowledge-base.json + workflows 全部就绪", + "by": "铸渊" + } + ] +} \ No newline at end of file diff --git a/.github/workflows/zhuyuan-daily-selfcheck.yml b/.github/workflows/zhuyuan-daily-selfcheck.yml new file mode 100644 index 00000000..5bb4922b --- /dev/null +++ b/.github/workflows/zhuyuan-daily-selfcheck.yml @@ -0,0 +1,32 @@ +name: 铸渊 · 每日自检与自进化 + +on: + schedule: + - cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00 + workflow_dispatch: # 手动触发 + +jobs: + self-check: + name: 🔍 铸渊每日自检 + runs-on: ubuntu-latest + permissions: + contents: write + issues: read + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 铸渊自检 + run: node scripts/zhuyuan-daily-selfcheck.js + + - name: 提交自检结果 + run: | + git config user.name "铸渊 (ZhùYuān)" + git config user.email "zhuyuan@guanghulab.com" + git add .github/persona-brain/ + git diff --cached --quiet || git commit -m "🔍 铸渊每日自检 · $(date +%Y-%m-%d)" + git push diff --git a/.github/workflows/zhuyuan-issue-reply.yml b/.github/workflows/zhuyuan-issue-reply.yml new file mode 100644 index 00000000..ed8c6556 --- /dev/null +++ b/.github/workflows/zhuyuan-issue-reply.yml @@ -0,0 +1,30 @@ +name: 铸渊 · Issue 自动回复 + +on: + issues: + types: [opened] + +jobs: + auto-reply: + name: 🤖 铸渊回答问题 + runs-on: ubuntu-latest + permissions: + issues: write + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 铸渊处理Issue + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + YUNWU_API_KEY: ${{ secrets.YUNWU_API_KEY }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + ISSUE_TITLE: ${{ github.event.issue.title }} + ISSUE_BODY: ${{ github.event.issue.body }} + ISSUE_LABELS: ${{ join(github.event.issue.labels.*.name, ',') }} + run: node scripts/zhuyuan-issue-reply.js diff --git a/scripts/zhuyuan-daily-selfcheck.js b/scripts/zhuyuan-daily-selfcheck.js new file mode 100644 index 00000000..9d1ec6ed --- /dev/null +++ b/scripts/zhuyuan-daily-selfcheck.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); + +// === 最多保留的最近事件条数 === +const MAX_RECENT_EVENTS = 50; + +const BRAIN_DIR = '.github/persona-brain'; + +let memory, kb, journal; +try { + memory = JSON.parse(fs.readFileSync(path.join(BRAIN_DIR, 'memory.json'), 'utf8')); + kb = JSON.parse(fs.readFileSync(path.join(BRAIN_DIR, 'knowledge-base.json'), 'utf8')); + journal = fs.readFileSync(path.join(BRAIN_DIR, 'growth-journal.md'), 'utf8'); +} catch (err) { + console.error('❌ 铸渊大脑文件读取失败:', err.message); + process.exit(1); +} + +const today = new Date().toISOString().split('T')[0]; +console.log(`🔍 铸渊每日自检开始 · ${today}`); + +// === ① 大脑文件完整性检查 === +const requiredFiles = [ + 'identity.md', 'memory.json', 'routing-map.json', + 'responsibility.md', 'decision-log.md', 'growth-journal.md', + 'dev-status.json', 'knowledge-base.json' +]; +const missing = requiredFiles.filter(f => !fs.existsSync(path.join(BRAIN_DIR, f))); +if (missing.length > 0) { + console.error('⚠️ 缺失文件:' + missing.join(', ')); +} else { + console.log('✅ 大脑文件完整性:全部就绪'); +} + +// === ② 知识库去重与整理 === +const seen = new Set(); +const uniqueFaq = kb.faq.filter(item => { + const key = item.q.toLowerCase().trim(); + if (seen.has(key)) return false; + seen.add(key); + return true; +}); +if (uniqueFaq.length < kb.faq.length) { + console.log(`🧹 知识库去重:${kb.faq.length} → ${uniqueFaq.length}`); + kb.faq = uniqueFaq; +} +kb.last_updated = today; +fs.writeFileSync(path.join(BRAIN_DIR, 'knowledge-base.json'), JSON.stringify(kb, null, 2)); + +// === ③ 记忆整理(只保留最近50条事件) === +if (memory.recent_events && memory.recent_events.length > MAX_RECENT_EVENTS) { + memory.recent_events = memory.recent_events.slice(0, MAX_RECENT_EVENTS); + console.log(`🧹 记忆整理:截断到${MAX_RECENT_EVENTS}条最近事件`); +} +memory.last_updated = new Date().toISOString(); +memory.total_selfchecks = (memory.total_selfchecks || 0) + 1; + +if (!memory.recent_events) memory.recent_events = []; +memory.recent_events.unshift({ + date: today, + type: 'daily_selfcheck', + description: `每日自检完成 · 知识库${uniqueFaq.length}条 · 缺失文件${missing.length}个`, + by: '铸渊自检' +}); +fs.writeFileSync(path.join(BRAIN_DIR, 'memory.json'), JSON.stringify(memory, null, 2)); + +// === ④ 成长日记追加 === +const newEntry = `\n## ${today} · 每日自检\n` + + `- 大脑文件完整性:${missing.length === 0 ? '✅' : '⚠️ 缺失 ' + missing.join(', ')}\n` + + `- 知识库条目:${uniqueFaq.length}条\n` + + `- 累计自检次数:${memory.total_selfchecks}\n` + + `- 累计CI运行:${memory.total_ci_runs || 0}次\n` + + `- HLI覆盖率:${memory.hli_coverage || '未知'}\n`; +fs.writeFileSync(path.join(BRAIN_DIR, 'growth-journal.md'), journal + newEntry); + +console.log(`\n🔍 铸渊每日自检完成 · ${today}`); +console.log(`📊 知识库:${uniqueFaq.length}条 · 自检次数:${memory.total_selfchecks}`); diff --git a/scripts/zhuyuan-issue-reply.js b/scripts/zhuyuan-issue-reply.js new file mode 100644 index 00000000..f0307bb1 --- /dev/null +++ b/scripts/zhuyuan-issue-reply.js @@ -0,0 +1,218 @@ +const fs = require('fs'); +const https = require('https'); + +// === 知识库匹配阈值:问题词中至少40%出现在Issue文本中才算命中 === +const FAQ_MATCH_THRESHOLD = 0.4; + +// === 读取铸渊大脑 === +let devStatus, knowledgeBase; +try { + devStatus = JSON.parse(fs.readFileSync('.github/persona-brain/dev-status.json', 'utf8')); + knowledgeBase = JSON.parse(fs.readFileSync('.github/persona-brain/knowledge-base.json', 'utf8')); +} catch (err) { + console.error('❌ 铸渊大脑文件读取失败:', err.message); + process.exit(1); +} + +const issueNumber = process.env.ISSUE_NUMBER; +const issueTitle = process.env.ISSUE_TITLE || ''; +const issueBody = process.env.ISSUE_BODY || ''; +const issueLabels = process.env.ISSUE_LABELS || ''; + +// === 判断Issue类型 === +const isProgressQuery = issueLabels.includes('progress-query'); +const isDevQuestion = issueLabels.includes('dev-question'); + +// === 提取开发者编号 === +const devIdMatch = issueBody.match(/DEV-\d{3}/i); +const devId = devIdMatch ? devIdMatch[0].toUpperCase() : null; +const devInfo = devId ? devStatus.team_status.find(d => d.dev_id === devId) : null; + +// === 构建回复 === +async function generateReply() { + let reply = ''; + + // --- 进度查询(指定开发者)--- + if (isProgressQuery && devInfo) { + reply = `## ⚒️ 铸渊回复 · 进度查询\n\n`; + reply += `**${devInfo.name}(${devInfo.dev_id})当前状态:**\n`; + reply += `- 📌 模块:${devInfo.modules.join('、')}\n`; + reply += `- 📊 状态:${devInfo.status}\n`; + reply += `- ⏳ 等待中:${devInfo.waiting_for}\n`; + reply += `- 👉 下一步:${devInfo.next_step}\n`; + reply += `- 💻 环境:${devInfo.os}\n\n`; + reply += `---\n*数据来源:Notion主控台 · 最后同步时间 ${devStatus.last_synced}*\n`; + reply += `*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('✅answered'); + await removeLabel('pending'); + return; + } + + // --- 进度查询(团队整体)--- + if (isProgressQuery && !devId) { + reply = `## ⚒️ 铸渊回复 · 团队进度总览\n\n`; + devStatus.team_status.forEach(dev => { + reply += `**${dev.dev_id} ${dev.name}** · ${dev.status}\n`; + }); + reply += `\n---\n*最后同步:${devStatus.last_synced}*\n`; + reply += `*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('✅answered'); + await removeLabel('pending'); + return; + } + + // --- 技术问题:先查知识库 --- + if (isDevQuestion) { + const matchedFaq = findInKnowledgeBase(issueTitle + ' ' + issueBody); + + if (matchedFaq) { + reply = `## ⚒️ 铸渊回复\n\n`; + reply += `${matchedFaq.a}\n\n`; + if (matchedFaq.related_broadcast !== '通用') { + reply += `📡 相关广播:${matchedFaq.related_broadcast}\n\n`; + } + reply += `---\n*如果这没解决你的问题,继续在下面留言,霜砚会在下次巡检时补充回答。*\n`; + reply += `*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('✅answered'); + await removeLabel('pending'); + return; + } + + // --- 知识库没有 → 调用云雾API --- + const aiReply = await callYunwuAPI(issueTitle, issueBody, devInfo); + if (aiReply) { + reply = `## ⚒️ 铸渊回复\n\n`; + reply += `${aiReply}\n\n`; + if (devInfo) { + reply += `📌 你当前在做:${devInfo.modules.join('、')} · ${devInfo.status}\n`; + } + reply += `\n---\n*AI生成回答,如有不准确请在下面补充,霜砚巡检时会修正。*\n`; + reply += `*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('✅answered'); + await removeLabel('pending'); + return; + } + + // --- API也答不了 → 等霜砚 --- + reply = `## ⚒️ 铸渊收到\n\n`; + reply += `这个问题我需要查更多资料。已标记为待处理,霜砚会在下次巡检时来回答你。\n\n`; + if (devInfo) { + const routing = devStatus.tech_routing.level_2_peer_help; + reply += `💡 **临时建议**:你也可以先问问同伴开发者:\n`; + Object.entries(routing).forEach(([area, who]) => { + reply += `- ${area}:${who}\n`; + }); + } + reply += `\n---\n*霜砚巡检时间:每天 12:00 和 23:00(北京时间)*\n`; + reply += `*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('⏳waiting-shuangyan'); + await removeLabel('pending'); + return; + } + + // --- 其他类型 --- + reply = `## ⚒️ 铸渊收到\n\n已记录。霜砚会在下次巡检时处理。\n\n*—— 铸渊(ICE-GL-ZY001)*`; + await postComment(reply); + await addLabel('⏳waiting-shuangyan'); +} + +// === 知识库模糊匹配 === +function findInKnowledgeBase(text) { + const keywords = text.toLowerCase(); + for (const faq of knowledgeBase.faq) { + const qWords = faq.q.toLowerCase().split(/\s+/); + const matchCount = qWords.filter(w => keywords.includes(w)).length; + if (matchCount >= qWords.length * FAQ_MATCH_THRESHOLD) return faq; + } + return null; +} + +// === 调用云雾API === +async function callYunwuAPI(title, body, devInfo) { + const apiKey = process.env.YUNWU_API_KEY; + if (!apiKey) return null; + + const systemPrompt = `你是铸渊,光湖系统的GitHub代码守护人格体。你负责回答开发者的技术问题。\n当前开发者信息:${devInfo ? JSON.stringify(devInfo) : '未知'}\n服务器信息:${JSON.stringify(devStatus.server_info)}\n请用简洁、友好的语气回答,给出可直接执行的命令。如果不确定,说明并建议找谁。`; + + const data = JSON.stringify({ + model: 'deepseek-chat', + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: `问题标题:${title}\n问题内容:${body}` } + ], + max_tokens: 1000 + }); + + return new Promise((resolve) => { + const req = https.request({ + hostname: 'api.yunwu.ai', + path: '/v1/chat/completions', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` + } + }, (res) => { + let responseBody = ''; + res.on('data', chunk => { responseBody += chunk; }); + res.on('end', () => { + try { + const json = JSON.parse(responseBody); + resolve(json.choices?.[0]?.message?.content || null); + } catch { resolve(null); } + }); + }); + req.on('error', () => resolve(null)); + req.write(data); + req.end(); + }); +} + +// === GitHub API 工具函数 === +async function postComment(body) { + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + return githubRequest('POST', `/repos/${owner}/${repo}/issues/${issueNumber}/comments`, { body }); +} + +async function addLabel(label) { + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + return githubRequest('POST', `/repos/${owner}/${repo}/issues/${issueNumber}/labels`, { labels: [label] }); +} + +async function removeLabel(label) { + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + // 404 means label wasn't on the issue — that's fine, just ignore it + return githubRequest('DELETE', `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(label)}`).catch(() => null); +} + +function githubRequest(method, path, data) { + return new Promise((resolve) => { + const reqOptions = { + hostname: 'api.github.com', + path: path, + method: method, + headers: { + 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'Zhuyuan-Bot', + 'Content-Type': 'application/json' + } + }; + const req = https.request(reqOptions, (res) => { + let responseBody = ''; + res.on('data', chunk => { responseBody += chunk; }); + res.on('end', () => resolve(responseBody)); + }); + req.on('error', () => resolve(null)); + if (data) req.write(JSON.stringify(data)); + req.end(); + }); +} + +// === 执行 === +generateReply().catch(console.error);