diff --git a/.github/workflows/auto-reply-discussions.yml b/.github/workflows/auto-reply-discussions.yml
new file mode 100644
index 00000000..14d2d9ad
--- /dev/null
+++ b/.github/workflows/auto-reply-discussions.yml
@@ -0,0 +1,58 @@
+name: "🤖 铸渊 · Discussion 自动回复"
+
+on:
+ discussion:
+ types: [created]
+ discussion_comment:
+ types: [created]
+
+permissions:
+ discussions: write
+
+jobs:
+ auto-reply:
+ name: 铸渊自动回复
+ runs-on: ubuntu-latest
+ # 仅在非 bot 触发时运行
+ if: >-
+ github.actor != 'github-actions[bot]' &&
+ github.actor != 'qinfendebingshuo'
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ # ── 处理新 Discussion ──
+ - name: 回复新 Discussion
+ if: github.event_name == 'discussion' && github.event.action == 'created'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ DISCUSSION_ID: ${{ github.event.discussion.number }}
+ DISCUSSION_BODY: ${{ github.event.discussion.body }}
+ DISCUSSION_AUTHOR: ${{ github.event.discussion.user.login }}
+ DISCUSSION_CATEGORY: ${{ github.event.discussion.category.name }}
+ DISCUSSION_NODE_ID: ${{ github.event.discussion.node_id }}
+ IS_COMMENT: 'false'
+ BOT_LOGIN: 'github-actions[bot]'
+ run: node scripts/auto-reply-discussions.js
+
+ # ── 处理新评论 ──
+ - name: 回复新评论
+ if: github.event_name == 'discussion_comment' && github.event.action == 'created'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ DISCUSSION_ID: ${{ github.event.discussion.number }}
+ DISCUSSION_BODY: ${{ github.event.comment.body }}
+ DISCUSSION_AUTHOR: ${{ github.event.comment.user.login }}
+ DISCUSSION_CATEGORY: ${{ github.event.discussion.category.name }}
+ DISCUSSION_NODE_ID: ${{ github.event.discussion.node_id }}
+ IS_COMMENT: 'true'
+ BOT_LOGIN: 'github-actions[bot]'
+ run: node scripts/auto-reply-discussions.js
diff --git a/.github/workflows/daily-report.yml b/.github/workflows/daily-report.yml
new file mode 100644
index 00000000..7ea2156b
--- /dev/null
+++ b/.github/workflows/daily-report.yml
@@ -0,0 +1,48 @@
+name: "📰 铸渊 · 光湖开发日报"
+
+on:
+ schedule:
+ - cron: '0 2 * * *' # UTC 02:00 = 北京时间 10:00
+ workflow_dispatch: # 支持手动触发
+
+permissions:
+ contents: write
+ discussions: write
+
+jobs:
+ daily-report:
+ name: 生成并发布开发日报
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 50 # 获取最近提交用于日报亮点
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ # ── 生成并发布日报 ──
+ - name: 生成开发日报
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ DISCUSSION_CATEGORY: "Announcements"
+ run: node scripts/generate-daily-report.js
+
+ # ── 提交日报存档 ──
+ - name: 提交日报存档
+ run: |
+ git config user.name "铸渊 (ZhùYuān)"
+ git config user.email "zhuyuan@guanghulab.com"
+ git add docs/daily-reports/
+ if git diff --cached --quiet; then
+ echo "📌 无日报文件需要提交"
+ else
+ git commit -m "📰 光湖开发日报 · $(TZ='Asia/Shanghai' date '+%Y-%m-%d') [skip ci]"
+ git push
+ echo "✅ 日报存档已提交"
+ fi
diff --git a/.github/workflows/sync-dev-status.yml b/.github/workflows/sync-dev-status.yml
new file mode 100644
index 00000000..b531528f
--- /dev/null
+++ b/.github/workflows/sync-dev-status.yml
@@ -0,0 +1,45 @@
+name: "📡 铸渊 · dev-status 自动同步"
+
+on:
+ schedule:
+ - cron: '0 1 * * *' # UTC 01:00 = 北京时间 09:00
+ - cron: '0 14 * * *' # UTC 14:00 = 北京时间 22:00
+ workflow_dispatch: # 支持手动触发
+
+permissions:
+ contents: write
+
+jobs:
+ sync-dev-status:
+ name: 同步 dev-status.json
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ # ── 执行同步脚本 ──
+ - name: 同步 dev-status.json
+ env:
+ NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
+ DEV_STATUS_DB_ID: ${{ secrets.DEV_STATUS_DB_ID }}
+ run: node scripts/sync-dev-status.js
+
+ # ── 提交更新 ──
+ - name: 提交 dev-status 更新
+ run: |
+ git config user.name "铸渊 (ZhùYuān)"
+ git config user.email "zhuyuan@guanghulab.com"
+ git add .github/persona-brain/dev-status.json dev-status.json
+ if git diff --cached --quiet; then
+ echo "📌 dev-status.json 无变更"
+ else
+ git commit -m "📡 dev-status 同步 · $(TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M') [skip ci]"
+ git push
+ echo "✅ dev-status.json 已同步并推送"
+ fi
diff --git a/README.md b/README.md
index defb282a..0fdaef59 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,19 @@
-# 🌊 光湖 HoloLake
+# 🌊 HoloLake · 光湖纪元
-**人格语言操作系统(AGE OS)· 壳-核分离架构**
+**第五代人工智能语言人格高级智能平台**
+[](https://github.com/qinfendebingshuo/guanghulab)
+[](https://github.com/qinfendebingshuo/guanghulab/discussions)
[](https://github.com/qinfendebingshuo/guanghulab/actions/workflows/zhuyuan-daily-selfcheck.yml)
[](https://github.com/qinfendebingshuo/guanghulab/actions/workflows/deploy-to-server.yml)
+[](LICENSE)
+
+🏛️ 通感语言核系统编程语言 · 作品著作权认证(中国版权保护中心)
+📋 多项软件著作权申请中 · ™️ 商标申请中
+
+[📊 开发进度](#-系统状态) · [💬 留言互动](https://github.com/qinfendebingshuo/guanghulab/discussions/categories/访客留言板) · [📢 开发日报](https://github.com/qinfendebingshuo/guanghulab/discussions/categories/announcements)
`guanghulab.com` · Node.js 20 + Express + PM2 + Nginx
@@ -278,10 +286,22 @@ npm run test:smoke
---
+## 💬 联系我们
+
+有任何问题、建议、或者只是想打个招呼?
+
+→ [访客留言板](https://github.com/qinfendebingshuo/guanghulab/discussions/categories/访客留言板) · 铸渊(AI守护者)会自动回复你!
+
+→ [📢 开发日报](https://github.com/qinfendebingshuo/guanghulab/discussions/categories/announcements) · 每日发布团队最新进展
+
+---
+
**光湖 HoloLake** · 由冰朔创建 · 铸渊守护
*壳-核分离 · 人格共生 · 协作共建*
+🏛️ 通感语言核系统编程语言 · 作品著作权认证(中国版权保护中心)
+
diff --git a/scripts/auto-reply-discussions.js b/scripts/auto-reply-discussions.js
new file mode 100644
index 00000000..9229bca9
--- /dev/null
+++ b/scripts/auto-reply-discussions.js
@@ -0,0 +1,298 @@
+/**
+ * auto-reply-discussions.js
+ * 铸渊自动回复 Discussions 留言
+ *
+ * 使用方式:
+ * GITHUB_TOKEN=xxx GITHUB_REPOSITORY=owner/repo \
+ * DISCUSSION_ID=xxx DISCUSSION_BODY=xxx DISCUSSION_AUTHOR=xxx \
+ * DISCUSSION_CATEGORY=xxx DISCUSSION_NODE_ID=xxx \
+ * IS_COMMENT=true/false COMMENT_NODE_ID=xxx \
+ * node scripts/auto-reply-discussions.js
+ */
+
+const https = require('https');
+const fs = require('fs');
+const path = require('path');
+
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
+const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY || '';
+const DISCUSSION_BODY = process.env.DISCUSSION_BODY || '';
+const DISCUSSION_AUTHOR = process.env.DISCUSSION_AUTHOR || '';
+const DISCUSSION_CATEGORY = process.env.DISCUSSION_CATEGORY || '';
+const DISCUSSION_NODE_ID = process.env.DISCUSSION_NODE_ID || '';
+const IS_COMMENT = process.env.IS_COMMENT === 'true';
+const BOT_LOGIN = process.env.BOT_LOGIN || 'github-actions[bot]';
+
+// 频率限制文件
+const RATE_LIMIT_FILE = '/tmp/discussion-reply-rate.json';
+
+// ── 签名 ──
+const SIGNATURE = `\n\n—— 🐙 铸渊 · HoloLake AI Guardian\n自动回复 · 如需人工帮助请 @qinfendebingshuo`;
+
+// ── GraphQL 请求 ──
+function graphqlRequest(query, variables = {}) {
+ return new Promise((resolve, reject) => {
+ const data = JSON.stringify({ query, variables });
+ const options = {
+ hostname: 'api.github.com',
+ path: '/graphql',
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${GITHUB_TOKEN}`,
+ 'Content-Type': 'application/json',
+ 'User-Agent': 'guanghulab-auto-reply',
+ 'Content-Length': Buffer.byteLength(data),
+ },
+ };
+
+ const req = https.request(options, (res) => {
+ let result = '';
+ res.on('data', (chunk) => result += chunk);
+ res.on('end', () => {
+ try {
+ resolve(JSON.parse(result));
+ } catch {
+ reject(new Error(`GraphQL 解析失败: ${result.slice(0, 200)}`));
+ }
+ });
+ });
+ req.on('error', reject);
+ req.write(data);
+ req.end();
+ });
+}
+
+// ── 添加 Discussion 评论 ──
+async function addDiscussionComment(discussionId, body) {
+ const mutation = `
+ mutation($discussionId: ID!, $body: String!) {
+ addDiscussionComment(input: {
+ discussionId: $discussionId,
+ body: $body
+ }) {
+ comment {
+ id
+ url
+ }
+ }
+ }
+ `;
+ return graphqlRequest(mutation, { discussionId, body });
+}
+
+// ── 频率限制检查 ──
+function checkRateLimit(author) {
+ let limits = {};
+ try {
+ if (fs.existsSync(RATE_LIMIT_FILE)) {
+ limits = JSON.parse(fs.readFileSync(RATE_LIMIT_FILE, 'utf8'));
+ }
+ } catch {
+ limits = {};
+ }
+
+ const now = Date.now();
+ const dayAgo = now - 24 * 60 * 60 * 1000;
+
+ // 清理过期记录
+ if (limits[author]) {
+ limits[author] = limits[author].filter(ts => ts > dayAgo);
+ }
+
+ const count = (limits[author] || []).length;
+ if (count >= 3) {
+ console.log(`⚠️ ${author} 24h内已回复${count}次,跳过`);
+ return false;
+ }
+
+ // 记录本次回复
+ if (!limits[author]) limits[author] = [];
+ limits[author].push(now);
+ fs.writeFileSync(RATE_LIMIT_FILE, JSON.stringify(limits, null, 2));
+ return true;
+}
+
+// ── 垃圾内容检测 ──
+function isSpam(body) {
+ if (!body || body.trim().length < 3) return true;
+
+ // 纯链接
+ const urlPattern = /^https?:\/\/\S+$/;
+ if (urlPattern.test(body.trim())) return true;
+
+ // 重复字符
+ if (/^(.)\1{10,}$/.test(body.trim())) return true;
+
+ // 常见广告词
+ const spamKeywords = ['casino', 'viagra', 'lottery', 'click here to win', 'free money'];
+ const lower = body.toLowerCase();
+ if (spamKeywords.some(kw => lower.includes(kw))) return true;
+
+ return false;
+}
+
+// ── 意图分类 ──
+function classifyIntent(body) {
+ const lower = (body || '').toLowerCase();
+ const text = body || '';
+
+ // 问项目是什么
+ if (/什么(项目|系统|平台)|做什么|是什么|what is|what does|about this/i.test(text)) {
+ return 'about_project';
+ }
+
+ // 问技术细节
+ if (/架构|协议|模块|技术|实现|源码|code|architecture|protocol|api|how does/i.test(text)) {
+ return 'technical';
+ }
+
+ // 想参与开发
+ if (/参与|加入|贡献|contribute|join|help|想.*开发/i.test(text)) {
+ return 'want_to_join';
+ }
+
+ // 提 Bug 或建议
+ if (/bug|问题|错误|建议|suggest|issue|feature|request|improvement|修复|fix/i.test(text)) {
+ return 'bug_or_suggestion';
+ }
+
+ // 打招呼 / 点赞
+ if (/你好|hello|hi|hey|awesome|cool|great|棒|厉害|赞|star|支持|nice|love|喜欢/i.test(text)) {
+ return 'greeting';
+ }
+
+ return 'unknown';
+}
+
+// ── 生成回复内容 ──
+function generateReply(intent) {
+ const replies = {
+ about_project: `🌊 **光湖纪元(HoloLake)** 是第五代人工智能语言人格高级智能平台。
+
+我们正在构建一套由 AI 人格体驱动的分布式协作开发系统,目前有 11 位开发者协同推进 47+ 个功能模块。
+
+通感语言核系统编程语言已获 **中国版权保护中心作品著作权认证**。
+
+核心特色:
+- 🤖 三位 AI 人格体(铸渊/冰朔/霜砚)协同守护
+- 📡 广播-回执闭环协作协议
+- 🧠 壳-核分离架构设计
+
+⭐ 感兴趣的话,欢迎 Star 关注我们的进展!`,
+
+ technical: `感谢你对技术细节的关注!🔍
+
+光湖采用壳-核分离架构:
+- **壳 (Shell)**: 前端交互层 — 对话 UI、用户中心、工单系统
+- **核 (Core)**: 后端智能层 — 人格引擎、广播分发、信号处理
+
+更多信息可以查看仓库的 [README](https://github.com/qinfendebingshuo/guanghulab) 和 \`docs/\` 目录。
+
+如果有更具体的技术问题,欢迎继续提问!`,
+
+ want_to_join: `太好了!🎉 我们欢迎新的开发者加入光湖大家庭!
+
+目前团队有 11 位协作者,每位负责不同的功能模块,通过广播-回执协议进行协作。
+
+参与方式:
+1. ⭐ 先 Star 本仓库关注动态
+2. 📖 阅读 README 了解项目架构
+3. 💬 在这里留下你感兴趣的方向,团队会联系你!
+
+期待你的加入!`,
+
+ bug_or_suggestion: `感谢你的反馈!🙏
+
+为了更好地追踪和处理,建议你:
+1. 📋 [点击这里创建一个 Issue](https://github.com/qinfendebingshuo/guanghulab/issues/new)
+2. 描述你遇到的问题或建议
+3. 铸渊会自动处理并回复
+
+Issue 的方式可以让我们更好地跟进进展。感谢你帮助光湖变得更好!`,
+
+ greeting: `谢谢关注光湖!🌊
+
+很高兴你来到这里!我们是一个正在快速发展的 AI 人格体协作开发平台。
+
+有任何问题随时聊~
+如果觉得有意思,⭐ Star 一下是对我们最大的支持!
+
+欢迎常来逛逛 😊`,
+
+ unknown: `你好!感谢你的留言 🌊
+
+我是铸渊,光湖纪元的 AI 守护者。如果你有任何关于项目的问题,欢迎继续提问!
+
+常见话题:
+- 📖 项目介绍和架构
+- 🤖 AI 人格体系统
+- 🔧 技术实现细节
+- 🤝 如何参与开发
+
+⭐ 也欢迎给我们 Star 支持!`,
+ };
+
+ return (replies[intent] || replies.unknown) + SIGNATURE;
+}
+
+// ── 主流程 ──
+async function main() {
+ console.log('🤖 铸渊自动回复检查...');
+
+ // 过滤条件:跳过自己的帖子
+ if (DISCUSSION_AUTHOR === BOT_LOGIN || DISCUSSION_AUTHOR === 'qinfendebingshuo') {
+ console.log(`⏭️ 跳过:作者是 ${DISCUSSION_AUTHOR}(避免自回复循环)`);
+ return;
+ }
+
+ // 跳过 Announcements 分类
+ if (DISCUSSION_CATEGORY === 'Announcements' || DISCUSSION_CATEGORY === '📢 Announcements') {
+ console.log('⏭️ 跳过:Announcements 分类不自动回复');
+ return;
+ }
+
+ // 只回复指定分类
+ const allowedCategories = ['访客留言板', '💬 访客留言板', 'Ideas', '💡 Ideas', 'Q&A', 'General'];
+ if (!allowedCategories.some(c => DISCUSSION_CATEGORY.includes(c))) {
+ console.log(`⏭️ 跳过:分类 "${DISCUSSION_CATEGORY}" 不在自动回复范围`);
+ return;
+ }
+
+ // 垃圾内容检测
+ if (isSpam(DISCUSSION_BODY)) {
+ console.log('🚫 检测到垃圾内容,跳过回复');
+ return;
+ }
+
+ // 频率限制
+ if (!checkRateLimit(DISCUSSION_AUTHOR)) {
+ return;
+ }
+
+ // 意图分类
+ const intent = classifyIntent(DISCUSSION_BODY);
+ console.log(`📝 内容: ${DISCUSSION_BODY.slice(0, 100)}...`);
+ console.log(`🎯 意图分类: ${intent}`);
+
+ // 生成回复
+ const reply = generateReply(intent);
+
+ // 发送回复
+ if (!GITHUB_TOKEN || !DISCUSSION_NODE_ID) {
+ console.log('⚠️ 缺少 GITHUB_TOKEN 或 DISCUSSION_NODE_ID,输出回复内容:');
+ console.log(reply);
+ return;
+ }
+
+ const result = await addDiscussionComment(DISCUSSION_NODE_ID, reply);
+ if (result?.data?.addDiscussionComment?.comment?.url) {
+ console.log(`✅ 已回复: ${result.data.addDiscussionComment.comment.url}`);
+ } else {
+ console.log('⚠️ 回复结果:', JSON.stringify(result?.errors || result).slice(0, 500));
+ }
+}
+
+main().catch(err => {
+ console.error('❌ 自动回复失败:', err.message);
+ process.exit(1);
+});
diff --git a/scripts/generate-daily-report.js b/scripts/generate-daily-report.js
new file mode 100644
index 00000000..d2e16ac8
--- /dev/null
+++ b/scripts/generate-daily-report.js
@@ -0,0 +1,289 @@
+/**
+ * generate-daily-report.js
+ * 生成「光湖开发日报」内容,并通过 GitHub GraphQL API 发布到 Discussions
+ *
+ * 使用方式:
+ * GITHUB_TOKEN=xxx GITHUB_REPOSITORY=owner/repo node scripts/generate-daily-report.js
+ *
+ * 环境变量:
+ * GITHUB_TOKEN - GitHub token (需要 discussions:write 权限)
+ * GITHUB_REPOSITORY - 仓库名 (owner/repo)
+ * DISCUSSION_CATEGORY - Discussions 分类名 (默认: Announcements)
+ */
+
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+const { execSync } = require('child_process');
+
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
+const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY || '';
+const DISCUSSION_CATEGORY = process.env.DISCUSSION_CATEGORY || 'Announcements';
+
+const DEV_STATUS_PATH = path.join('.github', 'persona-brain', 'dev-status.json');
+
+// ── GitHub GraphQL API 请求 ──
+function graphqlRequest(query, variables = {}) {
+ return new Promise((resolve, reject) => {
+ const data = JSON.stringify({ query, variables });
+ const options = {
+ hostname: 'api.github.com',
+ path: '/graphql',
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${GITHUB_TOKEN}`,
+ 'Content-Type': 'application/json',
+ 'User-Agent': 'guanghulab-daily-report',
+ 'Content-Length': Buffer.byteLength(data),
+ },
+ };
+
+ const req = https.request(options, (res) => {
+ let result = '';
+ res.on('data', (chunk) => result += chunk);
+ res.on('end', () => {
+ try {
+ resolve(JSON.parse(result));
+ } catch {
+ reject(new Error(`GraphQL 解析失败: ${result.slice(0, 200)}`));
+ }
+ });
+ });
+ req.on('error', reject);
+ req.write(data);
+ req.end();
+ });
+}
+
+// ── 获取 Discussion 分类 ID ──
+async function getCategoryId(owner, repo, categoryName) {
+ const query = `
+ query($owner: String!, $repo: String!) {
+ repository(owner: $owner, name: $repo) {
+ discussionCategories(first: 20) {
+ nodes {
+ id
+ name
+ }
+ }
+ }
+ }
+ `;
+ const result = await graphqlRequest(query, { owner, repo });
+ const categories = result?.data?.repository?.discussionCategories?.nodes || [];
+ const category = categories.find(c => c.name === categoryName);
+ return category?.id || null;
+}
+
+// ── 获取仓库 ID ──
+async function getRepositoryId(owner, repo) {
+ const query = `
+ query($owner: String!, $repo: String!) {
+ repository(owner: $owner, name: $repo) {
+ id
+ }
+ }
+ `;
+ const result = await graphqlRequest(query, { owner, repo });
+ return result?.data?.repository?.id || null;
+}
+
+// ── 创建 Discussion ──
+async function createDiscussion(repoId, categoryId, title, body) {
+ const mutation = `
+ mutation($repoId: ID!, $categoryId: ID!, $title: String!, $body: String!) {
+ createDiscussion(input: {
+ repositoryId: $repoId,
+ categoryId: $categoryId,
+ title: $title,
+ body: $body
+ }) {
+ discussion {
+ id
+ url
+ }
+ }
+ }
+ `;
+ return graphqlRequest(mutation, { repoId, categoryId, title, body });
+}
+
+// ── 获取最近 24h 的 Git 活动 ──
+function getRecentActivity() {
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
+ let commits = [];
+ let prActivity = '';
+
+ try {
+ const log = execSync(
+ `git log --since="${since}" --oneline --no-merges 2>/dev/null || true`,
+ { encoding: 'utf8', timeout: 10000 }
+ ).trim();
+ if (log) {
+ commits = log.split('\n').filter(Boolean).slice(0, 10);
+ }
+ } catch {
+ // Git log may fail in shallow clone
+ }
+
+ try {
+ const merges = execSync(
+ `git log --since="${since}" --oneline --merges 2>/dev/null || true`,
+ { encoding: 'utf8', timeout: 10000 }
+ ).trim();
+ if (merges) {
+ prActivity = merges.split('\n').filter(Boolean).slice(0, 5).join('\n');
+ }
+ } catch {
+ // Ignore errors
+ }
+
+ return { commits, prActivity };
+}
+
+// ── 生成日报 Markdown ──
+function generateReport(devStatus, activity) {
+ const today = new Date();
+ const bjDate = new Date(today.getTime() + 8 * 60 * 60 * 1000);
+ const dateStr = bjDate.toISOString().split('T')[0];
+ const team = devStatus.team || [];
+
+ // 团队状态表格
+ let teamTable = '| 开发者 | 当前模块 | 连胜 | 状态 |\n| --- | --- | --- | --- |\n';
+ team.forEach(dev => {
+ const statusEmoji = {
+ 'active': '🟢 活跃',
+ 'waiting_syslog': '🟡 等待SYSLOG',
+ 'waiting_broadcast': '🔵 等待广播',
+ 'paused': '⏸️ 暂停',
+ };
+ const status = statusEmoji[dev.status] || dev.status;
+ const streak = dev.streak > 0 ? `🔥${dev.streak}` : '—';
+ teamTable += `| ${dev.name} | ${dev.module} | ${streak} | ${status} |\n`;
+ });
+
+ // 今日亮点
+ let highlights = '';
+ if (activity.commits.length > 0) {
+ highlights += activity.commits.slice(0, 5).map(c => `· ${c}`).join('\n') + '\n';
+ }
+ // 连胜记录
+ const topStreakers = team.filter(d => d.streak >= 5).sort((a, b) => b.streak - a.streak);
+ if (topStreakers.length > 0) {
+ highlights += topStreakers.map(d => `· 🔥 ${d.name} ${d.streak}连胜 — ${d.module}`).join('\n') + '\n';
+ }
+ if (!highlights) {
+ highlights = '· 团队持续推进中,期待新的突破!\n';
+ }
+
+ // 项目概览
+ const activeDevs = team.filter(d => d.status !== 'paused').length;
+ const summary = devStatus.summary || {};
+ const graduatedCount = summary.graduated_modules?.length || 0;
+
+ const report = `# 🌊 光湖开发日报 · ${dateStr}
+
+## 📊 团队状态
+${teamTable}
+## 🔥 今日亮点
+${highlights}
+## 🏗️ 项目概览
+· 活跃开发者:${activeDevs}人
+· 最高连胜:${summary.top_streak || '无'}
+· 代码仓库:[guanghulab](https://github.com/qinfendebingshuo/guanghulab)
+· 🏛️ 通感语言核系统编程语言 · 作品著作权认证(中国版权保护中心)
+
+---
+⭐ 觉得有意思?给个 Star 支持一下!
+💬 有问题或想聊聊?欢迎到 [访客留言板](https://github.com/qinfendebingshuo/guanghulab/discussions/categories/访客留言板) 留言!
+`;
+
+ return { title: `🌊 光湖开发日报 · ${dateStr}`, body: report };
+}
+
+// ── 主流程 ──
+async function main() {
+ console.log('📰 开始生成光湖开发日报...');
+
+ // 读取 dev-status
+ let devStatus = {};
+ try {
+ devStatus = JSON.parse(fs.readFileSync(DEV_STATUS_PATH, 'utf8'));
+ } catch {
+ console.log('⚠️ dev-status.json 读取失败,使用空数据');
+ }
+
+ // 获取 Git 活动
+ const activity = getRecentActivity();
+
+ // 生成日报内容
+ const { title, body } = generateReport(devStatus, activity);
+
+ console.log(`📝 日报标题: ${title}`);
+ console.log('---');
+ console.log(body.slice(0, 500) + '...');
+
+ // 保存日报到本地(供调试和存档)
+ const reportDir = 'docs/daily-reports';
+ if (!fs.existsSync(reportDir)) {
+ fs.mkdirSync(reportDir, { recursive: true });
+ }
+ const bjDate = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().split('T')[0];
+ fs.writeFileSync(path.join(reportDir, `${bjDate}.md`), `# ${title}\n\n${body}`);
+ console.log(`✅ 日报已保存到 ${reportDir}/${bjDate}.md`);
+
+ // 发布到 Discussions
+ if (!GITHUB_TOKEN || !GITHUB_REPOSITORY) {
+ console.log('⚠️ GITHUB_TOKEN 或 GITHUB_REPOSITORY 未配置,跳过 Discussion 发布');
+ return;
+ }
+
+ const [owner, repo] = GITHUB_REPOSITORY.split('/');
+ if (!owner || !repo) {
+ console.log('⚠️ GITHUB_REPOSITORY 格式错误,应为 owner/repo');
+ return;
+ }
+
+ // 获取仓库和分类 ID
+ let repoId;
+ try {
+ repoId = await getRepositoryId(owner, repo);
+ } catch (err) {
+ console.log(`⚠️ 无法连接 GitHub API: ${err.message},跳过 Discussion 发布`);
+ return;
+ }
+ if (!repoId) {
+ console.log('⚠️ 无法获取仓库 ID,跳过 Discussion 发布');
+ return;
+ }
+
+ let categoryId;
+ try {
+ categoryId = await getCategoryId(owner, repo, DISCUSSION_CATEGORY);
+ } catch (err) {
+ console.log(`⚠️ 获取分类失败: ${err.message},跳过 Discussion 发布`);
+ return;
+ }
+ if (!categoryId) {
+ console.log(`⚠️ 未找到 "${DISCUSSION_CATEGORY}" 分类,跳过 Discussion 发布`);
+ console.log(' 请先在仓库 Settings → Discussions 中创建该分类');
+ return;
+ }
+
+ // 创建 Discussion
+ try {
+ const result = await createDiscussion(repoId, categoryId, title, body);
+ if (result?.data?.createDiscussion?.discussion?.url) {
+ console.log(`✅ 日报已发布: ${result.data.createDiscussion.discussion.url}`);
+ } else {
+ console.log('⚠️ Discussion 创建结果:', JSON.stringify(result?.errors || result).slice(0, 500));
+ }
+ } catch (err) {
+ console.log(`⚠️ Discussion 发布失败: ${err.message}`);
+ }
+}
+
+main().catch(err => {
+ console.error('❌ 日报生成失败:', err.message);
+ process.exit(1);
+});
diff --git a/scripts/selfcheck.js b/scripts/selfcheck.js
index d6f911e3..684347f4 100644
--- a/scripts/selfcheck.js
+++ b/scripts/selfcheck.js
@@ -44,13 +44,43 @@ if (fs.existsSync(broadcastDir)) {
}
console.log(`📬 待处理广播: ${pendingBroadcasts}`);
-// 4. 更新 memory.json
+// 4. dev-status.json 同步时效检查
+let devStatusAlert = '';
+const devStatusPath = path.join(BRAIN, 'dev-status.json');
+if (fs.existsSync(devStatusPath)) {
+ try {
+ const devStatus = JSON.parse(fs.readFileSync(devStatusPath, 'utf8'));
+ if (devStatus.last_sync) {
+ const lastSync = new Date(devStatus.last_sync);
+ const now = new Date();
+ const hoursAgo = Math.round((now - lastSync) / (1000 * 60 * 60));
+ if (hoursAgo > 24) {
+ devStatusAlert = `⚠️ dev-status.json 同步中断·已超${hoursAgo}h·请排查`;
+ console.log(devStatusAlert);
+ } else {
+ console.log(`✅ dev-status.json 同步正常 · ${hoursAgo}h 前更新`);
+ }
+ } else {
+ devStatusAlert = '⚠️ dev-status.json 缺少 last_sync 字段';
+ console.log(devStatusAlert);
+ }
+ } catch {
+ devStatusAlert = '⚠️ dev-status.json 解析失败';
+ console.log(devStatusAlert);
+ }
+} else {
+ devStatusAlert = '⚠️ dev-status.json 文件缺失';
+ console.log(devStatusAlert);
+}
+
+// 5. 更新 memory.json
let memory = JSON.parse(fs.readFileSync(path.join(BRAIN, 'memory.json'), 'utf8'));
memory.daily_selfcheck = {
last_run: new Date().toISOString(),
brain_integrity: brainOK ? 'ok' : 'missing_files',
schema_coverage: schemaCount + '/17',
- pending_broadcasts: pendingBroadcasts
+ pending_broadcasts: pendingBroadcasts,
+ dev_status_sync: devStatusAlert || 'ok'
};
fs.writeFileSync(path.join(BRAIN, 'memory.json'), JSON.stringify(memory, null, 2));
diff --git a/scripts/sync-dev-status.js b/scripts/sync-dev-status.js
new file mode 100644
index 00000000..e221c7d1
--- /dev/null
+++ b/scripts/sync-dev-status.js
@@ -0,0 +1,213 @@
+/**
+ * sync-dev-status.js
+ * 从 Notion 主控台同步 dev-status.json 到 GitHub 仓库
+ *
+ * 使用方式:
+ * NOTION_TOKEN=xxx DEV_STATUS_DB_ID=xxx node scripts/sync-dev-status.js
+ *
+ * 如果 Notion 环境变量不可用,将基于本地文件刷新 last_sync 时间戳
+ */
+
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+
+const DEV_STATUS_PATH = path.join('.github', 'persona-brain', 'dev-status.json');
+const ROOT_STATUS_PATH = 'dev-status.json';
+
+const NOTION_TOKEN = process.env.NOTION_TOKEN || '';
+const DEV_STATUS_DB_ID = process.env.DEV_STATUS_DB_ID || '';
+
+// ── Notion API 请求封装 ──
+function notionRequest(method, endpoint, body) {
+ return new Promise((resolve, reject) => {
+ const data = body ? JSON.stringify(body) : null;
+ const options = {
+ hostname: 'api.notion.com',
+ path: `/v1/${endpoint}`,
+ method,
+ headers: {
+ 'Authorization': `Bearer ${NOTION_TOKEN}`,
+ 'Notion-Version': '2022-06-28',
+ 'Content-Type': 'application/json',
+ },
+ };
+ if (data) options.headers['Content-Length'] = Buffer.byteLength(data);
+
+ const req = https.request(options, (res) => {
+ let result = '';
+ res.on('data', (chunk) => result += chunk);
+ res.on('end', () => {
+ try {
+ resolve({ status: res.statusCode, data: JSON.parse(result) });
+ } catch {
+ resolve({ status: res.statusCode, data: result });
+ }
+ });
+ });
+ req.on('error', reject);
+ if (data) req.write(data);
+ req.end();
+ });
+}
+
+// ── 从 Notion 数据库查询开发者状态 ──
+async function fetchDevStatusFromNotion() {
+ if (!NOTION_TOKEN || !DEV_STATUS_DB_ID) {
+ console.log('⚠️ NOTION_TOKEN 或 DEV_STATUS_DB_ID 未配置,跳过 Notion 同步');
+ return null;
+ }
+
+ console.log('📡 正在从 Notion 查询开发者状态...');
+
+ const response = await notionRequest('POST', `databases/${DEV_STATUS_DB_ID}/query`, {
+ sorts: [{ property: '编号', direction: 'ascending' }],
+ });
+
+ if (response.status !== 200) {
+ console.error(`❌ Notion API 返回 ${response.status}:`, JSON.stringify(response.data).slice(0, 500));
+ return null;
+ }
+
+ const pages = response.data.results || [];
+ if (pages.length === 0) {
+ console.log('⚠️ Notion 数据库为空,跳过同步');
+ return null;
+ }
+
+ console.log(`✅ 从 Notion 获取到 ${pages.length} 条开发者记录`);
+
+ // 解析 Notion 页面属性为 dev-status 格式
+ const team = pages.map(page => {
+ const props = page.properties || {};
+ return {
+ dev_id: getNotionText(props['编号']) || getNotionTitle(props['Name']) || '',
+ name: getNotionText(props['昵称']) || getNotionTitle(props['Name']) || '',
+ module: getNotionText(props['当前模块']) || '',
+ status: mapNotionStatus(getNotionSelect(props['状态'])),
+ current: getNotionText(props['当前进度']) || '',
+ waiting: getNotionText(props['等待项']) || '',
+ streak: getNotionNumber(props['连胜']) || 0,
+ };
+ }).filter(d => d.dev_id);
+
+ return team;
+}
+
+// ── Notion 属性解析辅助函数 ──
+function getNotionText(prop) {
+ if (!prop) return '';
+ if (prop.type === 'rich_text') return (prop.rich_text || []).map(t => t.plain_text).join('');
+ if (prop.type === 'title') return (prop.title || []).map(t => t.plain_text).join('');
+ return '';
+}
+
+function getNotionTitle(prop) {
+ if (!prop) return '';
+ if (prop.type === 'title') return (prop.title || []).map(t => t.plain_text).join('');
+ return '';
+}
+
+function getNotionSelect(prop) {
+ if (!prop || prop.type !== 'select') return '';
+ return prop.select?.name || '';
+}
+
+function getNotionNumber(prop) {
+ if (!prop || prop.type !== 'number') return 0;
+ return prop.number || 0;
+}
+
+function mapNotionStatus(status) {
+ const map = {
+ '活跃': 'active',
+ '等待SYSLOG': 'waiting_syslog',
+ '等待广播': 'waiting_broadcast',
+ '暂停': 'paused',
+ '已毕业': 'graduated',
+ };
+ return map[status] || status || 'active';
+}
+
+// ── 生成汇总信息 ──
+function generateSummary(team) {
+ const activeCount = team.filter(d => d.status === 'active').length;
+ const waitingSyslog = team.filter(d => d.status === 'waiting_syslog').length;
+ const topStreak = team.reduce((max, d) => d.streak > max.streak ? d : max, { streak: 0 });
+ const alerts = team
+ .filter(d => d.waiting && d.waiting.includes('⚠️'))
+ .map(d => `${d.dev_id} ${d.name}: ${d.waiting}`);
+
+ return {
+ total_devs: team.length,
+ active_waiting_syslog: waitingSyslog,
+ active_normal: activeCount,
+ top_streak: topStreak.dev_id ? `${topStreak.dev_id} ${topStreak.name} ${topStreak.streak}连胜` : '无',
+ alerts,
+ };
+}
+
+// ── 主流程 ──
+async function main() {
+ const now = new Date();
+ const bjTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
+ const timestamp = bjTime.toISOString().replace('Z', '+08:00');
+
+ console.log(`🔄 dev-status.json 同步开始 · ${timestamp}`);
+
+ // 读取当前状态
+ let currentStatus = {};
+ try {
+ currentStatus = JSON.parse(fs.readFileSync(DEV_STATUS_PATH, 'utf8'));
+ } catch {
+ console.log('⚠️ 当前 dev-status.json 不存在或格式错误,将创建新文件');
+ }
+
+ // 尝试从 Notion 获取数据
+ const notionTeam = await fetchDevStatusFromNotion();
+
+ if (notionTeam) {
+ // Notion 同步成功
+ currentStatus.team = notionTeam;
+ currentStatus.summary = generateSummary(notionTeam);
+ currentStatus.sync_source = 'notion-master-console';
+ console.log(`✅ Notion 同步成功 · ${notionTeam.length} 位开发者`);
+ } else {
+ // Notion 不可用时,保持现有数据,仅更新时间戳
+ console.log('📌 Notion 不可用,保留当前数据,更新同步时间戳');
+ if (currentStatus.team) {
+ currentStatus.summary = generateSummary(currentStatus.team);
+ }
+ currentStatus.sync_source = currentStatus.sync_source || 'local-refresh';
+ }
+
+ // 更新同步时间
+ currentStatus.last_sync = timestamp;
+
+ // 写入 persona-brain 版本
+ fs.writeFileSync(DEV_STATUS_PATH, JSON.stringify(currentStatus, null, 2) + '\n');
+ console.log(`✅ 已写入 ${DEV_STATUS_PATH}`);
+
+ // 同步写入根目录版本
+ fs.writeFileSync(ROOT_STATUS_PATH, JSON.stringify(currentStatus, null, 2) + '\n');
+ console.log(`✅ 已写入 ${ROOT_STATUS_PATH}`);
+
+ // 输出摘要
+ if (currentStatus.summary) {
+ console.log('\n📊 团队状态摘要:');
+ console.log(` 总开发者: ${currentStatus.summary.total_devs}`);
+ console.log(` 等待SYSLOG: ${currentStatus.summary.active_waiting_syslog}`);
+ console.log(` 最高连胜: ${currentStatus.summary.top_streak}`);
+ if (currentStatus.summary.alerts?.length > 0) {
+ console.log(` ⚠️ 告警: ${currentStatus.summary.alerts.length} 条`);
+ currentStatus.summary.alerts.forEach(a => console.log(` - ${a}`));
+ }
+ }
+
+ console.log(`\n✅ dev-status.json 同步完成 · ${timestamp}`);
+}
+
+main().catch(err => {
+ console.error('❌ 同步失败:', err.message);
+ process.exit(1);
+});