diff --git a/.blueprint/CONVENTIONS.md b/.blueprint/CONVENTIONS.md
index 3c3fa0d..87b2ca4 100644
--- a/.blueprint/CONVENTIONS.md
+++ b/.blueprint/CONVENTIONS.md
@@ -1,5 +1,5 @@
---
-version: 1.0.0
+version: 1.3.0
updated: 2026-04-03
source: dev-manager-tauri
---
@@ -25,10 +25,12 @@ source: dev-manager-tauri
| 事件 | 操作 |
|------|------|
| 用户确认了新需求 | 在 manifest.yaml 新增模块,创建 modules/.md |
-| 完成了功能实现 | 更新模块 status → done,标记任务卡 ✅ |
+| **开始执行任务卡(信息1)** | **任务卡前缀改为 🔵,status → in_progress(第一行代码之前)** |
+| **完成了功能实现(信息2)** | **任务卡前缀改为 ✅,status → done(验收通过之后)** |
| 拆分了大需求为子任务 | 在 modules/.md 中添加任务卡 |
| 调整了架构方向 | 更新 edges 关系,可能调整 area |
| 放弃了某个方向 | 删除对应模块和文件,或标记 status: abandoned |
+| **重构已完成的模块(迭代)** | **manifest.iteration 递增,旧任务卡保留,新建替代任务卡,模块加 `## 迭代记录`** |
## 模块状态定义
@@ -52,6 +54,8 @@ modules/.md 中的每个二级标题(`###`)是一张任务卡:
- depends: other-module-id
- acceptance: 简要描述完成标准
- blocked_reason: (仅 status: blocked 时填写)执行受阻的具体原因
+- iteration: 1 # 可选,省略时继承 manifest 顶层 iteration 值
+- replaces: 旧任务标题 # 可选,仅重构任务填写,说明本卡替代哪张旧卡
详细说明(可选)。
```
@@ -60,8 +64,8 @@ modules/.md 中的每个二级标题(`###`)是一张任务卡:
| 前缀 | status | 含义 |
|------|--------|------|
-| ✅ | done | 已完成 |
-| 🔵 | in_progress | 进行中 |
+| ✅ | done | 已完成(信息1 + 信息2 均已写入) |
+| 🔵 | in_progress | 执行中或被中断(只有信息1,等待信息2) |
| 📋 | todo | 可派发(信息充足) |
| 💭 | concept | 构思中 |
| 🔴 | blocked | 执行受阻 |
@@ -78,6 +82,15 @@ modules/.md 中的每个二级标题(`###`)是一张任务卡:
## manifest.yaml 规范
+### 顶层字段
+
+```yaml
+version: 1
+name: 项目名称
+iteration: 1 # 当前迭代号(默认 1,重构已完成模块时手动递增)
+updated: YYYY-MM-DD
+```
+
### 模块字段
```yaml
@@ -109,7 +122,64 @@ modules/.md 中的每个二级标题(`###`)是一张任务卡:
模块的 progress 由其任务卡的完成比例推算:
- 无任务卡的模块:手动设置 progress
-- 有任务卡的模块:`完成数 / 总数 × 100`(取整)
+- 有任务卡的模块(单迭代):`完成数 / 总数 × 100`(取整)
+- 有任务卡的模块(多迭代):**只计当前 iteration 的任务卡**,旧迭代 done 任务不计入分母,避免历史完成率虚高
+
+## 迭代管理
+
+### 何时递增 iteration
+
+**需要递增**(重构已完成的功能):
+- 一个已有 ✅ done 任务卡的模块需要重新设计或重写
+- 核心交互/架构方案被替换(不是在原有基础上扩展)
+
+**不需要递增**(正常演进):
+- 在现有模块上添加新功能
+- 修复 bug
+- 新增模块
+
+### 重构时的操作步骤
+
+1. **manifest.yaml 顶层** `iteration` +1,更新 `updated` 日期
+2. **旧任务卡**:保留原位,在卡片末尾加一行注释说明被替代(不改 status、不删除):
+ ```markdown
+ ### ✅ 旧功能实现
+ - status: done
+ - iteration: 1
+ ...
+ > ⚠️ 已被迭代 2「新功能实现」替代,仅作历史记录。
+ ```
+3. **新任务卡**:新建替代卡,标注 `iteration` 和 `replaces`:
+ ```markdown
+ ### 📋 新功能实现
+ - status: todo
+ - iteration: 2
+ - replaces: 旧功能实现
+ - complexity: M
+ - files: ...
+ - acceptance: ...
+ ```
+4. **模块文件**:追加 `## 迭代记录` 段落(见下方格式)
+5. **模块 status**:重置为 `in_progress`,progress 按当前迭代任务卡重新计算
+
+### 迭代记录格式
+
+模块文件末尾追加,每次迭代一条:
+
+```markdown
+## 迭代记录
+
+### 迭代 2(YYYY-MM-DD)
+- **原因**:React Flow 在 50+ 节点时性能卡顿,重构为虚拟化渲染方案
+- **替代**:「React Flow 节点图渲染(迭代1)」→「节点图虚拟化渲染」
+- **保留**:迭代1的任务卡作为历史记录,不删除
+```
+
+### 梦核如何感知迭代
+
+- usage.json 快照携带 `iteration` 字段(见飞轮外环章节)
+- 飞轮数据层按 iteration 分组计算完成率,迭代跳变(如 1→2 后完成率骤降)是正常信号
+- 梦核 prompt 会展示「完成率跳变项目」摘要,Opus 据此判断是主动重构还是项目失控
## 决策记录格式
@@ -144,15 +214,32 @@ Claude 在每次对话中应遵循以下流程:
| "开始做 XX" | 模块 status → in_progress |
| "为什么选这个方案" | 记录到模块的 `## 决策记录` |
-### 阶段 3:实现功能 — 标记完成或上报阻塞
-```
-成功 → 更新任务卡前缀为 ✅ → status: done
-所有任务卡完成 → 模块 status: done
-更新 manifest.yaml 的 updated 日期
+### 阶段 3:实现功能 — 两阶段执行锁
+
+任务卡执行遵循**两阶段提交**:开始写第一行代码前加锁(信息1),全部完成后解锁(信息2)。只有信息1没有信息2,说明任务被中断。
-失败且可自行解决 → 继续尝试
-失败且无法独立解决 → 标记 🔴 blocked + 填写 blocked_reason → 留给架构师处理
```
+📋 todo
+ ↓ 【信息1:加锁】第一行代码改动之前,先更新任务卡
+🔵 in_progress
+ ↓ 执行代码改动
+ ↓
+ ├─ 成功 → 【信息2:解锁】更新任务卡前缀为 ✅ → status: done
+ │ 所有任务卡完成 → 模块 status: done
+ │ 更新 manifest.yaml 的 updated 日期
+ │
+ ├─ 失败且可自行解决 → 继续尝试,保持 🔵
+ │
+ └─ 失败且无法独立解决 → 🔴 blocked + blocked_reason → 留给架构师处理
+```
+
+#### 锁悬空时的续接规则
+
+新会话读蓝图发现 🔵 任务卡(只有信息1,没有信息2)时:
+1. 检查任务卡的 `files` 字段,读取相关文件的**当前代码状态**
+2. 判断已完成什么、还缺什么
+3. 从断点继续执行,**不重头来过**
+4. 完成后补写 ✅(信息2),关锁
#### 架构师处理 blocked 任务
当 Opus 读蓝图发现 🔴 blocked 任务时:
@@ -179,6 +266,74 @@ Claude 应关注这些信号来决定是否更新蓝图:
- 执行模型遇到无法解决的问题 → 标记 🔴 blocked + blocked_reason
- Opus 看到 blocked 任务 → 分析原因,拆/补/调后产出新任务卡
+## 飞轮外环(跨项目数据反哺)
+
+内环描述的是单项目的执行流程。外环负责**把多个项目的执行数据聚合起来,反哺 CONVENTIONS 本身**。
+
+### 数据采集:usage.json 快照
+
+每次 Claude 读取 `.blueprint/manifest.yaml`(通过炼境打开蓝图时触发),炼境自动向项目的 `.blueprint/usage.json` 追加一条当日快照:
+
+```json
+{
+ "project_name": "my-project",
+ "snapshots": [
+ {
+ "at": "2026-04-05T10:00:00Z",
+ "conventions_version": "1.3.0",
+ "iteration": 1,
+ "modules": { "total": 22, "done": 18, "in_progress": 2, "planned": 1, "concept": 1, "blocked": 0, "stalled": [] },
+ "tasks": { "total": 45, "done": 38, "in_progress": 3, "blocked": 0, "todo": 4, "dispatched": 3 },
+ "blocked_reasons": []
+ }
+ ]
+}
+```
+
+- `dispatched` = 当前满足📋可派发条件的任务数(有 files + acceptance + complexity S/M)
+- 节流:同一 UTC 日期只写一条,避免多次打开导致数据膨胀
+- 文件存于项目本地,不上传服务器
+
+### 数据聚合:飞轮指标
+
+炼境跨项目读取所有 usage.json,按 `conventions_version` 分组,对每个版本队列计算 4 项指标:
+
+| 指标 | 计算方式 | 含义 |
+|------|----------|------|
+| 可派发率 | dispatched / total tasks | 任务卡规划质量,越高说明任务定义越清晰 |
+| 任务完成率 | done / total tasks | 整体执行进度均值 |
+| 飞轮持续率 | 30天内有快照的项目比例 | 外环是否在持续运转 |
+| 阻塞密度 | blocked / total tasks | 系统性阻塞程度,越低越好 |
+
+迭代感知:同一项目若 `iteration` 值增大,说明发生了主动重构。飞轮数据层对此类项目单独计算「当前迭代完成率」,与历史累计值区分展示,避免虚高。梦核 prompt 中标注「完成率跳变项目」供 Opus 分析是健康重构还是项目失控。
+
+### 梦核分析:记忆巩固
+
+炼境将聚合数据 + 当前 CONVENTIONS 全文格式化为提示词,发给 **Claude Opus** 做两件事:
+
+- **固化**:哪些跨项目重复出现的阻塞模式、哪些有效实践,值得写进 CONVENTIONS
+- **遗忘**:哪些休眠项目(90天无快照)的 AI 文档可以归档,哪些 CONVENTIONS 规则已过时
+
+### 闭环路径
+
+```
+执行任务 → usage.json 快照
+ ↓
+ 炼境聚合指标
+ ↓
+ 梦核提示词 → Claude Opus 分析
+ ↓
+ 用户/维护者审核建议
+ ↓
+ 更新 CONVENTIONS.md 源码 + 版本号
+ ↓
+ 发布新版炼境
+ ↓
+ 治理面板 sync_blueprint_rules → 分发到各项目
+```
+
+> **注意**:CONVENTIONS.md 编译进炼境二进制,更新需要修改炼境源码并发版,不支持运行时修改。这是有意为之的设计——保证规则版本可追溯,防止随意污染。
+
## 注意事项
- 不要删除已完成的任务卡,它们是项目历史的一部分
diff --git a/.blueprint/manifest.yaml b/.blueprint/manifest.yaml
index b22d881..94ed9f5 100644
--- a/.blueprint/manifest.yaml
+++ b/.blueprint/manifest.yaml
@@ -1,7 +1,9 @@
version: 1
-name: Dev Manager Tauri
+name: 炼境
description: Git 多空间开发管理器,集成项目管理、Git 操作、CI/CD、开发工具链管理
-updated: 2026-04-03
+iteration: 1
+updated: 2026-04-04
+
areas:
@@ -83,8 +85,8 @@ modules:
- id: blueprint-onboarding
name: 蓝图接入引导
area: frontend
- status: planned
- progress: 0
+ status: done
+ progress: 100
position: [1250, 0]
# ── 后端 ────────────────────────────────────────
@@ -140,15 +142,22 @@ modules:
- id: blueprint-governance
name: 蓝图治理
area: backend
- status: planned
- progress: 0
+ status: done
+ progress: 100
position: [1250, 350]
- - id: blueprint-feedback
- name: 蓝图反馈聚合
+ - id: blueprint-flywheel
+ name: 飞轮数据层
area: backend
- status: concept
- progress: 0
+ status: done
+ progress: 100
+ position: [1000, 500]
+
+ - id: blueprint-feedback
+ name: 梦核 · 记忆巩固
+ area: backend
+ status: done
+ progress: 100
position: [1250, 500]
# ── 基础设施 ────────────────────────────────────
@@ -163,7 +172,7 @@ modules:
name: 客户端自动更新
area: infra
status: done
- progress: 90
+ progress: 100
position: [250, 650]
- id: wsl-proxy
@@ -187,6 +196,34 @@ modules:
progress: 100
position: [750, 650]
+ - id: tray-service
+ name: 系统托盘常驻
+ area: infra
+ status: done
+ progress: 100
+ position: [1000, 650]
+
+ - id: mcp-server
+ name: 内嵌 MCP Server
+ area: backend
+ status: done
+ progress: 100
+ position: [1500, 350]
+
+ - id: mcp-config-inject
+ name: MCP 配置注入
+ area: backend
+ status: done
+ progress: 100
+ position: [1500, 500]
+
+ - id: product-group-mcp
+ name: 产品组 MCP 设置
+ area: frontend
+ status: done
+ progress: 100
+ position: [1500, 0]
+
edges:
# 前端依赖
- from: workspace-mgmt
@@ -235,14 +272,21 @@ edges:
- from: blueprint-reader
to: blueprint-governance
type: related
- - from: blueprint-view
- to: blueprint-onboarding
- type: dependency
- - from: blueprint-governance
- to: blueprint-feedback
- type: related
- from: blueprint-onboarding
to: blueprint-view
+ type: dependency
+ # 外环:飞轮数据层 → 梦核 → 治理分发
+ - from: blueprint-reader
+ to: blueprint-flywheel
+ type: dependency
+ - from: blueprint-flywheel
+ to: blueprint-feedback
+ type: dependency
+ - from: blueprint-feedback
+ to: blueprint-governance
+ type: dependency
+ - from: blueprint-view
+ to: blueprint-flywheel
type: related
# 基础设施关联
@@ -258,3 +302,17 @@ edges:
- from: auto-update
to: settings-page
type: related
+
+ # 产品组 MCP 飞轮
+ - from: tray-service
+ to: mcp-server
+ type: dependency
+ - from: mcp-server
+ to: mcp-config-inject
+ type: dependency
+ - from: mcp-config-inject
+ to: product-group-mcp
+ type: dependency
+ - from: product-group-mcp
+ to: settings-page
+ type: related
diff --git a/.blueprint/modules/auto-update.md b/.blueprint/modules/auto-update.md
index c7ee862..7d93f14 100644
--- a/.blueprint/modules/auto-update.md
+++ b/.blueprint/modules/auto-update.md
@@ -2,7 +2,7 @@
id: auto-update
name: 客户端自动更新
status: done
-progress: 90
+progress: 100
---
## 概述
@@ -35,6 +35,6 @@ progress: 90
updater:default + process:default + http 域名放行。
-### 💭 自动检查更新
-- status: concept
+### ✅ 自动检查更新
+- status: done
- notes: 应用启动时自动检查,发现新版本后在状态栏提示,而非需要手动进入设置页。
diff --git a/.blueprint/modules/blueprint-feedback.md b/.blueprint/modules/blueprint-feedback.md
index 8962910..13feb82 100644
--- a/.blueprint/modules/blueprint-feedback.md
+++ b/.blueprint/modules/blueprint-feedback.md
@@ -1,29 +1,64 @@
---
id: blueprint-feedback
-name: 蓝图反馈聚合
-status: concept
-progress: 0
+name: 梦核 · 记忆巩固
+status: done
+progress: 100
---
## 概述
-建立跨项目蓝图反馈收集和分析机制。各项目 Claude 执行时将遇到的问题、格式缺口、改进建议写入 `.blueprint/FEEDBACK.md`,dev-manager-tauri 扫描聚合后呈现,供 Opus 分析并优化 master CONVENTIONS.md,形成飞轮闭环。
+炼境的记忆巩固层。类比人类睡眠时大脑对记忆的主动处理——保留重要的(固化进 CONVENTIONS),清理无用的(归档休眠文档)。
-## 决策记录
+梦核不是算法,是一条精心设计的提示词。炼境负责收集和格式化数据(飞轮数据层),Claude 负责做判断:哪些跨项目重复出现的阻塞模式值得写进 CONVENTIONS,哪些长期休眠的文档可以归档。用户确认后,治理模块执行分发。
-- FEEDBACK.md 为纯 Markdown,按条目写入,每条含 type / reason / suggestion 字段
-- 反馈类型:blocked(执行受阻)/ convention_gap(规则不够用)/ format_issue(格式问题)/ improvement(改进建议)
-- 聚合视图只读,不提供编辑功能,Claude 才是修改 CONVENTIONS.md 的执行者
+```
+[飞轮数据层] 聚合跨项目数据
+ ↓
+[梦核提示词] 喂给 Claude Opus 做判断
+ ↓
+ 固化 ──────────────────── 遗忘
+ ↓ ↓
+CONVENTIONS 改进草稿 归档候选列表
+ ↓ ↓
+[治理模块] 分发 用户确认 → 归档操作
+```
## 任务卡
-### 💭 FEEDBACK.md 格式约定
-- status: concept
-- notes: 需要在 CONVENTIONS.md 中加入 FEEDBACK.md 的写入规则和格式规范,确保各项目 Claude 写入格式一致
+### ✅ 梦核分析入口(蓝图弹窗)
+- status: done
+- complexity: S
+- files: src/components/dashboard/BlueprintModal.tsx, src/lib/commands.ts
+- depends: blueprint-flywheel
+- acceptance: 治理面板新增「梦核分析」区块,点击"♻️ 打开飞轮面板"跳转到飞轮面板;飞轮面板生成梦核提示词(可复制);引导文字说明正确流程:「复制 → 粘贴给 Claude Opus → 将建议反馈给炼境维护者 → 更新 CONVENTIONS.md 并发布新版本」(梦核不自动写入,需手动修改源码发版)
-### 💭 get_all_blueprint_feedbacks command
-- status: concept
-- notes: 扫描所有已注册项目的 .blueprint/FEEDBACK.md,解析条目,按 type 分类返回
+### ✅ 归档候选展示
+- status: done
+- complexity: S
+- files: src/components/dashboard/BlueprintModal.tsx
+- depends: blueprint-flywheel
+- acceptance: 飞轮面板展示两类归档候选:① 飞轮停转项目(usage.json 最新 at 距今超过 60 天,显示最后活跃日期)② AI文档休眠项目(usage.json 最新 at 距今超过 90 天,推断 Claude 长期未读该项目的 AI 文档);每条候选显示项目名 + 最后活跃日期 + 「归档」按钮
-### 💭 跨项目反馈聚合视图
+### ✅ 归档 command
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src-tauri/src/lib.rs, src/lib/commands.ts
+- acceptance: archive_project_docs command;接收项目路径;将 .blueprint/modules/*.md 复制到 .blueprint/archive/YYYY-MM-DD-HHmmss/ 后删除原文件;写 MANIFEST.txt 记录归档信息;返回归档摘要(archived_files + archive_dir)
+
+### 💭 恢复 command
- status: concept
-- notes: 在蓝图界面或设置页展示:高频 blocked 原因、重复出现的 convention_gap、跨项目 suggestion 列表;提供「复制给 Opus 分析」按钮,生成汇总报告
+- notes: 读取 .blueprint/archive/*/MANIFEST.txt,将文件还原到原路径,支持选择性恢复,覆盖前需用户确认
+
+### ✅ 梦核提示词增加停滞模块数据
+- status: done
+- complexity: S
+- files: src-tauri/src/commands/blueprint.rs, src/lib/commands.ts
+- acceptance: get_flywheel_stats 收集所有项目最新快照的 modules.stalled,聚合为 stalled_modules: Vec(格式 "project/module_id");FlywheelStats 新增 stalled_modules 字段;梦核提示词新增「停滞模块」摘要段;同步实现迭代跳变检测(iteration_jumps)并补入提示词
+
+## 决策记录
+
+- 梦核的核心是提示词工程而非算法,判断质量随 Claude 模型升级自动变强,无需维护打分公式
+- 固化输出(CONVENTIONS 改进)由用户确认后交 governance 执行,不自动写入,保留人工节点
+- 遗忘操作(归档)提供撤销能力(MANIFEST.txt + 恢复 command),降低误操作风险
+- AI 文档休眠判断基于 usage.json 中 get_blueprint 的调用记录,间接反映 Claude 是否在读这些文档
+- 命名选「梦核」:梦境处理记忆(非主动学习),核心是巩固而非监控
+- 归档功能直接操作 .blueprint/modules/,用户在飞轮面板触发,清空休眠项目的模块 md 文件
diff --git a/.blueprint/modules/blueprint-flywheel.md b/.blueprint/modules/blueprint-flywheel.md
new file mode 100644
index 0000000..6b2e3f5
--- /dev/null
+++ b/.blueprint/modules/blueprint-flywheel.md
@@ -0,0 +1,55 @@
+---
+id: blueprint-flywheel
+name: 飞轮数据层
+status: done
+progress: 100
+---
+
+## 概述
+外环数据采集与聚合层。在各项目的 .blueprint/usage.json 中记录飞轮运转快照(任务完成率、阻塞密度、conventions 版本),炼境跨项目聚合后计算健康指标,并格式化为梦核分析提示词的输入数据。
+
+代码只处理确定性的机械工作(采集、计算、格式化),判断交给 Claude(梦核)。
+
+## 任务卡
+
+### ✅ usage.json 埋点写入
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs
+- acceptance: get_blueprint command 执行后,向项目 .blueprint/usage.json 追加一条 snapshot;schema:{ project_name, at, conventions_version, iteration, modules: {total,done,in_progress,planned,concept,blocked,stalled:string[]}, tasks: {total,done,in_progress,blocked,todo,dispatched}, blocked_reasons: string[] };iteration 从 manifest.yaml 顶层 iteration 字段读取,失败写 1;节流:同一 UTC 日期同一项目只追加一条;conventions_version 从 .blueprint/CONVENTIONS.md frontmatter 读取,失败写 "unknown";usage.json 不存在时自动创建为 { "project_name": "...", "snapshots": [] }
+
+### ✅ get_flywheel_stats command
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src-tauri/src/lib.rs, src/lib/commands.ts
+- depends: blueprint-reader
+- acceptance: 接收项目路径数组(由前端从 store 传入,与 sync_blueprint_rules 同模式);读取每个项目的 .blueprint/usage.json;按 conventions_version 分组,对每组计算 4 项指标:可派发率(dispatched/total tasks)、任务完成率(done/total tasks)、飞轮持续率(30天内有快照的项目比例)、阻塞密度(blocked/total tasks);跨项目聚合 blocked_reasons TOP10;检测停转项目(60天无快照)和 AI文档休眠项目(90天无快照),均携带 last_active 日期;返回结构化 FlywheelStats
+
+### ✅ get_flywheel_stats 支持迭代感知
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src/lib/commands.ts
+- acceptance: usage.json snapshot 写入 iteration(读 manifest.yaml 顶层字段,失败写 1);BlueprintManifest struct 新增 iteration 字段;get_flywheel_stats 扫描每个项目所有快照,检测 iteration 跳变,返回 iteration_jumps: Vec;同时收集最新快照中 modules.stalled 列表,返回 stalled_modules: Vec(格式 "project/module_id");梦核 prompt 补充「迭代跳变项目」和「停滞模块」两段摘要;commands.ts 新增 IterationJump 接口,FlywheelStats 补两字段
+
+### ✅ 梦核提示词生成 command
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src-tauri/src/lib.rs, src/lib/commands.ts
+- depends: blueprint-reader
+- acceptance: generate_muhe_prompt command,接收 FlywheelStats(由前端先调 get_flywheel_stats 获取后传入)+ 读取 master CONVENTIONS.md 内容,生成完整梦核分析提示词字符串;提示词包含:聚合数据摘要、TOP blocked_reasons、CONVENTIONS 全文、固化/遗忘双任务指令;注意:本任务须在 get_flywheel_stats 实现后执行
+
+### ✅ 飞轮健康面板
+- status: done
+- complexity: M
+- files: src/components/dashboard/BlueprintModal.tsx, src/lib/commands.ts
+- depends: blueprint-view
+- acceptance: 蓝图弹窗新增「飞轮」侧边面板;展示:各 conventions 版本队列的指标对比表(可派发率 / 任务完成率 / 飞轮持续率 / 阻塞密度)、跨项目 blocked_reasons TOP10、飞轮停转项目(60–90天,仅展示未进入休眠列表的)、AI文档休眠项目(90天+,含归档按钮+二次确认弹窗)、迭代跳变项目(iteration 递增,展示完成率跳变,⚠️标注大幅下降)、停滞模块(in_progress 但无可推进任务,格式 project/module_id);「生成梦核提示词」按钮调用 generate_muhe_prompt,展示结果供复制
+
+## 决策记录
+
+- 代码只做机械工作(采集、聚合、格式化),梦核判断完全交给 Claude,不写打分公式
+- usage.json 存各项目本地,不上传服务器,符合炼境 document-driven 设计哲学
+- 节流策略:同一天一条,避免频繁打开蓝图导致数据膨胀
+- conventions_version 打标是版本对比的关键,是外环版本评估的基础
+- 4项指标中「可派发率」衡量任务卡规划质量,「飞轮持续率」衡量外环是否在转,两者是最先导指标;任务完成率衡量整体执行进度,阻塞密度是系统性风险信号
+- iteration 字段用于区分历史 done 和当前迭代 done,避免多次重构后完成率虚高;迭代跳变是正常信号(主动重构),梦核据此判断而非告警
diff --git a/.blueprint/modules/blueprint-governance.md b/.blueprint/modules/blueprint-governance.md
index 1be7846..53bf461 100644
--- a/.blueprint/modules/blueprint-governance.md
+++ b/.blueprint/modules/blueprint-governance.md
@@ -1,8 +1,8 @@
---
id: blueprint-governance
name: 蓝图治理
-status: planned
-progress: 0
+status: done
+progress: 100
---
## 概述
@@ -13,36 +13,37 @@ progress: 0
- CONVENTIONS.md 加 YAML frontmatter 版本号,应用以此判断是否需要同步
- CLAUDE.md 用 `` / `` 标记管理区,应用只替换标记内内容,不动项目自有内容
- 无 CLAUDE.md 时生成初始文件;有 CLAUDE.md 但无 MANAGED 区时追加到末尾
+- MASTER_CONVENTIONS 通过 `include_str!()` 在编译时嵌入,保证 binary 自包含
+- BlueprintStatus 四状态:none / rules_outdated / content_stale / synced
## 任务卡
-### 📋 CONVENTIONS.md 版本化
-- status: todo
+### ✅ CONVENTIONS.md 版本化
+- status: done
- complexity: S
- files: .blueprint/CONVENTIONS.md
- acceptance: frontmatter 加 version 和 updated 字段,格式为 semver(如 1.0.0)
-### 📋 get_blueprint_status command
-- status: todo
-- complexity: M
-- files: src-tauri/src/commands/blueprint.rs
-- acceptance: 接收项目路径,返回四种状态之一:none / rules_outdated / content_stale / synced;判断依据:.blueprint/ 是否存在、CONVENTIONS.md 版本是否匹配 master、CLAUDE.md 是否有 MANAGED 区且版本匹配
-
-### 📋 sync_blueprint_rules command
-- status: todo
-- complexity: M
-- files: src-tauri/src/commands/blueprint.rs
-- acceptance: 接收项目路径,执行三步:(1) 覆盖写入最新 CONVENTIONS.md;(2) 检测 CLAUDE.md 是否存在及 MANAGED 区状态,按三种情况(无文件/无区块/版本旧)分别处理;(3) 返回操作摘要
-
-### 📋 CLAUDE.md 模板定义
-- status: todo
+### ✅ CLAUDE.md 模板定义
+- status: done
- complexity: S
- files: src-tauri/src/commands/blueprint.rs
- acceptance: 在 Rust 中定义 CLAUDE.md MANAGED 区的标准内容模板,内容涵盖:蓝图飞轮流程、读取 manifest 指令、任务卡派发规则;模板版本与 CONVENTIONS.md 同步
-### 📋 批量同步所有项目规则
-- status: todo
+### ✅ get_blueprint_status command
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src-tauri/src/lib.rs, src/lib/commands.ts
+- acceptance: 接收项目路径,返回四种状态之一:none / rules_outdated / content_stale / synced;判断依据:.blueprint/ 是否存在、CONVENTIONS.md 版本是否匹配 master、CLAUDE.md 是否有 MANAGED 区且版本匹配
+
+### ✅ sync_blueprint_rules command
+- status: done
+- complexity: M
+- files: src-tauri/src/commands/blueprint.rs, src-tauri/src/lib.rs, src/lib/commands.ts
+- acceptance: 接收项目路径,执行三步:(1) 覆盖写入最新 CONVENTIONS.md;(2) 检测 CLAUDE.md 是否存在及 MANAGED 区状态,按三种情况(无文件/无区块/版本旧)分别处理;(3) 返回操作摘要
+
+### ✅ 批量同步所有项目规则
+- status: done
- complexity: S
-- files: src-tauri/src/commands/blueprint.rs, src/components/settings/SettingsPage.tsx
-- depends: sync_blueprint_rules command
+- files: src/components/settings/SettingsPage.tsx
- acceptance: 设置页提供「同步所有项目蓝图规则」按钮,调用 sync_blueprint_rules 遍历所有已注册项目,返回每个项目的同步结果摘要
diff --git a/.blueprint/modules/blueprint-onboarding.md b/.blueprint/modules/blueprint-onboarding.md
index 4bd5be8..e83788d 100644
--- a/.blueprint/modules/blueprint-onboarding.md
+++ b/.blueprint/modules/blueprint-onboarding.md
@@ -1,8 +1,8 @@
---
id: blueprint-onboarding
name: 蓝图接入引导
-status: planned
-progress: 0
+status: done
+progress: 100
---
## 概述
@@ -13,26 +13,26 @@ progress: 0
- 提示词生成由 Rust 完成(扫描目录结构、读取 git log、拼接模板),前端只做展示
- 提示词包含完整上下文(项目信息 + 目录结构 + 现有蓝图 + CONVENTIONS 摘要),确保 Claude 无需额外询问
- 目录扫描限深度 3 层,避免输出过大
+- 配置文件扫描采用「存在则读」策略:AI 文档(ARCHITECTURE.md/CLAUDE.md/AGENTS.md)自动包含,对无此类文件的简单项目零影响
+- monorepo 自动感知:检测到 pnpm-workspace.yaml 等标志文件后,自动读子包配置并调整模块数建议,用户无需手动区分项目复杂度
## 任务卡
-### 📋 generate_blueprint_prompt command
-- status: todo
+### ✅ generate_blueprint_prompt command
+- status: done
- complexity: M
- files: src-tauri/src/commands/blueprint.rs
- depends: blueprint-governance
-- acceptance: 接收项目路径和提示词类型(init / sync),返回对应提示词字符串;init 类型扫描目录结构 + 读取 README/package.json/Cargo.toml;sync 类型额外读取现有蓝图文件 + 近 20 条 git log
+- acceptance: 接收项目路径和提示词类型(init / sync),返回对应提示词字符串;init 类型扫描目录结构 + 读取 README/package.json/Cargo.toml/ARCHITECTURE.md/CLAUDE.md/AGENTS.md/DEVELOPMENT.md(存在才读,不存在跳过);monorepo 自动检测(pnpm-workspace.yaml/lerna.json/turbo.json),检测到则额外读 apps/ 前4个子包 + packages/ 前2个子包的 package.json,并在提示词中注入 monorepo 提示(模块数建议 10-25、以业务能力为单位划分);sync 类型额外读取现有蓝图文件 + 近 20 条 git log
-### 📋 项目列表蓝图状态徽章
-- status: todo
+### ✅ 项目列表蓝图状态徽章
+- status: done
- complexity: M
- files: src/components/dashboard/ProjectCard.tsx
-- depends: get_blueprint_status command
- acceptance: 项目卡片显示蓝图状态徽章(🔘无蓝图 / 🟡规则待更新 / 🟠内容待同步 / 🟢已同步);🟡状态点击直接执行同步(无需 Claude);其余状态点击打开操作弹窗
-### 📋 蓝图操作弹窗
-- status: todo
+### ✅ 蓝图操作弹窗
+- status: done
- complexity: M
- files: src/components/dashboard/BlueprintPromptModal.tsx
-- depends: generate_blueprint_prompt command
- acceptance: 弹窗展示生成的提示词(monospace、可选中)、一键复制按钮、说明文案("复制后粘贴给 Claude Opus 执行");🔘状态显示「初始化蓝图」提示词,🟠状态显示「同步描绘」提示词
diff --git a/.blueprint/modules/blueprint-view.md b/.blueprint/modules/blueprint-view.md
index c5c108d..c6b67c3 100644
--- a/.blueprint/modules/blueprint-view.md
+++ b/.blueprint/modules/blueprint-view.md
@@ -1,8 +1,8 @@
---
id: blueprint-view
name: 蓝图可视化
-status: done
-progress: 100
+status: in_progress
+progress: 86
---
## 概述
@@ -78,6 +78,30 @@ progress: 100
- files: src/components/dashboard/BlueprintModal.tsx
- acceptance: 根据模块相对位置自动选择最短路径 handle,避免连线绕远
+### ✅ 蓝图治理面板
+- status: done
+- complexity: M
+- files: src/components/dashboard/BlueprintModal.tsx, src/lib/commands.ts
+- acceptance: 侧边新增「治理」面板,展示 CONVENTIONS.md 当前版本与 master 版本对比;提供「同步规则」按钮(替代设置页的同步入口);提供「初始化/同步蓝图」提示词入口(替代卡片徽章弹窗,统一收口)
+
+### ✅ 顶部状态行扩展
+- status: done
+- complexity: S
+- files: src/components/dashboard/BlueprintModal.tsx
+- depends: blueprint-governance
+- acceptance: 统计条新增「规则」状态指示(🟢已同步/🟡待更新),点击跳转到治理面板;「AI文档」指示器预留位置但灰显(depends: blueprint-feedback 未完成时不可用,避免跳转到不存在的面板)
+
+### ✅ 批量执行提示词入口
+- status: done
+- complexity: S
+- files: src/components/dashboard/BlueprintModal.tsx
+- depends: blueprint-view
+- acceptance: 下一步汇总面板新增「复制批量执行提示词」按钮;点击后将当前项目的批量执行提示词复制到剪贴板;提示词包含项目名称,指示 Claude 自主遍历所有 📋 任务卡依次执行(标记 🔵 加锁 → 实现 → 标记 ✅ 解锁,失败标 🔴 blocked),中途不等用户确认,全部完成后输出执行报告;提示词纯前端生成,无需新增 Tauri command
+
+### 💭 AI 文档健康面板
+- status: concept
+- notes: 侧边新增「AI文档」面板,列出项目内所有 AI 相关文档(CLAUDE.md/AGENTS.md/.cursorrules 等)及其使用频率评分;标出休眠文档;提供「归档」操作。依赖 ai-doc-analytics 模块的埋点数据。
+
## 决策记录
- 选择 React Flow (@xyflow/react) 而非 D3 或自绘 canvas,因为 React Flow 提供开箱即用的节点拖拽、缩放、连线路由
@@ -85,3 +109,4 @@ progress: 100
- 区域分组用 React Flow 父子节点实现,模块作为子节点自动跟随分组
- hover 只做边高亮,点击(pin)才做节点淡化,避免 React Flow 节点重建导致的闪烁
- 飞轮模型分配:Opus 做架构需求和蓝图维护,Sonnet 执行任务卡,.blueprint/ 文件作为模型间共享状态
+- 蓝图弹窗定位为项目蓝图驾驶舱:所有蓝图相关操作(治理、AI文档健康、提示词生成)统一收口到此弹窗,不再分散到设置页和卡片徽章
diff --git a/.blueprint/modules/mcp-config-inject.md b/.blueprint/modules/mcp-config-inject.md
new file mode 100644
index 0000000..f3f108d
--- /dev/null
+++ b/.blueprint/modules/mcp-config-inject.md
@@ -0,0 +1,40 @@
+# MCP 配置注入
+
+产品组成员变更时,炼境自动向对应项目的 `.claude/settings.json` 注入或移除 MCP 服务器配置,实现热插拔。
+
+## 决策记录
+
+- 使用 `lian-jing:group-{group_id}` 命名空间前缀标记炼境写入的 key,与用户自有配置严格隔离
+- 炼境只操作自己命名空间下的 key,其余配置永远不动
+- 加入/退出操作由炼境 UI 驱动,炼境必然在线,无需异步清理机制
+- 注入前做 JSON 有效性校验,文件损坏时不覆盖,在 UI 提示用户
+
+## 任务卡
+
+### ✅ 加入产品组时注入配置
+- status: done
+- complexity: M
+- files: src-tauri/src/mcp_inject.rs, src-tauri/src/commands/groups.rs
+- depends: mcp-server
+- acceptance:
+ - 项目加入产品组后,其 win_path 或 wsl_path 对应的 `.claude/settings.json` 被写入以下条目
+ - key 格式:`lian-jing:group-{group_id}`
+ - value:`{ "type": "http", "url": "http://localhost:{port}/mcp/group/{group_id}" }`
+ - 文件不存在时自动创建;文件已存在时合并写入,不影响其他 key
+ - 文件 JSON 损坏时 eprintln 记录,不影响成员关系 DB 写入
+
+### ✅ 退出产品组时清除配置
+- status: done
+- complexity: S
+- files: src-tauri/src/mcp_inject.rs, src-tauri/src/commands/groups.rs
+- depends: mcp-config-inject(注入)
+- acceptance:
+ - 项目退出产品组后,`lian-jing:group-{group_id}` 这一 key 被精确删除
+ - 其他 key(包括其他产品组的 key、用户自有配置)完全不受影响
+ - 在 DB 删除前先执行清除(remove_project_mcp 需要 project_workspaces 查路径)
+
+### ✅ 端口变更时重新注入所有配置
+- status: done
+- complexity: S
+- files: src-tauri/src/mcp_inject.rs, src-tauri/src/commands/settings.rs
+- acceptance: update_settings 检测到 mcp_port key 变更后自动调用 reinject_all_mcp,遍历所有 project_group_map 记录重新写入 settings.json
diff --git a/.blueprint/modules/mcp-server.md b/.blueprint/modules/mcp-server.md
new file mode 100644
index 0000000..10178e4
--- /dev/null
+++ b/.blueprint/modules/mcp-server.md
@@ -0,0 +1,41 @@
+# 内嵌 MCP Server
+
+炼境进程内置一个 axum HTTP 服务器,实现 MCP 协议,按产品组聚合上下文数据,供组内所有项目的 Claude 实例查询。
+
+## 决策记录
+
+- 内嵌在 Tauri 进程而非独立进程,因为炼境托盘常驻时天然保证服务存活,无需额外进程管理
+- 使用 HTTP/SSE 传输而非 stdio,因为 HTTP 可被 Windows 和 WSL 两侧同时访问(WSL localhost 穿透)
+- 默认端口 27190(冷门端口,冲突概率极低),可在设置页修改
+- MCP 工具按 group_id 路由,不同产品组的数据完全隔离
+
+## 任务卡
+
+### ✅ 启动时绑定 HTTP 端口
+- status: done
+- complexity: M
+- files: src-tauri/src/mcp_server.rs, src-tauri/src/lib.rs, src-tauri/Cargo.toml
+- depends: tray-service
+- acceptance: 炼境启动后 `http://localhost:27190/health` 返回 200,进程退出后端口释放
+
+### ✅ 实现 MCP JSON-RPC 基础协议
+- status: done
+- complexity: M
+- files: src-tauri/src/mcp_server.rs
+- acceptance: 支持 `initialize`、`tools/list`、`tools/call` 三个 JSON-RPC 方法,通过 MCP Inspector 验证
+
+### ✅ 实现产品组上下文工具
+- status: done
+- complexity: M
+- files: src-tauri/src/mcp_server.rs
+- depends: mcp-server(基础协议)
+- acceptance: 以下三个工具可用且返回正确数据
+ - `list_group_projects(group_id)` → 组内项目列表(名称、路径、平台、tech_stack)
+ - `get_project_blueprint(group_id, project_id)` → 对应项目的 manifest.yaml 内容
+ - `get_project_file(group_id, project_id, relative_path)` → 对应项目的任意文件内容
+
+### ✅ MCP 端口可配置(Rust 侧)
+- status: done
+- complexity: S
+- files: src-tauri/src/db.rs, src-tauri/src/mcp_server.rs
+- acceptance: settings 表默认写入 mcp_port=27190,启动时读取该值绑定端口(SettingsPage UI 见 product-group-mcp 模块)
diff --git a/.blueprint/modules/product-group-mcp.md b/.blueprint/modules/product-group-mcp.md
new file mode 100644
index 0000000..b1092f0
--- /dev/null
+++ b/.blueprint/modules/product-group-mcp.md
@@ -0,0 +1,25 @@
+# 产品组 MCP 设置
+
+产品组 UI 中展示 MCP 激活状态,让用户可感知哪些项目已接入跨项目上下文能力。
+
+## 任务卡
+
+### ✅ 产品组详情显示 MCP 注入状态
+- status: done
+- complexity: S
+- files: src/components/manage/ManagePage.tsx
+- depends: mcp-config-inject
+- acceptance:
+ - 产品组成员卡片显示平台标识(Win / WSL)
+ - 已配置路径的项目显示绿点 "MCP" 徽标,hover 显示路径
+ - 未配置路径的项目显示「无路径」灰色徽标,说明注入已跳过
+
+### ✅ 设置页添加 MCP 端口配置
+- status: done
+- complexity: S
+- files: src/components/settings/SettingsPage.tsx, src-tauri/src/commands/settings.rs
+- depends: mcp-server(端口可配置)
+- acceptance:
+ - 设置页新增「MCP 服务」分区,含端口输入框(默认 27190)和绿色「运行中」状态
+ - 保存时校验端口范围(1024~65535),调用 updateSettings({mcp_port})
+ - Rust 侧自动触发 reinject_all_mcp,所有成员配置即时更新
diff --git a/.blueprint/modules/tray-service.md b/.blueprint/modules/tray-service.md
new file mode 100644
index 0000000..0dbe435
--- /dev/null
+++ b/.blueprint/modules/tray-service.md
@@ -0,0 +1,28 @@
+# 系统托盘常驻
+
+炼境作为后台服务常驻,关闭窗口后进程继续运行,保持 MCP Server 持续提供服务。
+
+## 决策记录
+
+- 选择托盘常驻而非动态端口,因为 MCP 配置注入写静态 URL,进程不常驻则 Claude 连不上
+- 使用 tauri-plugin-single-instance 保证进程唯一,从根本上消除端口冲突风险
+
+## 任务卡
+
+### ✅ 集成 tauri-plugin-single-instance
+- status: done
+- complexity: S
+- files: src-tauri/Cargo.toml, src-tauri/src/lib.rs, src-tauri/tauri.conf.json
+- acceptance: 双击启动炼境时不新建进程,自动激活已有窗口并置顶
+
+### ✅ 系统托盘图标与菜单
+- status: done
+- complexity: S
+- files: src-tauri/src/lib.rs, src-tauri/tauri.conf.json
+- acceptance: 任务栏托盘显示炼境图标,右键菜单有「显示窗口」「退出炼境」两项
+
+### ✅ 关闭窗口最小化到托盘
+- status: done
+- complexity: S
+- files: src-tauri/src/lib.rs
+- acceptance: 点击窗口关闭按钮时,窗口隐藏但进程不退出;点「退出炼境」才真正结束进程
diff --git a/.blueprint/usage.json b/.blueprint/usage.json
new file mode 100644
index 0000000..081436a
--- /dev/null
+++ b/.blueprint/usage.json
@@ -0,0 +1,26 @@
+{
+ "project_name": "dev-manager-tauri",
+ "snapshots": [
+ {
+ "at": "2026-04-03T04:52:50Z",
+ "blocked_reasons": [],
+ "conventions_version": "1.1.0",
+ "modules": {
+ "blocked": 0,
+ "concept": 0,
+ "done": 22,
+ "in_progress": 1,
+ "planned": 2,
+ "total": 25
+ },
+ "tasks": {
+ "blocked": 0,
+ "dispatched": 4,
+ "done": 84,
+ "in_progress": 0,
+ "todo": 7,
+ "total": 95
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5f1f42f..698114f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,7 +53,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
- name: Dev Manager ${{ github.ref_name }}
+ name: 炼境 ${{ github.ref_name }}
body: |
## 更新内容
请查看提交记录了解详情。
@@ -69,7 +69,7 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ github.ref_name }}
- releaseName: Dev Manager ${{ github.ref_name }}
+ releaseName: 炼境 ${{ github.ref_name }}
releaseBody: |
## 更新内容
请查看提交记录了解详情。
diff --git a/CLAUDE.md b/CLAUDE.md
index 3c5e2c9..9413df6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,4 +1,4 @@
-# Dev Manager Tauri
+# 炼境 (Dev Manager Tauri)
Git 多空间开发管理器,Tauri v2 + React + TypeScript + Rust。
@@ -42,3 +42,28 @@ src-tauri/src/commands/ Tauri command 实现
.blueprint/ 项目蓝图(manifest + 模块详情 + 任务卡)
docs/ 部署和更新指南
```
+
+
+## 项目蓝图
+
+本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
+
+**每次对话都应遵循以下流程:**
+
+### 1. 了解当前状态
+对话开始时,先读取 `.blueprint/manifest.yaml` 了解项目模块和状态。
+
+### 2. 讨论需求时更新蓝图
+- 确认了新需求 → 在 manifest.yaml 新增模块,创建 `.blueprint/modules/.md`
+- 拆分了大需求 → 在模块文件中添加任务卡
+- 调整了方向 → 更新模块关系(edges)
+
+### 3. 完成实现后标记蓝图
+- 完成了功能 → 更新对应任务卡状态为 `done`(前缀改为 ✅)
+- 更新 manifest.yaml 顶部的 `updated` 日期
+
+### 4. 任务卡规范
+详见 `.blueprint/CONVENTIONS.md`,核心规则:
+- 任务卡同时具备 `files` + `acceptance` + complexity `S/M` → 标记为 📋 可派发
+- 可派发的任务卡可以直接交给 Claude Agent 独立完成
+
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index c5b4f53..3d80f41 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -234,6 +234,61 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+[[package]]
+name = "axum"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "base64"
version = "0.21.7"
@@ -827,6 +882,7 @@ dependencies = [
name = "dev-manager-tauri"
version = "0.1.11"
dependencies = [
+ "axum",
"base64 0.22.1",
"chrono",
"git2",
@@ -845,7 +901,9 @@ dependencies = [
"tauri-plugin-opener",
"tauri-plugin-process",
"tauri-plugin-shell",
+ "tauri-plugin-single-instance",
"tauri-plugin-updater",
+ "tokio",
"ureq",
"urlencoding",
"uuid",
@@ -1780,6 +1838,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
[[package]]
name = "hyper"
version = "1.8.1"
@@ -1794,6 +1858,7 @@ dependencies = [
"http",
"http-body",
"httparse",
+ "httpdate",
"itoa",
"pin-project-lite",
"pin-utils",
@@ -2406,6 +2471,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
[[package]]
name = "memchr"
version = "2.8.0"
@@ -4050,6 +4121,17 @@ dependencies = [
"zmij",
]
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
+dependencies = [
+ "itoa",
+ "serde",
+ "serde_core",
+]
+
[[package]]
name = "serde_repr"
version = "0.1.20"
@@ -4792,6 +4874,21 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "tauri-plugin-single-instance"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc61e4822b8f74d68278e09161d3e3fdd1b14b9eb781e24edccaabf10c420e8c"
+dependencies = [
+ "serde",
+ "serde_json",
+ "tauri",
+ "thiserror 2.0.18",
+ "tracing",
+ "windows-sys 0.60.2",
+ "zbus",
+]
+
[[package]]
name = "tauri-plugin-updater"
version = "2.10.0"
@@ -5222,6 +5319,7 @@ dependencies = [
"tokio",
"tower-layer",
"tower-service",
+ "tracing",
]
[[package]]
@@ -5260,6 +5358,7 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
+ "log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 01a22bb..694bf62 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -34,6 +34,9 @@ git2 = { version = "0.19", features = ["vendored-openssl"] }
tauri-plugin-http = "2"
tauri-plugin-updater = "2"
tauri-plugin-process = "2"
+tauri-plugin-single-instance = "2"
+axum = "0.7"
+tokio = { version = "1", features = ["net"] }
ureq = { version = "2", features = ["json"] }
open = "5"
base64 = "0.22"
diff --git a/src-tauri/src/commands/blueprint.rs b/src-tauri/src/commands/blueprint.rs
index 556fe4c..a4d1255 100644
--- a/src-tauri/src/commands/blueprint.rs
+++ b/src-tauri/src/commands/blueprint.rs
@@ -1,6 +1,37 @@
use serde::{Deserialize, Serialize};
use std::path::Path;
+// ── 主节点:master CONVENTIONS.md 内容和版本(编译时嵌入)────────────────────
+
+const MASTER_CONVENTIONS: &str = include_str!("../../../.blueprint/CONVENTIONS.md");
+const MASTER_CONVENTIONS_VERSION: &str = "1.3.0";
+
+// CLAUDE.md 蓝图管理区模板(版本与 CONVENTIONS 保持一致)
+const CLAUDE_MANAGED_TEMPLATE: &str = r#"
+## 项目蓝图
+
+本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
+
+**每次对话都应遵循以下流程:**
+
+### 1. 了解当前状态
+对话开始时,先读取 `.blueprint/manifest.yaml` 了解项目模块和状态。
+
+### 2. 讨论需求时更新蓝图
+- 确认了新需求 → 在 manifest.yaml 新增模块,创建 `.blueprint/modules/.md`
+- 拆分了大需求 → 在模块文件中添加任务卡
+- 调整了方向 → 更新模块关系(edges)
+
+### 3. 完成实现后标记蓝图
+- 完成了功能 → 更新对应任务卡状态为 `done`(前缀改为 ✅)
+- 更新 manifest.yaml 顶部的 `updated` 日期
+
+### 4. 任务卡规范
+详见 `.blueprint/CONVENTIONS.md`,核心规则:
+- 任务卡同时具备 `files` + `acceptance` + complexity `S/M` → 标记为 📋 可派发
+- 可派发的任务卡可以直接交给 Claude Agent 独立完成
+"#;
+
// ── manifest.yaml 对应结构 ───────────────────────────────────────────────────
#[derive(Debug, Serialize, Deserialize, Clone)]
@@ -9,6 +40,8 @@ pub struct BlueprintManifest {
pub name: String,
pub description: Option,
pub updated: Option,
+ /// 当前迭代号(默认 1,重构已完成模块时手动递增)
+ pub iteration: Option,
#[serde(default)]
pub areas: Vec,
#[serde(default)]
@@ -151,6 +184,9 @@ pub fn get_blueprint(project_path: String) -> Result
-
+
+
+
+
+
{/* 统计条 */}
{stats && (
+ {blueprint?.manifest.iteration != null && blueprint.manifest.iteration > 1 && (
+
+ 迭代 {blueprint.manifest.iteration}
+
+ )}
@@ -406,6 +454,30 @@ export function BlueprintModal({ projectName, projectPath, onClose }: Props) {
📋 {stats.dispatchable} 可派发
)}
+ {/* 规则 & AI文档 状态指示器 */}
+ {bpStatus && (
+
+ )}
+
{/* 总进度条(固定在右侧) */}
{pinnedModuleId && (
@@ -514,9 +586,19 @@ export function BlueprintModal({ projectName, projectPath, onClose }: Props) {
onSelectModule={(mod) => { setSidePanel({ type: "module", module: mod }); setPinnedModuleId(mod.id); }}
onClose={() => { setSidePanel(null); setPinnedModuleId(null); }}
/>
+ ) : sidePanel.type === "governance" ? (
+
setSidePanel(null)}
+ onOpenFlywheel={() => setSidePanel({ type: "flywheel" })}
+ />
+ ) : sidePanel.type === "flywheel" ? (
+ setSidePanel(null)} />
) : (
{ setSidePanel({ type: "module", module: mod }); setPinnedModuleId(mod.id); }}
onClose={() => { setSidePanel(null); setPinnedModuleId(null); }}
/>
@@ -747,13 +829,40 @@ function TaskCard({ task, moduleName }: { task: BlueprintTask; moduleName?: stri
function NextActionsPanel({
modules,
+ projectName,
onSelectModule,
onClose,
}: {
modules: BlueprintModule[];
+ projectName: string;
onSelectModule: (mod: BlueprintModule) => void;
onClose: () => void;
}) {
+ const [batchCopied, setBatchCopied] = useState(false);
+
+ const handleCopyBatchPrompt = () => {
+ const prompt = [
+ `当前项目:${projectName}`,
+ ``,
+ `读取 .blueprint/manifest.yaml 和所有 .blueprint/modules/*.md,`,
+ `以及 .blueprint/CONVENTIONS.md 了解执行规则。`,
+ ``,
+ `【处理断点】先检查是否有 🔵 in_progress 任务卡(上次中断的),`,
+ `若有,读取其 files 字段判断已完成什么、还缺什么,从断点继续。`,
+ ``,
+ `【批量执行】找出所有 📋 todo 任务卡,按任务卡 depends 字段及 manifest.yaml edges 综合拓扑排序后依次执行:`,
+ `- 执行前:前缀改为 🔵(信息1·加锁)`,
+ `- 执行依据:任务卡的 files 字段(涉及哪些文件)+ acceptance 字段(验收标准)`,
+ `- 完成后:前缀改为 ✅(信息2·解锁);若模块所有任务卡均完成,模块 status 改为 done`,
+ `- 更新 manifest.yaml 的 updated 日期`,
+ `- 无法独立解决:标记 🔴 blocked + blocked_reason,跳过继续下一张`,
+ ``,
+ `中途不需要等我确认,直接执行到底,全部完成后输出执行报告。`,
+ ].join("\n");
+ navigator.clipboard.writeText(prompt);
+ setBatchCopied(true);
+ setTimeout(() => setBatchCopied(false), 2000);
+ };
// 收集所有可派发和进行中的任务
const actionItems = useMemo(() => {
const items: { module: BlueprintModule; task: BlueprintTask; dispatchable: boolean }[] = [];
@@ -784,7 +893,16 @@ function NextActionsPanel({
下一步
-
+
+
+
+
{actionItems.length === 0 ? (
@@ -877,6 +995,225 @@ function NextActionsPanel({
);
}
+// ── 蓝图治理面板 ────────────────────────────────────────────────────────────
+
+function GovernancePanel({
+ projectPath,
+ projectName,
+ onClose,
+ onOpenFlywheel,
+}: {
+ projectPath: string;
+ projectName: string;
+ onClose: () => void;
+ onOpenFlywheel?: () => void;
+}) {
+ const { data: bpStatus, isPending: statusLoading, refetch: refetchStatus } = useQuery
({
+ queryKey: ["bp-status-governance", projectPath],
+ queryFn: () => getBlueprintStatus(projectPath),
+ });
+
+ const [syncing, setSyncing] = useState(false);
+ const [syncResult, setSyncResult] = useState(null);
+ const [syncError, setSyncError] = useState(null);
+
+ const [promptMode, setPromptMode] = useState<"init" | "sync" | null>(null);
+ const [prompt, setPrompt] = useState("");
+ const [promptLoading, setPromptLoading] = useState(false);
+ const [promptError, setPromptError] = useState(null);
+ const [copied, setCopied] = useState(false);
+
+ const handleSyncRules = async () => {
+ setSyncing(true);
+ setSyncError(null);
+ setSyncResult(null);
+ try {
+ const res = await syncBlueprintRules(projectPath);
+ setSyncResult(res);
+ refetchStatus();
+ } catch (e) {
+ setSyncError(String(e));
+ } finally {
+ setSyncing(false);
+ }
+ };
+
+ const handleOpenPrompt = async (mode: "init" | "sync") => {
+ if (promptMode === mode) {
+ setPromptMode(null);
+ return;
+ }
+ setPromptMode(mode);
+ setPrompt("");
+ setPromptError(null);
+ setPromptLoading(true);
+ try {
+ const result = await generateBlueprintPrompt(projectPath, mode);
+ setPrompt(result);
+ } catch (e) {
+ setPromptError(String(e));
+ } finally {
+ setPromptLoading(false);
+ }
+ };
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(prompt).then(() => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ });
+ };
+
+ const rulesOutdated = bpStatus?.status === "rules_outdated";
+ const currentVersion = bpStatus?.conventions_version ?? "—";
+ const masterVersion = bpStatus?.master_version ?? "—";
+ // 用后端完整状态判断(rules_outdated 才是真正未同步,content_stale/synced 都意味着规则已同步)
+ const versionSynced = bpStatus?.status === "synced" || bpStatus?.status === "content_stale";
+
+ return (
+
+ {/* 标题 */}
+
+
蓝图治理
+
+
+
+ {/* 规则版本状态 */}
+
+
CONVENTIONS 规则版本
+ {statusLoading ? (
+
检测中…
+ ) : (
+
+
+ 当前版本
+ {currentVersion}
+
+
+ 最新版本
+ {masterVersion}
+
+
+ 状态
+ {versionSynced ? (
+ 🟢 已同步
+ ) : (
+ 🟡 待更新
+ )}
+
+
+ )}
+
+
+ {/* 同步规则区域 */}
+ {rulesOutdated && !syncResult && (
+
+
规则版本待更新
+
+ 将覆盖写入 .blueprint/CONVENTIONS.md 并更新 CLAUDE.md 中的蓝图规则区块。
+
+ {syncError && (
+
{syncError}
+ )}
+
+
+ )}
+
+ {syncResult && (
+
+
✓ 同步完成
+
{syncResult.message}
+
+ )}
+
+ {/* 蓝图内容提示词 */}
+
+
蓝图内容
+
+ 将提示词交给 Claude Opus,由它负责理解和创作蓝图内容。
+
+
+
+
+
+
+
+ {/* 提示词内容区 */}
+ {promptMode && (
+
+ {promptLoading ? (
+
生成提示词中…
+ ) : promptError ? (
+
{promptError}
+ ) : (
+ <>
+
+ )}
+
+ {/* 梦核分析入口 */}
+ {onOpenFlywheel && (
+
+
梦核 · 记忆巩固
+
+ 基于跨项目飞轮数据,生成梦核提示词,由 Claude Opus 完成固化 / 遗忘判断。
+
+
+
+ )}
+
+ {/* 项目信息 */}
+
+
+ );
+}
+
// ── 未完成任务面板 ──────────────────────────────────────────────────────────
const REMAINING_ORDER: Record = { blocked: 0, in_progress: 1, todo: 2, concept: 3 };
@@ -947,3 +1284,334 @@ function RemainingTasksPanel({
);
}
+
+// ── 飞轮健康面板 ────────────────────────────────────────────────────────────
+
+function StalledProjectRow({ p, projectsMap, onArchive }: {
+ p: StalledProject;
+ projectsMap: Record; // name → path
+ onArchive?: (path: string, name: string) => void;
+}) {
+ const path = projectsMap[p.name];
+ return (
+
+
+
· {p.name}
+
最后活跃: {p.last_active}
+
+ {onArchive && path && (
+
+ )}
+
+ );
+}
+
+function FlywheelPanel({ onClose }: { onClose: () => void }) {
+ const { data: projects } = useQuery({
+ queryKey: ["projects-for-flywheel"],
+ queryFn: () => getProjects(),
+ });
+
+ const { projectPaths, projectsMap } = useMemo(() => {
+ const paths = (projects ?? [])
+ .map((p) => p.win_path || p.wsl_path || "")
+ .filter(Boolean);
+ const map: Record = {};
+ for (const p of (projects ?? [])) {
+ const path = p.win_path || p.wsl_path || "";
+ if (!path) continue;
+ // 用炼境项目名做 key
+ map[p.name] = path;
+ // 同时用路径末尾目录名做备用 key(usage.json 里存的是目录名)
+ const dirName = path.replace(/\\/g, "/").split("/").filter(Boolean).pop() ?? "";
+ if (dirName && !map[dirName]) map[dirName] = path;
+ }
+ return { projectPaths: paths, projectsMap: map };
+ }, [projects]);
+
+ const { data: stats, isLoading } = useQuery({
+ queryKey: ["flywheel-stats", projectPaths],
+ queryFn: () => getFlywheelStats(projectPaths),
+ enabled: projectPaths.length > 0,
+ });
+
+ const [muhePrompt, setMuhePrompt] = useState("");
+ const [generating, setGenerating] = useState(false);
+ const [copied, setCopied] = useState(false);
+ const [archiveMsg, setArchiveMsg] = useState(null);
+ const [archiving, setArchiving] = useState(null);
+ const [pendingArchive, setPendingArchive] = useState<{ path: string; name: string } | null>(null);
+
+ const handleGenerateMuhe = async () => {
+ if (!stats) return;
+ setGenerating(true);
+ try {
+ const result = await generateMuhePrompt(stats);
+ setMuhePrompt(result);
+ } finally {
+ setGenerating(false);
+ }
+ };
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(muhePrompt).then(() => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ });
+ };
+
+ const handleArchiveConfirm = async () => {
+ if (!pendingArchive) return;
+ const { path, name } = pendingArchive;
+ setPendingArchive(null);
+ setArchiving(name);
+ setArchiveMsg(null);
+ try {
+ const result = await archiveProjectDocs(path);
+ setArchiveMsg(`✓ 已归档 ${result.archived_files.length} 个文件 → ${result.archive_dir}`);
+ } catch (e) {
+ setArchiveMsg(`归档失败: ${String(e)}`);
+ } finally {
+ setArchiving(null);
+ }
+ };
+
+ const fmtPct = (v: number) => `${(v * 100).toFixed(1)}%`;
+
+ // Fix 2:去重——90天休眠的项目不在60天停转里重复展示
+ const stalledOnlyProjects = (stats?.stalled_projects ?? []).filter(
+ (p) => !(stats?.ai_doc_stale_projects ?? []).some((q) => q.name === p.name)
+ );
+
+ return (
+
+
+
+
飞轮健康
+ {stats && (
+
+ {stats.projects_with_data}/{stats.total_projects_scanned} 个项目有数据
+
+ )}
+
+
+
+
+ {isLoading ? (
+
聚合中…
+ ) : !stats || stats.projects_with_data === 0 ? (
+
+
暂无飞轮数据
+
打开各项目蓝图后数据将自动积累
+
+ ) : (
+ <>
+ {/* CONVENTIONS 版本队列对比 */}
+ {stats.cohorts.length > 0 && (
+
+
版本队列对比
+
+ {stats.cohorts.map((c) => (
+
+
+ v{c.conventions_version}
+ {c.project_count} 个项目
+
+ {/* Fix 4:标签改为"任务完成率",字段改为 completion_rate */}
+
+ 0.3} />
+ 0.5} />
+ 0.6} />
+
+
+
+ ))}
+
+
+ )}
+
+ {/* TOP blocked reasons */}
+ {stats.top_blocked_reasons.length > 0 && (
+
+
TOP 阻塞原因
+
+ {stats.top_blocked_reasons.map((r, i) => (
+ -
+ #{i + 1}
+ {r.reason}
+ ×{r.count}
+
+ ))}
+
+
+ )}
+
+ {/* Fix 2:仅展示"停转但未到90天"的项目 */}
+ {stalledOnlyProjects.length > 0 && (
+
+
+ 飞轮停转(60–90天无快照)
+
+ {stalledOnlyProjects.map((p, i) => (
+
+ ))}
+
+ )}
+
+ {/* AI文档休眠项目(≥90天) */}
+ {stats.ai_doc_stale_projects.length > 0 && (
+
+
+ AI文档休眠(90天+无快照)
+
+
+ 建议归档 .blueprint/modules/,清空 Claude 上下文负担。
+
+ {/* Fix 1:归档确认弹窗 */}
+ {pendingArchive ? (
+
+
+ 确认归档「{pendingArchive.name}」?
+
+
+ 将删除 .blueprint/modules/*.md,文件会备份到 .blueprint/archive/,但操作本身不可一键撤销。
+
+
+
+
+
+
+ ) : (
+ stats.ai_doc_stale_projects.map((p, i) => (
+
setPendingArchive({ path, name })}
+ />
+ ))
+ )}
+ {archiving && (
+ 归档 {archiving} 中…
+ )}
+ {archiveMsg && (
+
+ {archiveMsg}
+
+ )}
+
+ )}
+
+ {/* 迭代跳变项目 */}
+ {(stats.iteration_jumps ?? []).length > 0 && (
+
+
+ 迭代跳变(主动重构,完成率重置)
+
+ {(stats.iteration_jumps ?? []).map((j, i) => (
+
+ {j.project}
+ 迭代 {j.from_iteration}→{j.to_iteration}
+
+ {(j.rate_before * 100).toFixed(0)}%→{(j.rate_after * 100).toFixed(0)}%
+ {j.rate_after < j.rate_before - 0.3 && " ⚠️"}
+
+
+ ))}
+
+ )}
+
+ {/* 停滞模块 */}
+ {(stats.stalled_modules ?? []).length > 0 && (
+
+
+ 停滞模块(in_progress 但无可推进任务)
+
+
+ {(stats.stalled_modules ?? []).map((m, i) => (
+ - {m}
+ ))}
+
+
+ )}
+
+ {/* 梦核分析 */}
+
+
梦核分析
+
+ 生成包含以上数据的提示词,交给 Claude Opus 做固化/遗忘判断。
+
+ {!muhePrompt ? (
+
+ ) : (
+
+ )}
+
+ >
+ )}
+
+ );
+}
+
+function MetricRow({ label, value, good, invert = false }: {
+ label: string;
+ value: string;
+ good: boolean;
+ invert?: boolean;
+}) {
+ const isGood = invert ? !good : good;
+ return (
+
+ {label}
+
+ {value}
+
+
+ );
+}
diff --git a/src/components/dashboard/BlueprintPromptModal.tsx b/src/components/dashboard/BlueprintPromptModal.tsx
new file mode 100644
index 0000000..253ba39
--- /dev/null
+++ b/src/components/dashboard/BlueprintPromptModal.tsx
@@ -0,0 +1,229 @@
+import { useState, useEffect, useRef } from "react";
+import { generateBlueprintPrompt, syncBlueprintRules, type BlueprintStatus, type SyncResult } from "../../lib/commands";
+
+interface Props {
+ projectName: string;
+ projectPath: string;
+ bpStatus: BlueprintStatus["status"];
+ onClose: () => void;
+ onSynced?: () => void; // 同步完成后通知父组件刷新状态
+}
+
+// ── 规则同步确认模式(rules_outdated)────────────────────────────────────────
+
+function SyncConfirmView({
+ projectName, projectPath, onClose, onSynced,
+}: {
+ projectName: string;
+ projectPath: string;
+ onClose: () => void;
+ onSynced?: () => void;
+}) {
+ const [syncing, setSyncing] = useState(false);
+ const [result, setResult] = useState(null);
+ const [error, setError] = useState(null);
+
+ const handleSync = async () => {
+ setSyncing(true);
+ setError(null);
+ try {
+ const res = await syncBlueprintRules(projectPath);
+ setResult(res);
+ onSynced?.();
+ } catch (e) {
+ setError(String(e));
+ } finally {
+ setSyncing(false);
+ }
+ };
+
+ return (
+ <>
+ {/* 头部 */}
+
+
+
规则版本待更新
+
{projectName}
+
+
+
+
+ {/* 说明 */}
+
+
+
将执行以下操作:
+
+ - 覆盖写入
.blueprint/CONVENTIONS.md(升级至最新版本)
+ - 更新
CLAUDE.md 中的蓝图规则区块
+
+
不会影响项目的其他文件和蓝图内容。
+
+
+ {result && (
+
+
✓ 同步完成
+
{result.message}
+
+ )}
+
+ {error && (
+
+ {error}
+
+ )}
+
+
+ {/* 底部操作 */}
+
+
+ {!result && (
+
+ )}
+
+ >
+ );
+}
+
+// ── 提示词生成模式(none / content_stale)────────────────────────────────────
+
+function PromptView({
+ projectName, projectPath, bpStatus, onClose,
+}: {
+ projectName: string;
+ projectPath: string;
+ bpStatus: "none" | "content_stale";
+ onClose: () => void;
+}) {
+ const [prompt, setPrompt] = useState("");
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [copied, setCopied] = useState(false);
+ const textareaRef = useRef(null);
+
+ const promptType = bpStatus === "none" ? "init" : "sync";
+ const title = bpStatus === "none" ? "初始化蓝图" : "同步蓝图描绘";
+ const hint = bpStatus === "none"
+ ? "将以下提示词复制后粘贴给 Claude Opus,让它为项目创建蓝图"
+ : "将以下提示词复制后粘贴给 Claude Opus,让它更新项目蓝图";
+
+ useEffect(() => {
+ setLoading(true);
+ setError(null);
+ generateBlueprintPrompt(projectPath, promptType)
+ .then(setPrompt)
+ .catch((e) => setError(String(e)))
+ .finally(() => setLoading(false));
+ }, [projectPath, promptType]);
+
+ const handleCopy = () => {
+ navigator.clipboard.writeText(prompt).then(() => {
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ });
+ };
+
+ return (
+ <>
+ {/* 头部 */}
+
+
+
{title}
+
{projectName}
+
+
+
+
+ {/* 说明 */}
+
+
+ 🤖 {hint},由 Claude 负责理解和创作蓝图内容。
+
+
+
+ {/* 提示词区域 */}
+
+ {loading ? (
+
+ 生成提示词中…
+
+ ) : error ? (
+
+ ) : (
+
+
+ {/* 底部操作 */}
+
+
点击文本框可全选
+
+
+
+
+
+ >
+ );
+}
+
+// ── 主组件 ────────────────────────────────────────────────────────────────────
+
+export function BlueprintPromptModal({ projectName, projectPath, bpStatus, onClose, onSynced }: Props) {
+ const handleBackdrop = (e: React.MouseEvent) => {
+ if (e.target === e.currentTarget) onClose();
+ };
+
+ return (
+
+
+ {bpStatus === "rules_outdated" ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/src/components/dashboard/ProjectCard.tsx b/src/components/dashboard/ProjectCard.tsx
index 90b3290..944e6fc 100644
--- a/src/components/dashboard/ProjectCard.tsx
+++ b/src/components/dashboard/ProjectCard.tsx
@@ -1,6 +1,6 @@
import { useState, useRef, useEffect } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
-import { getActivity, getSettings, getProjectTools, openEditor, openTerminal, launchTool, gitStatus, gitFetch, gitPull, gitPush, gitUpstreamBehind, githubGetToken, githubGetAccount, gitCreateBranch, gitListBranches, gitCheckoutBranch, fetchActionsRuns, parseRepoPath, type Project, type Space, type ActionsRun } from "../../lib/commands";
+import { getActivity, getSettings, getProjectTools, openEditor, openTerminal, launchTool, gitStatus, gitFetch, gitPull, gitPush, gitUpstreamBehind, githubGetToken, githubGetAccount, gitCreateBranch, gitListBranches, gitCheckoutBranch, fetchActionsRuns, parseRepoPath, getBlueprintStatus, type Project, type Space, type ActionsRun, type BlueprintStatus } from "../../lib/commands";
import { useUIStore } from "../../store/ui";
import { PriorityBadge, StatusBadge } from "../ui/Badge";
import { ProjectToolsModal } from "./ProjectToolsModal";
@@ -8,9 +8,48 @@ import { ProjectEnvModal } from "./ProjectEnvModal";
import { CicdModal } from "./CicdModal";
import { PublishModal } from "./PublishModal";
import { BlueprintModal } from "./BlueprintModal";
+import { BlueprintPromptModal } from "./BlueprintPromptModal";
import { getOtherSpacesInfo, recordPushToRemote, formatRelativeTime, type SpacesJson } from "../../lib/spacesSync";
import { openUrl } from "@tauri-apps/plugin-opener";
+// ── 蓝图状态徽章 ──────────────────────────────────────────────────────────────
+
+const BP_BADGE: Record = {
+ none: { dot: "bg-gray-300", label: "无蓝图", title: "项目尚未接入蓝图,点击获取初始化提示词" },
+ rules_outdated: { dot: "bg-yellow-400", label: "规则待更新", title: "CONVENTIONS.md 版本落后,点击查看详情" },
+ content_stale: { dot: "bg-amber-400", label: "内容待同步", title: "蓝图内容可能落后于代码,点击获取同步提示词" },
+ synced: { dot: "bg-green-400", label: "已同步", title: "蓝图规则和内容均为最新" },
+};
+
+function BlueprintStatusBadge({
+ status, syncing, onOpenPrompt, onOpenViewer,
+}: {
+ status: BlueprintStatus["status"];
+ syncing: boolean;
+ onOpenPrompt: () => void;
+ onOpenViewer: () => void;
+}) {
+ const cfg = BP_BADGE[status];
+
+ const handleClick = () => {
+ if (status === "synced") { onOpenViewer(); return; }
+ onOpenPrompt(); // none / rules_outdated / content_stale
+ };
+
+ return (
+
+ );
+}
+
+
interface Props {
project: Project;
role?: string;
@@ -24,6 +63,7 @@ export function ProjectCard({ project: p, role, spacesJson }: Props) {
const [showCicd, setShowCicd] = useState(false);
const [showPublish, setShowPublish] = useState(false);
const [showBlueprint, setShowBlueprint] = useState(false);
+ const [showBlueprintPrompt, setShowBlueprintPrompt] = useState(false);
const [showBranchMenu, setShowBranchMenu] = useState(false);
const [newBranchName, setNewBranchName] = useState("");
const [creatingBranch, setCreatingBranch] = useState(false);
@@ -145,6 +185,18 @@ export function ProjectCard({ project: p, role, spacesJson }: Props) {
retry: false,
});
+ // 蓝图路径:优先 win_path;WSL 项目仅当 wsl_path 为 UNC 格式时可从 Windows 侧访问
+ const blueprintPath = p.win_path
+ || (p.wsl_path?.startsWith("\\\\wsl.") ? p.wsl_path : undefined);
+
+ const { data: bpStatus, refetch: refetchBpStatus, isPending: bpPending } = useQuery({
+ queryKey: ["blueprintStatus", p.id],
+ queryFn: () => getBlueprintStatus(blueprintPath!),
+ enabled: !!blueprintPath,
+ staleTime: 60000,
+ retry: false,
+ });
+
// PR 链接:从 repo_url 解析出 GitHub 路径
const prUrl = (() => {
if (!p.repo_url || !gitStat) return null;
@@ -317,6 +369,15 @@ export function ProjectCard({ project: p, role, spacesJson }: Props) {
))}
+ {/* 蓝图状态徽章:路径存在且查询已完成(成功或失败均显示) */}
+ {blueprintPath && !bpPending && (
+ setShowBlueprintPrompt(true)}
+ onOpenViewer={() => setShowBlueprint(true)}
+ />
+ )}
@@ -696,11 +757,21 @@ export function ProjectCard({ project: p, role, spacesJson }: Props) {
{showBlueprint && (p.win_path || p.wsl_path) && (
setShowBlueprint(false)}
/>
)}
+ {showBlueprintPrompt && blueprintPath && (
+ setShowBlueprintPrompt(false)}
+ onSynced={() => refetchBpStatus()}
+ />
+ )}
+
{/* Expanded activity */}
{expanded && (
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index 17f6596..aa4dc0a 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -59,7 +59,7 @@ export function Sidebar({ account, onLogout, page, onPageChange }: Props) {
{/* Logo */}
- Dev Manager
+ 炼境
{/* 导航区(随当前空间) */}
diff --git a/src/components/manage/ManagePage.tsx b/src/components/manage/ManagePage.tsx
index 3761c37..787d1ed 100644
--- a/src/components/manage/ManagePage.tsx
+++ b/src/components/manage/ManagePage.tsx
@@ -295,9 +295,37 @@ function GroupsTab({ spaceId }: { spaceId?: string }) {
{members.map((m) => {
const p = projects.find((x) => x.id === m.project_id);
+ const hasPath = !!(p?.win_path || p?.wsl_path);
return p ? (
{p.name}
+ {/* 平台标识 */}
+ {p.platform && (
+
+ {p.platform === "wsl" ? "WSL" : "Win"}
+
+ )}
+ {/* MCP 注入状态 */}
+ {hasPath ? (
+
+
+ MCP
+
+ ) : (
+
+ 无路径
+
+ )}
{m.role && {m.role}}
+ {/* ── MCP 服务 ── */}
+
+
+
MCP 服务
+
+
+ 运行中
+
+
+
+ 产品组 MCP Server,为组内项目的 Claude 提供跨项目上下文能力。配置好产品组成员后,每个项目的
+ {" "}.claude/settings.json{" "}
+ 会自动注入对应的 MCP 连接。
+
+
+
+
+ setMcpPort(e.target.value)}
+ min={1024}
+ max={65535}
+ />
+
+
+
+ 产品组 MCP 地址:
+
+ http://localhost:{mcpPort}/mcp/group/<group_id>
+
+
+
+
+
{/* 编辑器表单弹窗 */}
{formTarget !== undefined && (
)}
+
);
}
+/* ── 蓝图规则批量同步区块 ────────────────────────────────────────────────────── */
+
+interface SyncRow {
+ name: string;
+ path: string;
+ status: "pending" | "ok" | "error";
+ result?: SyncResult;
+ error?: string;
+}
+
+function BatchSyncSection() {
+ const [rows, setRows] = useState
([]);
+ const [running, setRunning] = useState(false);
+ const [done, setDone] = useState(false);
+
+ const handleSync = async () => {
+ setRunning(true);
+ setDone(false);
+
+ let projects: Awaited> = [];
+ try {
+ projects = await getProjects(undefined, undefined);
+ } catch {
+ setRunning(false);
+ return;
+ }
+
+ // 只处理有 win_path 的项目
+ const targets = projects
+ .filter((p) => p.win_path)
+ .map((p) => ({ name: p.name, path: p.win_path! }));
+
+ if (targets.length === 0) {
+ setRows([]);
+ setRunning(false);
+ setDone(true);
+ return;
+ }
+
+ const initial: SyncRow[] = targets.map((t) => ({ ...t, status: "pending" }));
+ setRows(initial);
+
+ const results = [...initial];
+ for (let i = 0; i < results.length; i++) {
+ try {
+ const result = await syncBlueprintRules(results[i].path);
+ results[i] = { ...results[i], status: "ok", result };
+ } catch (e) {
+ results[i] = { ...results[i], status: "error", error: String(e) };
+ }
+ setRows([...results]);
+ }
+
+ setRunning(false);
+ setDone(true);
+ };
+
+ return (
+
+
+
+
蓝图规则同步
+
+ 将最新 CONVENTIONS.md 同步至所有已注册项目
+
+
+
+
+
+ {rows.length > 0 && (
+
+ {rows.map((row) => (
+
+
+ {row.status === "ok" ? "✓" : row.status === "error" ? "✗" : "…"}
+
+
+ {row.name}
+ {row.status === "ok" && row.result && (
+ {row.result.message}
+ )}
+ {row.status === "error" && (
+ {row.error}
+ )}
+
+
+ ))}
+
+ )}
+
+ {done && rows.length === 0 && (
+ 没有找到包含 win_path 的项目。
+ )}
+
+ );
+}
+
/* ── 检查更新区块 ─────────────────────────────────────────────────────────────── */
type UpdateState =
@@ -526,7 +694,7 @@ function UpdateSection() {
关于 & 更新
-
Dev Manager
+
炼境
当前版本 v{appVersion}
diff --git a/src/lib/commands.ts b/src/lib/commands.ts
index 1751e27..daa11b7 100644
--- a/src/lib/commands.ts
+++ b/src/lib/commands.ts
@@ -908,6 +908,8 @@ export interface BlueprintData {
name: string;
description?: string;
updated?: string;
+ /** 当前迭代号,默认 1,重构已完成模块时递增 */
+ iteration?: number;
areas: BlueprintArea[];
modules: BlueprintModule[];
edges: BlueprintEdge[];
@@ -918,6 +920,93 @@ export interface BlueprintData {
export const getBlueprint = (projectPath: string) =>
invoke
("get_blueprint", { projectPath });
+export const generateBlueprintPrompt = (projectPath: string, promptType: "init" | "sync") =>
+ invoke("generate_blueprint_prompt", { projectPath, promptType });
+
+export interface BlueprintStatus {
+ status: "none" | "rules_outdated" | "content_stale" | "synced";
+ conventions_version?: string;
+ claude_md_managed: boolean;
+ claude_md_version?: string;
+ manifest_updated?: string;
+ master_version: string;
+}
+
+export interface SyncResult {
+ success: boolean;
+ conventions_written: boolean;
+ claude_md_action: string;
+ message: string;
+}
+
+export const getBlueprintStatus = (projectPath: string) =>
+ invoke("get_blueprint_status", { projectPath });
+
+export const syncBlueprintRules = (projectPath: string) =>
+ invoke("sync_blueprint_rules", { projectPath });
+
+export interface FlywheelCohort {
+ conventions_version: string;
+ project_count: number;
+ /** 可派发率:dispatched(可派发数) / total tasks(最新快照均值) */
+ dispatchable_rate: number;
+ /** 任务完成率:done / total tasks(最新快照均值,反映整体进度) */
+ completion_rate: number;
+ /** 飞轮持续率:30天内有快照的项目比例 */
+ continuity_rate: number;
+ /** 阻塞密度:blocked / total tasks(最新快照均值) */
+ blocked_density: number;
+}
+
+export interface BlockedReasonCount {
+ reason: string;
+ count: number;
+}
+
+export interface StalledProject {
+ name: string;
+ /** UTC 日期字符串 "YYYY-MM-DD",未知时为 "unknown" */
+ last_active: string;
+}
+
+export interface IterationJump {
+ project: string;
+ from_iteration: number;
+ to_iteration: number;
+ /** 跳变前最后一条快照的任务完成率 */
+ rate_before: number;
+ /** 跳变后第一条快照的任务完成率 */
+ rate_after: number;
+}
+
+export interface FlywheelStats {
+ cohorts: FlywheelCohort[];
+ top_blocked_reasons: BlockedReasonCount[];
+ /** 飞轮停转项目(60天无快照) */
+ stalled_projects: StalledProject[];
+ /** AI文档休眠项目(90天无快照) */
+ ai_doc_stale_projects: StalledProject[];
+ /** 迭代跳变记录(iteration 递增,说明发生了主动重构) */
+ iteration_jumps: IterationJump[];
+ /** 停滞模块(in_progress 但无可推进任务,格式 "project/module_id") */
+ stalled_modules: string[];
+ total_projects_scanned: number;
+ projects_with_data: number;
+}
+
+export const getFlywheelStats = (projectPaths: string[]) =>
+ invoke("get_flywheel_stats", { projectPaths });
+
+export const generateMuhePrompt = (stats: FlywheelStats) =>
+ invoke("generate_muhe_prompt", { stats });
+
+export interface ArchiveResult {
+ archived_files: string[];
+ archive_dir: string;
+}
+
+export const archiveProjectDocs = (projectPath: string) =>
+ invoke("archive_project_docs", { projectPath });
// ── Canvas State ───────────────────────────────────────────────────────────────
export const getCanvasState = (groupId: string) =>