feat: 新增 pnpm Monorepo+Expo 模板及蓝图模块扩展
- 新增 pnpm Monorepo + Expo Router 项目模板(含 Metro monorepo 路径解析及 Windows 兼容修复) - 扩展蓝图系统:新增 api-keys、server-registry、ssh-config-sync、vscode-remote 等 15+ 模块定义 - 新增 .blueprint/rules.md 项目私有规则支持,并在 CLAUDE.md 中集成 - 蓝图版本升级至 1.6.0,优化版本比较逻辑(语义化版本元组对比)
This commit is contained in:
parent
9660bcbc48
commit
4dd19574f0
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
version: 1.4.5
|
version: 1.6.0
|
||||||
updated: 2026-04-17
|
updated: 2026-04-19
|
||||||
source: dev-manager-tauri
|
source: dev-manager-tauri
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -15,7 +15,9 @@ source: dev-manager-tauri
|
|||||||
```
|
```
|
||||||
.blueprint/
|
.blueprint/
|
||||||
├── manifest.yaml ← 模块列表、关系、布局坐标
|
├── manifest.yaml ← 模块列表、关系、布局坐标
|
||||||
├── CONVENTIONS.md ← 本文件(Claude 行为规则)
|
├── CONVENTIONS.md ← 本文件(Claude 行为规则 + 全局推荐规则库)
|
||||||
|
├── rules.md ← 项目私有规则(可选,优先级高于全局规则)
|
||||||
|
├── usage.json ← 飞轮外环快照数据(炼境自动写入)
|
||||||
└── modules/
|
└── modules/
|
||||||
└── <module-id>.md ← 每个模块的详情 + 任务卡
|
└── <module-id>.md ← 每个模块的详情 + 任务卡
|
||||||
```
|
```
|
||||||
@ -32,6 +34,23 @@ source: dev-manager-tauri
|
|||||||
| 放弃了某个方向 | 删除对应模块和文件,或标记 status: abandoned |
|
| 放弃了某个方向 | 删除对应模块和文件,或标记 status: abandoned |
|
||||||
| **重构已完成的模块(迭代)** | **manifest.iteration 递增,旧任务卡保留,新建替代任务卡,模块加 `## 迭代记录`** |
|
| **重构已完成的模块(迭代)** | **manifest.iteration 递增,旧任务卡保留,新建替代任务卡,模块加 `## 迭代记录`** |
|
||||||
|
|
||||||
|
## 任务卡复杂度定义
|
||||||
|
|
||||||
|
| complexity | 含义 | 判断标准 | 推荐执行模型 |
|
||||||
|
|-----------|------|--------|------------|
|
||||||
|
| `S` | Simple | 单文件或机械操作;无需理解现有 pattern;可独立完成 | Haiku |
|
||||||
|
| `M` | Medium | 跨 2-4 个文件;需理解现有代码风格和上下文;有隐式依赖 | Sonnet |
|
||||||
|
| `L` | Large/Complex | 系统架构级;**无法直接执行,必须先 `/architect` 设计再 `/splitter` 拆分** | Opus(设计)+ Sonnet(执行) |
|
||||||
|
|
||||||
|
### 判断速查
|
||||||
|
|
||||||
|
- 改动涉及 3 个以上文件且分属不同功能区域 → 至少 M
|
||||||
|
- 需要先理解现有代码的内在逻辑或约定才能动手 → 至少 M
|
||||||
|
- 无法在派发时清晰描述完整实现方案 → L,需先 `/architect`
|
||||||
|
- 单纯的 SQL 建表、类型文件、配置修改 → S
|
||||||
|
|
||||||
|
> **注**:`/auto-execute` 技能会按此定义自动路由模型:S 卡委托 Haiku 子 agent,M 卡由当前 Sonnet 执行,L 卡直接标记 blocked 并提示走 `/architect`。
|
||||||
|
|
||||||
## 模块状态定义
|
## 模块状态定义
|
||||||
|
|
||||||
| 状态 | 含义 | 色标 |
|
| 状态 | 含义 | 色标 |
|
||||||
@ -82,6 +101,18 @@ modules/<id>.md 中的每个二级标题(`###`)是一张任务卡:
|
|||||||
|
|
||||||
不满足条件的 todo 任务保持无前缀,说明还需补充信息。
|
不满足条件的 todo 任务保持无前缀,说明还需补充信息。
|
||||||
|
|
||||||
|
### 💭 concept 卡的极简写法
|
||||||
|
|
||||||
|
concept 卡是想法 inbox,**不要求 files / acceptance / complexity**,随想随记:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### 💭 ServerManagerPanel 表格和软件块有重复逻辑
|
||||||
|
- status: concept
|
||||||
|
```
|
||||||
|
|
||||||
|
一行就够。等某次对话中方向变清晰,再补全字段、升格为 📋。
|
||||||
|
**不要因为"信息不全就不记"而让想法消失**——concept 卡存在的意义就是承接碎片。
|
||||||
|
|
||||||
## manifest.yaml 规范
|
## manifest.yaml 规范
|
||||||
|
|
||||||
### 顶层字段
|
### 顶层字段
|
||||||
@ -201,9 +232,13 @@ updated: YYYY-MM-DD
|
|||||||
Claude 在每次对话中应遵循以下流程:
|
Claude 在每次对话中应遵循以下流程:
|
||||||
|
|
||||||
### 阶段 1:进入对话 — 读取蓝图
|
### 阶段 1:进入对话 — 读取蓝图
|
||||||
|
|
||||||
```
|
```
|
||||||
读取 .blueprint/manifest.yaml → 了解项目全景和当前状态
|
1. 读取 .blueprint/manifest.yaml ← 项目全景和当前状态
|
||||||
|
2. 读取 .blueprint/CONVENTIONS.md ← 工作流规则 + 全局推荐规则
|
||||||
|
3. 读取 .blueprint/rules.md ← 项目私有规则(若存在,合并应用,优先级更高)
|
||||||
```
|
```
|
||||||
|
|
||||||
- 识别哪些模块 in_progress,哪些有可派发任务
|
- 识别哪些模块 in_progress,哪些有可派发任务
|
||||||
- 用简短话语告诉用户当前项目进展(如 "18/22 模块已完成,2 个进行中")
|
- 用简短话语告诉用户当前项目进展(如 "18/22 模块已完成,2 个进行中")
|
||||||
|
|
||||||
@ -268,6 +303,28 @@ Claude 应关注这些信号来决定是否更新蓝图:
|
|||||||
- 执行模型遇到无法解决的问题 → 标记 🔴 blocked + blocked_reason
|
- 执行模型遇到无法解决的问题 → 标记 🔴 blocked + blocked_reason
|
||||||
- Opus 看到 blocked 任务 → 分析原因,拆/补/调后产出新任务卡
|
- Opus 看到 blocked 任务 → 分析原因,拆/补/调后产出新任务卡
|
||||||
|
|
||||||
|
### 对话中的复杂度漂移检测
|
||||||
|
|
||||||
|
碎片化想法在对话中自然累积,有时从样式调整演变为架构改动。Claude 应主动识别并叫停。
|
||||||
|
|
||||||
|
**漂移信号**(以下任一出现即触发):
|
||||||
|
- 原始请求是样式/布局,但现在涉及组件 state、props 或后端命令
|
||||||
|
- 本次对话累计改动文件 ≥ 4 个,且分属不同功能区域
|
||||||
|
- 用户说了"顺便"、"既然这样"、"要不也把 XX"
|
||||||
|
- 前端改动已延伸到 DB 结构或 Rust 命令
|
||||||
|
|
||||||
|
**Claude 的应对动作**:检测到漂移信号时暂停执行,输出:
|
||||||
|
|
||||||
|
> ⚠️ 范围漂移:我们从「[原始请求]」走到了「[当前方向]」,累计涉及 [N] 个文件。建议:
|
||||||
|
> A) 完成当前这步,其余记为 💭 concept 卡待下次处理
|
||||||
|
> B) 现在拉清单,走 /architect 重新规划
|
||||||
|
> C) 继续执行,我在 session 结束前汇总成任务卡
|
||||||
|
|
||||||
|
**Session 结束汇总**:任何有代码改动但无预建任务卡的会话,结束前 Claude 应输出:
|
||||||
|
|
||||||
|
> 📋 本次会话实际改动:[文件列表 + 简述]
|
||||||
|
> 是否整理为蓝图任务卡?
|
||||||
|
|
||||||
## 交互质量复盘笔记
|
## 交互质量复盘笔记
|
||||||
|
|
||||||
> 本章节仅适用于**开发执行阶段**(实现代码时),初始化蓝图或同步蓝图时无需执行。
|
> 本章节仅适用于**开发执行阶段**(实现代码时),初始化蓝图或同步蓝图时无需执行。
|
||||||
@ -285,8 +342,15 @@ Claude 应关注这些信号来决定是否更新蓝图:
|
|||||||
返工原因: <若返工,一句话说明根因,如"WSL路径格式未预料">
|
返工原因: <若返工,一句话说明根因,如"WSL路径格式未预料">
|
||||||
有效策略: <本次奏效的做法,一句话>
|
有效策略: <本次奏效的做法,一句话>
|
||||||
遗留风险: <若有,一句话>
|
遗留风险: <若有,一句话>
|
||||||
|
auto_applied_rules: R01, R06 ← 本次任务触发并执行了哪些规则(无则省略)
|
||||||
|
rule_effectiveness: R01=有效, R06=有效 ← 规则是否阻止了问题(有效/无效/未验证)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**`auto_applied_rules` 填写说明**:
|
||||||
|
- 仅记录本次任务实际触发(Claude 主动检查或提示)的规则
|
||||||
|
- 若规则触发但用户选择跳过,也应记录(用于统计触发频率)
|
||||||
|
- `rule_effectiveness=无效` 表示规则触发了但没有阻止问题,这是最有价值的信号——梦核据此降低该规则置信度
|
||||||
|
|
||||||
### 触发时机
|
### 触发时机
|
||||||
|
|
||||||
| 情况 | 操作 |
|
| 情况 | 操作 |
|
||||||
@ -306,6 +370,8 @@ Claude 应关注这些信号来决定是否更新蓝图:
|
|||||||
返工原因: wsl_path 字段存的是 Windows UNC 格式(\\wsl.localhost\Ubuntu-22.04\...),而非 Linux 路径,导致 wsl 子命令收到错误参数
|
返工原因: wsl_path 字段存的是 Windows UNC 格式(\\wsl.localhost\Ubuntu-22.04\...),而非 Linux 路径,导致 wsl 子命令收到错误参数
|
||||||
有效策略: 在函数入口统一用 unc_to_distro_and_linux() 转换,一次解析,全程复用
|
有效策略: 在函数入口统一用 unc_to_distro_and_linux() 转换,一次解析,全程复用
|
||||||
遗留风险: Path::exists() 对 \\wsl.localhost\ 路径无效,所有 WSL 路径存在性检查必须走 wsl -d distro -- test -d/f
|
遗留风险: Path::exists() 对 \\wsl.localhost\ 路径无效,所有 WSL 路径存在性检查必须走 wsl -d distro -- test -d/f
|
||||||
|
auto_applied_rules: R10
|
||||||
|
rule_effectiveness: R10=未验证(任务开始前未触发,事后归因)
|
||||||
```
|
```
|
||||||
|
|
||||||
> 这些笔记不替代决策记录(决策记录记架构 WHY,复盘笔记记执行 HOW 和踩坑)。两者都写。
|
> 这些笔记不替代决策记录(决策记录记架构 WHY,复盘笔记记执行 HOW 和踩坑)。两者都写。
|
||||||
@ -362,10 +428,24 @@ Claude 应关注这些信号来决定是否更新蓝图:
|
|||||||
|
|
||||||
### 闭环路径
|
### 闭环路径
|
||||||
|
|
||||||
|
两条并行的反馈路径,速度不同:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
【快速路径 — 项目级,无需发版】
|
||||||
|
执行任务 → 复盘笔记(auto_applied_rules)
|
||||||
|
↓
|
||||||
|
梦核分析项目笔记
|
||||||
|
↓
|
||||||
|
输出项目私有规则草案
|
||||||
|
↓
|
||||||
|
用户确认 → 写入 .blueprint/rules.md
|
||||||
|
↓
|
||||||
|
下次对话立即生效
|
||||||
|
|
||||||
|
【慢速路径 — 全局级,需发版】
|
||||||
执行任务 → usage.json 快照
|
执行任务 → usage.json 快照
|
||||||
↓
|
↓
|
||||||
炼境聚合指标
|
炼境跨项目聚合指标
|
||||||
↓
|
↓
|
||||||
梦核提示词 → Claude Opus 分析
|
梦核提示词 → Claude Opus 分析
|
||||||
↓
|
↓
|
||||||
@ -378,7 +458,7 @@ Claude 应关注这些信号来决定是否更新蓝图:
|
|||||||
治理面板 sync_blueprint_rules → 分发到各项目
|
治理面板 sync_blueprint_rules → 分发到各项目
|
||||||
```
|
```
|
||||||
|
|
||||||
> **注意**:CONVENTIONS.md 编译进炼境二进制,更新需要修改炼境源码并发版,不支持运行时修改。这是有意为之的设计——保证规则版本可追溯,防止随意污染。
|
> **设计意图**:快速路径响应单个项目的学习,慢速路径沉淀跨项目的共识。CONVENTIONS.md 编译进炼境二进制保证全局规则版本可追溯;rules.md 存于项目本地实现快速迭代。两条路径互补,共同构成择升环。
|
||||||
|
|
||||||
## 无 MCP 环境兼容(Qwen Code / 其他 AI)
|
## 无 MCP 环境兼容(Qwen Code / 其他 AI)
|
||||||
|
|
||||||
@ -573,6 +653,195 @@ mod tests {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 项目私有规则(rules.md)
|
||||||
|
|
||||||
|
每个项目可以在 `.blueprint/rules.md` 中维护**仅对本项目生效**的推荐规则,格式与全局规则库完全相同。
|
||||||
|
|
||||||
|
### 优先级
|
||||||
|
|
||||||
|
```
|
||||||
|
项目私有规则(.blueprint/rules.md)> 全局规则(CONVENTIONS 自动推荐规则库)
|
||||||
|
```
|
||||||
|
|
||||||
|
项目私有规则更精准(来自该项目真实数据),全局规则更广泛(来自跨项目共识)。冲突时项目规则优先。
|
||||||
|
|
||||||
|
### 对话开始时的读取顺序
|
||||||
|
|
||||||
|
```
|
||||||
|
1. manifest.yaml ← 项目全景
|
||||||
|
2. CONVENTIONS.md ← 协议定义 + 全局规则
|
||||||
|
3. .blueprint/rules.md ← 项目私有规则(若存在,合并应用)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 梦核写入路径
|
||||||
|
|
||||||
|
梦核(飞轮阶段三启动后)分析项目复盘笔记后,除了输出 CONVENTIONS 更新建议,还应输出**项目私有规则草案**,格式如下:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 建议新增项目私有规则
|
||||||
|
|
||||||
|
| rule_id | 触发条件 | 自动动作 | confidence | evidence_count | derived_from |
|
||||||
|
|---------|---------|---------|-----------|---------------|-------------|
|
||||||
|
| P01 | <本项目特有触发条件> | <自动动作> | 0.70 | 3 | <复盘笔记引用> |
|
||||||
|
```
|
||||||
|
|
||||||
|
用户确认后,Claude 直接将规则写入 `.blueprint/rules.md`,**无需等待炼境发版**。这是项目级择升环与全局发版解耦的关键路径。
|
||||||
|
|
||||||
|
### rules.md 文件格式
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 项目私有规则
|
||||||
|
|
||||||
|
> 本文件由梦核分析 + 用户确认后生成,Claude 每次对话开始时读取并合并应用。
|
||||||
|
> 规则格式与 CONVENTIONS 自动推荐规则库相同,优先级高于全局规则。
|
||||||
|
|
||||||
|
## 规则列表
|
||||||
|
|
||||||
|
### P01 — 规则标题
|
||||||
|
- **触发条件**:xxx
|
||||||
|
- **自动动作**:xxx
|
||||||
|
- **confidence**: 0.70
|
||||||
|
- **evidence_count**: 3
|
||||||
|
- **derived_from**: 复盘笔记 YYYY-MM-DD,任务"xxx"中发现
|
||||||
|
```
|
||||||
|
|
||||||
|
### 何时创建 rules.md
|
||||||
|
|
||||||
|
- 梦核首次输出项目私有规则建议时创建
|
||||||
|
- 也可手动创建,记录人工识别的项目特有模式
|
||||||
|
- 文件不存在时跳过,不影响全局规则生效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 自动推荐规则库
|
||||||
|
|
||||||
|
> 本章节定义了 Claude 在开发过程中应**主动触发**的高收益决策规则。
|
||||||
|
> 规则有置信度(confidence)和证据数(evidence_count),置信度越高越应自动执行,无需用户确认。
|
||||||
|
|
||||||
|
### 执行策略
|
||||||
|
|
||||||
|
| confidence 范围 | 执行方式 |
|
||||||
|
|----------------|---------|
|
||||||
|
| ≥ 0.80 | 直接执行,仅一行说明("按 R01 检查...") |
|
||||||
|
| 0.60 ~ 0.79 | 执行前单句说明原因,用户可跳过 |
|
||||||
|
| < 0.60 | 仅作提醒,不自动执行 |
|
||||||
|
|
||||||
|
置信度由梦核(阶段三启动后)根据 evidence_count 和有效率自动更新;在此之前由人工在复盘中维护。
|
||||||
|
|
||||||
|
### scope 字段
|
||||||
|
|
||||||
|
每条规则有 `scope` 字段,标明适用范围。Claude 应在应用规则前检查当前项目是否匹配:
|
||||||
|
|
||||||
|
| scope 值 | 含义 |
|
||||||
|
|---------|------|
|
||||||
|
| `universal` | 适用于任何代码项目 |
|
||||||
|
| `tauri` | 仅适用于 Tauri 项目 |
|
||||||
|
| `rust` | 仅适用于含 Rust 后端的项目 |
|
||||||
|
| `sqlite` | 仅适用于使用 SQLite 的项目 |
|
||||||
|
| `react` | 仅适用于 React 前端项目 |
|
||||||
|
|
||||||
|
多个 scope 用 `+` 连接,如 `tauri+rust`。**scope 不匹配时跳过该规则,不作提醒。**
|
||||||
|
|
||||||
|
项目私有规则(`rules.md`)无需 scope 字段,默认对本项目全部生效。
|
||||||
|
|
||||||
|
### 规则列表
|
||||||
|
|
||||||
|
#### R01 — 新增 Tauri command 时检查前端封装层
|
||||||
|
- **scope**: `tauri`
|
||||||
|
- **触发条件**:在 `src-tauri/src/commands/` 下新增 `#[tauri::command]` 函数
|
||||||
|
- **自动动作**:读取 `src/lib/commands.ts`,确认是否有对应封装;缺失则提示补写,不阻止执行
|
||||||
|
- **confidence**: 0.90
|
||||||
|
- **evidence_count**: 8
|
||||||
|
- **derived_from**: 多次发生前后端接口断层(命令已实现但 commands.ts 未同步,前端调用报错)
|
||||||
|
|
||||||
|
#### R02 — 任务卡无 acceptance 时先补写再动代码
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:任务卡 `acceptance` 字段为空或缺失,但已准备开始执行
|
||||||
|
- **自动动作**:暂停执行,根据任务描述推导验收标准草稿,请用户确认后再写第一行代码
|
||||||
|
- **confidence**: 0.92
|
||||||
|
- **evidence_count**: 18
|
||||||
|
- **derived_from**: 无验收标准导致完成定义模糊,任务频繁反复("做完了但感觉不对")
|
||||||
|
|
||||||
|
#### R03 — M/L 任务标记 done 后写复盘笔记
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:M 或 L 复杂度的任务卡状态变更为 ✅ done
|
||||||
|
- **自动动作**:调用 `append_project_note` 写入复盘笔记(见"交互质量复盘笔记"章节格式)
|
||||||
|
- **confidence**: 0.88
|
||||||
|
- **evidence_count**: 24
|
||||||
|
- **derived_from**: 飞轮外环数据来源,无复盘笔记则梦核无输入,整个飞轮断链
|
||||||
|
|
||||||
|
#### R04 — 对话改动 ≥ 4 个跨功能区文件时触发漂移警告
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:单次对话累计改动文件 ≥ 4 个,且分属不同功能区(frontend/backend/infra)
|
||||||
|
- **自动动作**:输出范围漂移提示(见"对话中的复杂度漂移检测"章节),给出 A/B/C 三个选项
|
||||||
|
- **confidence**: 0.85
|
||||||
|
- **evidence_count**: 12
|
||||||
|
- **derived_from**: 多次"样式改动演变成后端重构"的失控扩散,最终要么返工要么留下技术债
|
||||||
|
|
||||||
|
#### R05 — SQLite 涉及新表或新字段时检查 migration
|
||||||
|
- **scope**: `sqlite`
|
||||||
|
- **触发条件**:在项目中出现 `CREATE TABLE` / `ALTER TABLE` / 新增列定义
|
||||||
|
- **自动动作**:检查是否有对应 migration 逻辑;缺失则提示添加,防止旧版本升级后 schema 断裂
|
||||||
|
- **confidence**: 0.82
|
||||||
|
- **evidence_count**: 7
|
||||||
|
- **derived_from**: 数据库 schema 变更未同步 migration,导致旧安装版本升级后启动报错
|
||||||
|
|
||||||
|
#### R06 — Rust 业务函数含 DB 操作时强制依赖注入
|
||||||
|
- **scope**: `rust+sqlite`
|
||||||
|
- **触发条件**:新增 Rust 函数内部直接获取 DB 连接(`pool().get()` 或等价写法)
|
||||||
|
- **自动动作**:提示改为依赖注入写法,连接由调用方传入;command 层负责获取连接
|
||||||
|
- **confidence**: 0.93
|
||||||
|
- **evidence_count**: 15
|
||||||
|
- **derived_from**: 飞轮阶段一实现时无测试导致静默 bug 上线;依赖注入是唯一能让 in-memory DB 测试覆盖业务逻辑的方式
|
||||||
|
|
||||||
|
#### R07 — 数据关键路径函数必须附带单元测试
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:新增函数负责数据采集、聚合、解析等关键路径(影响下游分析或展示的正确性)
|
||||||
|
- **自动动作**:在实现函数前提醒附带单元测试,覆盖正常路径 + 空数据边界;完成后验证测试全绿
|
||||||
|
- **confidence**: 0.95
|
||||||
|
- **evidence_count**: 6
|
||||||
|
- **derived_from**: 飞轮阶段一数据采集 bug 因无测试静默上线,污染所有下游分析
|
||||||
|
|
||||||
|
#### R08 — UI 状态显示必须来自真实检测
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:前端组件显示运行状态、健康状态、操作结果等实时信息
|
||||||
|
- **自动动作**:检查对应后端是否做了真实检测而非硬编码;发现硬编码状态时标记为需修复
|
||||||
|
- **confidence**: 0.87
|
||||||
|
- **evidence_count**: 5
|
||||||
|
- **derived_from**: 设置页 MCP 状态硬编码为"运行中",注入失败时 toast 始终显示成功——UI 掩盖真实问题
|
||||||
|
|
||||||
|
#### R09 — 自动化边界:事件触发只标 🔵,不标 ✅
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:讨论或实现基于外部事件(git commit、CI 结果等)自动更新任务卡状态
|
||||||
|
- **自动动作**:确认自动化只将任务卡置为 🔵 in_progress,绝不自动置为 ✅ done;完成语义需人工或 Claude 验收确认
|
||||||
|
- **confidence**: 0.91
|
||||||
|
- **evidence_count**: 4
|
||||||
|
- **derived_from**: 代码提交 ≠ 功能验收通过,自动标完成导致数据与实际不符
|
||||||
|
|
||||||
|
#### R10 — 对接外部协议/API 前先验证客户端实际行为
|
||||||
|
- **scope**: `universal`
|
||||||
|
- **触发条件**:实现与外部客户端的协议对接(MCP、LSP、WebSocket、SSE、OAuth 等)
|
||||||
|
- **自动动作**:在开始实现前,先通过文档或工具(curl、抓包)确认客户端实际发出的请求格式;不基于"通用规范"假设行为
|
||||||
|
- **confidence**: 0.85
|
||||||
|
- **evidence_count**: 3
|
||||||
|
- **derived_from**: Claude Code MCP 客户端用 GET 发起 SSE 握手,炼境只接受 POST 导致 405,MCP 整体失效
|
||||||
|
|
||||||
|
### 规则演进路径
|
||||||
|
|
||||||
|
```
|
||||||
|
人工识别(初始)
|
||||||
|
↓ evidence_count 累积 5+ 次
|
||||||
|
低置信验证期(confidence 0.60~0.79)
|
||||||
|
↓ 持续有效,evidence_count 15+
|
||||||
|
自动执行期(confidence ≥ 0.80)
|
||||||
|
↓ 发现无效或场景已变化
|
||||||
|
降级/归档(confidence 跌至 0.4 以下)
|
||||||
|
```
|
||||||
|
|
||||||
|
梦核(飞轮阶段三启动后)将根据复盘笔记中的 `auto_applied_rules` 和 `rule_effectiveness` 字段自动更新 confidence。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
- 不要删除已完成的任务卡,它们是项目历史的一部分
|
- 不要删除已完成的任务卡,它们是项目历史的一部分
|
||||||
|
|||||||
@ -6,7 +6,7 @@ description: >
|
|||||||
成为每个项目的贴身导师。炼境本身亦被纳入其中,
|
成为每个项目的贴身导师。炼境本身亦被纳入其中,
|
||||||
形成可自我进化的开发智能闭环。
|
形成可自我进化的开发智能闭环。
|
||||||
iteration: 1
|
iteration: 1
|
||||||
updated: 2026-04-17
|
updated: 2026-04-19
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
18
.blueprint/modules/api-keys.md
Normal file
18
.blueprint/modules/api-keys.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# API密钥管理
|
||||||
|
|
||||||
|
统一存储和管理各类 API 密钥(OpenAI、Anthropic、阿里云等),支持按服务商分类、显示/隐藏、复制到剪贴板,密钥加密存储在本地 SQLite。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 存本地 SQLite,不上云,炼境定位是本地工具,密钥不离开本机
|
||||||
|
- 目前不做系统 Keychain 集成,SQLite 存储已满足个人开发者场景;如有需求可在迭代二引入
|
||||||
|
- 支持「注入到项目环境变量」的快捷操作(配合 project-tools 模块)
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ API 密钥 CRUD 与安全展示
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/api_keys.rs, src-tauri/src/db.rs, src/components/dashboard/ApiKeysPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db
|
||||||
|
- acceptance: 可新增/编辑/删除 API 密钥;密钥默认隐藏,点击眼睛图标显示;支持一键复制;按服务商分类展示
|
||||||
16
.blueprint/modules/claude-autonomy-config.md
Normal file
16
.blueprint/modules/claude-autonomy-config.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Claude 自主性配置
|
||||||
|
|
||||||
|
炼境感知全局 Claude Beast 运行状态(安装情况、当前模式、bash 规则),在项目卡片提供只读状态展示 + CLI 操作引导,炼境本身不写入任何 Claude 配置。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 炼境只读,不写 Claude 配置:写入动作由用户通过 CLI 完成,炼境只做展示和引导,避免意外覆盖用户的 settings.json
|
||||||
|
- Beast 是全局状态,因此所有项目卡片都显示同一个 🤖 入口,而非每项目独立配置
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ Beast 全局状态读取与展示
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/claude_config.rs, src/components/dashboard/ClaudeConfigModal.tsx, src/components/dashboard/ProjectCard.tsx, src/lib/commands.ts
|
||||||
|
- acceptance: 项目卡片显示 🤖 按钮;点击弹窗展示当前 Beast 安装状态、运行模式、bash 规则;面板只读,提供 CLI 操作引导文案
|
||||||
17
.blueprint/modules/cloud-db-instances.md
Normal file
17
.blueprint/modules/cloud-db-instances.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 云数据库实例
|
||||||
|
|
||||||
|
云产品管理的子模块,管理云端数据库实例(MySQL、PostgreSQL、Redis 等),记录连接信息、展示实例状态,提供连接字符串快捷复制。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 炼境只做「连接信息管理」,不做数据库 GUI 客户端(那是 TablePlus/DBeaver 的职责)
|
||||||
|
- 连接信息与 api-keys 分开存:数据库连接串包含主机、端口、库名等配置参数,不只是密钥,放 cloud-db-instances 更语义清晰
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 云数据库实例信息管理
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/cloud_db_databases.rs, src-tauri/src/db.rs, src/components/dashboard/CloudProductsPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: cloud-products, sqlite-db
|
||||||
|
- acceptance: 云产品面板展示数据库实例列表;支持新增/编辑/删除;连接字符串可一键复制;按数据库类型(MySQL/PG/Redis)分类显示
|
||||||
17
.blueprint/modules/cloud-docker-apps.md
Normal file
17
.blueprint/modules/cloud-docker-apps.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 云产品 Docker 应用
|
||||||
|
|
||||||
|
云产品管理的子模块,管理部署在云服务器上的 Docker 容器/应用,展示运行状态、端口映射、镜像信息,支持基本的容器操作(启动/停止/重启)。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 作为 cloud-products 的子模块,复用服务器注册表和 SSH 连接基础设施
|
||||||
|
- 操作通过 SSH 执行 docker 命令,不依赖 Docker API 守护进程暴露端口,安全性更好
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ Docker 应用列表与状态管理
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/docker_apps.rs, src-tauri/src/db.rs, src/components/dashboard/CloudProductsPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: cloud-products, server-registry, ssh-config-sync
|
||||||
|
- acceptance: 云产品面板展示 Docker 应用列表,含状态、镜像、端口;支持启动/停止/重启操作;数据与服务器关联正确
|
||||||
17
.blueprint/modules/deploy-info.md
Normal file
17
.blueprint/modules/deploy-info.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 项目部署信息
|
||||||
|
|
||||||
|
记录和展示项目的部署环境配置(域名、服务器关联、部署路径、环境变量说明、部署脚本备注),作为项目的运维文档沉淀在炼境中。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 部署信息是「备忘」性质而非「执行」性质,炼境记录信息、不直接执行部署,避免炼境承载过多运维职责
|
||||||
|
- 与 server-registry 关联:部署目标服务器从注册表中选取,保证数据一致性
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 项目部署信息记录面板
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/deploy_info.rs, src-tauri/src/db.rs, src/components/dashboard/DeployInfoPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db, server-registry
|
||||||
|
- acceptance: 项目看板有「部署信息」Tab;可记录域名、部署服务器、路径、环境变量说明;数据持久化到 SQLite
|
||||||
@ -104,6 +104,7 @@
|
|||||||
- 任务完成时长分布(为 S/M/L 定义时间基准)
|
- 任务完成时长分布(为 S/M/L 定义时间基准)
|
||||||
- 高风险任务预警(特定文件组合 → 历史高返工率)
|
- 高风险任务预警(特定文件组合 → 历史高返工率)
|
||||||
- 跨项目模式库推荐
|
- 跨项目模式库推荐
|
||||||
|
- **自动推荐规则置信度更新**:梦核读取复盘笔记中的 `auto_applied_rules` + `rule_effectiveness` 字段,输出 CONVENTIONS 规则库的 confidence/evidence_count 更新建议;新增规则提案(从高频踩坑模式自动归纳)
|
||||||
|
|
||||||
> ⚠️ 阶段三为 L 级复杂度,到时须先走 /architect → /splitter 流程,禁止直接写代码。
|
> ⚠️ 阶段三为 L 级复杂度,到时须先走 /architect → /splitter 流程,禁止直接写代码。
|
||||||
|
|
||||||
|
|||||||
@ -1,35 +1,17 @@
|
|||||||
---
|
# GitHub 发布
|
||||||
id: github-publish
|
|
||||||
name: GitHub 发布
|
|
||||||
status: done
|
|
||||||
progress: 100
|
|
||||||
---
|
|
||||||
|
|
||||||
## 概述
|
在炼境内通过 GitHub OAuth 直接将本地项目推送到远程仓库,支持新建仓库或关联已有仓库,完成发布后自动回填 remote URL。
|
||||||
将本地项目发布到 GitHub,支持初始化 Git 仓库、创建远程仓库、推送代码、生成 .gitignore。
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 依赖 github-auth 模块提供的 access token,不在本模块持久化凭证
|
||||||
|
- 发布操作调用 GitHub REST API 创建仓库,然后通过 git2 库执行 push
|
||||||
|
|
||||||
## 任务卡
|
## 任务卡
|
||||||
|
|
||||||
### ✅ 扫描本地 Git 状态
|
### ✅ GitHub 仓库创建与推送
|
||||||
- status: done
|
- status: done
|
||||||
- files: src-tauri/src/commands/publish.rs
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/github.rs, src/components/dashboard/ProjectCard.tsx
|
||||||
检测项目是否已初始化 Git、是否有远程仓库。
|
- depends: github-auth
|
||||||
|
- acceptance: 未关联远程的项目可在看板卡片中一键发布到 GitHub;发布成功后 remote_url 写入 DB,卡片显示 GitHub 链接
|
||||||
### ✅ 创建 GitHub 仓库
|
|
||||||
- status: done
|
|
||||||
- files: src-tauri/src/commands/publish.rs
|
|
||||||
|
|
||||||
通过 GitHub API 创建新仓库。
|
|
||||||
|
|
||||||
### ✅ Git 初始化 & 首次推送
|
|
||||||
- status: done
|
|
||||||
- files: src-tauri/src/commands/publish.rs
|
|
||||||
|
|
||||||
git init、首次 commit、设置 remote、push。
|
|
||||||
|
|
||||||
### ✅ .gitignore 生成
|
|
||||||
- status: done
|
|
||||||
- files: src-tauri/src/commands/publish.rs
|
|
||||||
|
|
||||||
根据项目类型生成 .gitignore 文件。
|
|
||||||
|
|||||||
17
.blueprint/modules/host-apps.md
Normal file
17
.blueprint/modules/host-apps.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 宿主机应用管理
|
||||||
|
|
||||||
|
管理宿主机(Windows/Mac)上与开发相关的常用应用(IDE、工具、浏览器扩展等),支持快速启动和版本记录,作为开发环境的软件清单。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 宿主机应用与服务器软件分开管理:server-software 管远程,host-apps 管本地
|
||||||
|
- 以「记录与快启」为核心,不做安装管理(winget/brew 已有专门工具)
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 宿主机应用清单与快启
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/host_apps.rs, src-tauri/src/db.rs, src/components/dashboard/HostAppsPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db
|
||||||
|
- acceptance: 可添加/删除宿主机应用条目(名称、路径、版本备注);支持一键启动;数据存 SQLite
|
||||||
@ -27,3 +27,13 @@ progress: 100
|
|||||||
- files: src-tauri/src/commands/templates.rs
|
- files: src-tauri/src/commands/templates.rs
|
||||||
|
|
||||||
首次启动时自动插入预设模板。
|
首次启动时自动插入预设模板。
|
||||||
|
|
||||||
|
### ✅ P1+P3 模板扩充(React 生态全覆盖)
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/templates.rs
|
||||||
|
|
||||||
|
新增三个内置模板:
|
||||||
|
- `builtin-vite-react`:React 19 + TS + TailwindCSS v4 + React Router v7,纯前端 SPA 起点
|
||||||
|
- `builtin-tauri-react-tailwind`:Tauri v2 + React + TailwindCSS v4,客户端应用带样式
|
||||||
|
- `builtin-web-mobile-monorepo`:Web + Mobile 共享 Monorepo,packages/core(业务逻辑)+ packages/types + apps/web(Vite+Tailwind)+ apps/mobile(Expo+NativeWind)
|
||||||
|
|||||||
18
.blueprint/modules/server-registry.md
Normal file
18
.blueprint/modules/server-registry.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 服务器注册表
|
||||||
|
|
||||||
|
统一管理远程服务器信息(IP/域名、SSH 端口、用户名、描述、标签),是服务器管理体系的核心数据层,为 SSH Config 同步、VS Code Remote、软件管理等子功能提供基础数据。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 服务器信息存 SQLite 而非配置文件,方便跨模块查询和关联(项目与服务器多对多)
|
||||||
|
- 项目可关联多个服务器(部署环境),通过 project_servers 关联表实现
|
||||||
|
- 拆分为两个面板:ServerManagerPanel(全局服务器列表)+ ProjectServerPanel(项目维度的关联视图)
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 服务器 CRUD 与项目关联
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/servers.rs, src-tauri/src/db.rs, src/components/dashboard/ServerManagerPanel.tsx, src/components/dashboard/ProjectServerPanel.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db
|
||||||
|
- acceptance: 可新增/编辑/删除服务器;项目看板可关联/解除关联服务器;servers 表和 project_servers 关联表正确建立
|
||||||
18
.blueprint/modules/server-software.md
Normal file
18
.blueprint/modules/server-software.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 服务器软件管理
|
||||||
|
|
||||||
|
通过 SSH 远程查询服务器上已安装的软件(Nginx、Docker、Node.js 等),展示版本号和运行状态,支持常见软件的一键安装/启动/重启指令生成。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 查询通过 SSH 执行命令获取,不依赖服务器上预装的 agent,降低接入成本
|
||||||
|
- 仅覆盖高频运维软件,不做通用包管理器封装,避免过度复杂
|
||||||
|
- 指令生成而非直接执行:把安装命令展示给用户,由用户确认后在终端执行,避免炼境直接 SSH exec 引入安全风险
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 服务器软件扫描与展示
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/server_software.rs, src/components/dashboard/ServerSoftwareBlock.tsx
|
||||||
|
- depends: server-registry, ssh-config-sync
|
||||||
|
- acceptance: 服务器详情面板展示软件列表,含版本号和状态;支持刷新;已知软件有对应图标
|
||||||
17
.blueprint/modules/service-config.md
Normal file
17
.blueprint/modules/service-config.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 服务配置管理
|
||||||
|
|
||||||
|
管理项目依赖的外部服务配置(数据库连接字符串、消息队列地址、第三方 API endpoint 等),按环境(dev/staging/prod)分组存储,与 API 密钥管理互补。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 与 api-keys 区分:api-keys 存鉴权凭证,service-config 存服务地址和配置参数(非敏感或低敏感)
|
||||||
|
- 按环境分组是核心需求:同一服务在不同环境有不同配置,需要清晰区分
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 服务配置 CRUD 与环境分组
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/service_config.rs, src-tauri/src/db.rs, src/components/dashboard/ServiceConfigBlock.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db, server-registry
|
||||||
|
- acceptance: 项目看板可管理服务配置条目;支持按环境(dev/staging/prod)分组;CRUD 操作正常;数据存 SQLite
|
||||||
17
.blueprint/modules/source-library.md
Normal file
17
.blueprint/modules/source-library.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 源码库
|
||||||
|
|
||||||
|
管理个人收藏的优质开源项目、参考代码库、学习资源,支持按技术分类、标签过滤、快速跳转到仓库/本地路径,作为个人开发知识库的入口。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 独立页面而非 Dashboard 面板:源码库是横跨项目的全局资源,不属于某个具体项目,放顶层导航合适
|
||||||
|
- 与 repo-registry(项目仓库注册)区分:repo-registry 管理「正在做的项目」,source-library 管理「参考/学习用的仓库」
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ 源码库分类管理与项目详情
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/source_library.rs, src-tauri/src/db.rs, src/components/source-library/SourceLibraryPage.tsx, src/components/source-library/CategoryManager.tsx, src/components/source-library/ProjectFormModal.tsx, src/lib/commands.ts
|
||||||
|
- depends: sqlite-db
|
||||||
|
- acceptance: 导航栏有「源码库」入口;支持分类管理(增删改);每个条目有名称、URL、本地路径、标签、描述;支持按分类/标签过滤;数据存 SQLite
|
||||||
18
.blueprint/modules/ssh-config-sync.md
Normal file
18
.blueprint/modules/ssh-config-sync.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# SSH Config 自动同步
|
||||||
|
|
||||||
|
将炼境服务器注册表中的条目自动写入 `~/.ssh/config`,支持 Windows 宿主机和 WSL 两套 SSH Config 同步,使 `ssh server-alias` 直接可用。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 同步时采用「炼境专属 Block」写法(`# BEGIN LIANGJING` / `# END LIANGJING`),不影响用户手动维护的其他条目
|
||||||
|
- WSL 侧需额外同步一份,因为 WSL 和 Windows 各有独立的 `~/.ssh/config`
|
||||||
|
- 同步是主动触发(服务器变更时 + 用户手动点击),不做定时轮询
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ SSH Config 写入与同步
|
||||||
|
- status: done
|
||||||
|
- complexity: M
|
||||||
|
- files: src-tauri/src/commands/ssh_config.rs, src/components/dashboard/ServerManagerPanel.tsx
|
||||||
|
- depends: server-registry
|
||||||
|
- acceptance: 点击「同步 SSH Config」后,`~/.ssh/config` 出现炼境专属 Block,包含所有已注册服务器的 Host 条目;WSL 侧(若已配置)同步写入;重复触发幂等,不产生重复条目
|
||||||
17
.blueprint/modules/vscode-remote.md
Normal file
17
.blueprint/modules/vscode-remote.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# VS Code Remote SSH
|
||||||
|
|
||||||
|
在炼境服务器面板中一键用 VS Code 通过 Remote SSH 连接到指定服务器,复用 SSH Config 中已同步的 Host alias。
|
||||||
|
|
||||||
|
## 决策记录
|
||||||
|
|
||||||
|
- 依赖 ssh-config-sync 保证 Host alias 已写入 `~/.ssh/config`,不在本模块重复维护连接参数
|
||||||
|
- 直接调用 `code --remote ssh-remote+<alias> /path` 命令,复用 shell-launch 模块的进程启动能力
|
||||||
|
|
||||||
|
## 任务卡
|
||||||
|
|
||||||
|
### ✅ VS Code Remote SSH 一键连接
|
||||||
|
- status: done
|
||||||
|
- complexity: S
|
||||||
|
- files: src-tauri/src/commands/ssh_config.rs, src/components/dashboard/ServerManagerPanel.tsx
|
||||||
|
- depends: ssh-config-sync, shell-launch
|
||||||
|
- acceptance: 服务器列表每行有「VS Code」按钮;点击后 VS Code 打开并以 Remote SSH 连接对应服务器;SSH Config 未同步时给出提示
|
||||||
38
.blueprint/rules.md
Normal file
38
.blueprint/rules.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 项目私有规则
|
||||||
|
|
||||||
|
> 本文件是炼境(dev-manager-tauri)的项目级择升环规则库。
|
||||||
|
> 由梦核分析 + 用户确认后写入,Claude 每次对话开始时读取,优先级高于全局 CONVENTIONS 规则。
|
||||||
|
|
||||||
|
## 规则列表
|
||||||
|
|
||||||
|
### P01 — 炼境 blueprint 命令变更时同步治理分发逻辑
|
||||||
|
- **触发条件**:修改 `src-tauri/src/commands/blueprint*.rs` 中的数据结构或接口
|
||||||
|
- **自动动作**:检查 `blueprint_governance.rs` 的分发逻辑是否需要同步更新;特别检查 `sync_blueprint_rules` 命令的字段映射
|
||||||
|
- **confidence**: 0.82
|
||||||
|
- **evidence_count**: 4
|
||||||
|
- **derived_from**: blueprint 数据结构变更多次导致治理分发的字段不匹配,治理面板静默失效
|
||||||
|
|
||||||
|
### P02 — MCP 相关改动后必须做端到端连接测试
|
||||||
|
- **触发条件**:修改 `src-tauri/src/mcp_server.rs` 或 MCP 相关路由
|
||||||
|
- **自动动作**:提醒在改动后用 `curl -N` 实际测试 SSE 端点可达性;不能仅凭编译通过判断 MCP 功能正常
|
||||||
|
- **confidence**: 0.90
|
||||||
|
- **evidence_count**: 3
|
||||||
|
- **derived_from**: MCP SSE 握手问题(GET/POST 不匹配)编译完全正常,只有实际连接测试才能发现
|
||||||
|
|
||||||
|
### P03 — 梦核 prompt 构建函数变更后验证输出格式
|
||||||
|
- **触发条件**:修改 `src-tauri/src/commands/blueprint_feedback.rs` 中的 prompt 构建逻辑
|
||||||
|
- **自动动作**:在测试中断言 prompt 输出包含预期的结构段落(高频陷阱、规则更新建议等),防止格式静默退化
|
||||||
|
- **confidence**: 0.78
|
||||||
|
- **evidence_count**: 2
|
||||||
|
- **derived_from**: prompt 构建是纯字符串拼接,无类型约束,重构时容易遗漏段落
|
||||||
|
|
||||||
|
## 待验证规则(concept)
|
||||||
|
|
||||||
|
> 以下规则来自直觉,evidence_count 不足,暂不自动执行,仅作提醒。
|
||||||
|
|
||||||
|
### PC01 — usage.json 字段扩展时检查历史快照兼容性
|
||||||
|
- **触发条件**:向 usage.json snapshot 结构新增字段
|
||||||
|
- **自动动作**:提醒历史快照中该字段为空/缺失,下游聚合逻辑需处理 null/missing 情况
|
||||||
|
- **confidence**: 0.65
|
||||||
|
- **evidence_count**: 2
|
||||||
|
- **derived_from**: 阶段二扩展字段时发现旧快照无对应值,聚合时需要特判
|
||||||
File diff suppressed because it is too large
Load Diff
@ -42,7 +42,7 @@ docs/ 部署和更新指南
|
|||||||
- 代码重构/简化:/simplify → /code-review
|
- 代码重构/简化:/simplify → /code-review
|
||||||
- 发现深层问题:/Dev-deep-think
|
- 发现深层问题:/Dev-deep-think
|
||||||
|
|
||||||
<!-- BLUEPRINT_MANAGED_START version="1.4.5" -->
|
<!-- BLUEPRINT_MANAGED_START version="1.6.0" -->
|
||||||
## 项目蓝图
|
## 项目蓝图
|
||||||
|
|
||||||
本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
|
本项目使用 `.blueprint/` 目录维护项目全景架构图,驱动需求讨论和任务管理。
|
||||||
@ -51,6 +51,7 @@ docs/ 部署和更新指南
|
|||||||
|
|
||||||
1. 读取 `.blueprint/manifest.yaml` 了解项目全景和当前状态
|
1. 读取 `.blueprint/manifest.yaml` 了解项目全景和当前状态
|
||||||
2. 读取 `.blueprint/CONVENTIONS.md` 了解完整工作流规则
|
2. 读取 `.blueprint/CONVENTIONS.md` 了解完整工作流规则
|
||||||
|
3. 若 `.blueprint/rules.md` 存在,读取并与全局规则合并应用(项目私有规则,优先级高于全局)
|
||||||
|
|
||||||
### 需求讨论 → 蓝图同步规则
|
### 需求讨论 → 蓝图同步规则
|
||||||
|
|
||||||
|
|||||||
@ -1017,9 +1017,12 @@ pub fn get_blueprint_status(project_path: String) -> Result<BlueprintStatus, Str
|
|||||||
|
|
||||||
// 判断状态:对比当前生效版本(用户版优先,fallback 内置)
|
// 判断状态:对比当前生效版本(用户版优先,fallback 内置)
|
||||||
let effective_ver = effective_conventions_version();
|
let effective_ver = effective_conventions_version();
|
||||||
let rules_current = conventions_version.as_deref() == Some(effective_ver.as_str())
|
// 项目版本 >= 内置生效版本时视为规则已同步(项目可能领先于内置基线)
|
||||||
|
let project_ver_tuple = parse_semver_tuple(conventions_version.as_deref().unwrap_or("0.0.0"));
|
||||||
|
let effective_ver_tuple = parse_semver_tuple(&effective_ver);
|
||||||
|
let rules_current = project_ver_tuple >= effective_ver_tuple
|
||||||
&& claude_md_managed
|
&& claude_md_managed
|
||||||
&& claude_md_version.as_deref() == Some(effective_ver.as_str());
|
&& claude_md_version.as_deref().map(|v| parse_semver_tuple(v) >= effective_ver_tuple).unwrap_or(false);
|
||||||
|
|
||||||
let status = if !rules_current {
|
let status = if !rules_current {
|
||||||
"rules_outdated".to_string()
|
"rules_outdated".to_string()
|
||||||
|
|||||||
@ -690,6 +690,905 @@ pub fn run() {
|
|||||||
]"#),
|
]"#),
|
||||||
files: vec![],
|
files: vec![],
|
||||||
},
|
},
|
||||||
|
Tpl {
|
||||||
|
id: "builtin-pnpm-expo-monorepo",
|
||||||
|
name: "pnpm Monorepo + Expo",
|
||||||
|
description: "pnpm 多包 Monorepo:apps/ 内含 Expo Router (React Native) 应用,packages/ 内含共享 @repo/types 包。Metro 已配置 monorepo 路径解析(含 Windows 兼容修复),适合多 App 扩展场景。",
|
||||||
|
platform: "both",
|
||||||
|
tech_stack: "React Native,TypeScript,Expo,pnpm",
|
||||||
|
init_commands: "pnpm install",
|
||||||
|
variants: None,
|
||||||
|
files: vec![
|
||||||
|
// ── monorepo 根 ──────────────────────────────────────────────
|
||||||
|
("package.json", r#"{
|
||||||
|
"name": "{{project_name_kebab}}",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "pnpm --filter app start",
|
||||||
|
"android": "pnpm --filter app android",
|
||||||
|
"ios": "pnpm --filter app ios",
|
||||||
|
"lint": "pnpm -r lint"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18",
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("pnpm-workspace.yaml", "packages:\n - 'apps/*'\n - 'packages/*'\n"),
|
||||||
|
("tsconfig.base.json", r#"{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist", "build"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
(".gitignore", "node_modules/\ndist/\n.expo/\nandroid/\nios/\n*.log\n.env\n.env.*\n!.env.example\n"),
|
||||||
|
(".npmrc", "strict-peer-dependencies=false\nauto-install-peers=true\n"),
|
||||||
|
|
||||||
|
// ── packages/types ───────────────────────────────────────────
|
||||||
|
("packages/types/package.json", r#"{
|
||||||
|
"name": "@repo/types",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"import": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": ["src"],
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("packages/types/src/index.ts", "// 在此导出跨 App 共享的类型\nexport type {}\n"),
|
||||||
|
|
||||||
|
// ── apps/app ─────────────────────────────────────────────────
|
||||||
|
("apps/app/package.json", r#"{
|
||||||
|
"name": "app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "expo-router/entry",
|
||||||
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"android": "expo run:android",
|
||||||
|
"ios": "expo run:ios",
|
||||||
|
"lint": "eslint . --ext .ts,.tsx"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@repo/types": "workspace:*",
|
||||||
|
"expo": "~52.0.0",
|
||||||
|
"expo-router": "~4.0.0",
|
||||||
|
"expo-status-bar": "~2.0.0",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-native": "0.76.3",
|
||||||
|
"react-native-gesture-handler": "~2.20.0",
|
||||||
|
"react-native-reanimated": "~3.16.1",
|
||||||
|
"react-native-safe-area-context": "4.12.0",
|
||||||
|
"react-native-screens": "~4.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@types/react": "~18.3.12",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/app/app.json", r##"{
|
||||||
|
"expo": {
|
||||||
|
"name": "{{project_name}}",
|
||||||
|
"slug": "{{project_name_kebab}}",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "default",
|
||||||
|
"userInterfaceStyle": "light",
|
||||||
|
"scheme": "{{project_name_kebab}}",
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true,
|
||||||
|
"bundleIdentifier": "com.example.{{project_name_snake}}"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"package": "com.example.{{project_name_snake}}"
|
||||||
|
},
|
||||||
|
"plugins": ["expo-router"],
|
||||||
|
"experiments": {
|
||||||
|
"typedRoutes": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##),
|
||||||
|
("apps/app/tsconfig.json", r#"{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"paths": {
|
||||||
|
"@repo/types": ["../../packages/types/src/index.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/app/babel.config.js", r#"module.exports = function (api) {
|
||||||
|
api.cache(true)
|
||||||
|
return {
|
||||||
|
presets: ['babel-preset-expo'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
// Metro 配置:monorepo 路径解析 + Windows release 构建兼容修复
|
||||||
|
("apps/app/metro.config.js", r#"const { getDefaultConfig } = require('expo/metro-config')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const projectRoot = __dirname
|
||||||
|
const monorepoRoot = path.resolve(projectRoot, '../..')
|
||||||
|
|
||||||
|
const config = getDefaultConfig(projectRoot)
|
||||||
|
|
||||||
|
// Windows release 构建兼容:强制 unstable_serverRoot 为 projectRoot,
|
||||||
|
// 避免 Metro 与 RN Gradle 插件的路径基准不一致。
|
||||||
|
config.server = {
|
||||||
|
...config.server,
|
||||||
|
unstable_serverRoot: projectRoot,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 让 Metro 监听 monorepo 根目录,确保根 node_modules 可被解析
|
||||||
|
config.watchFolders = [monorepoRoot]
|
||||||
|
|
||||||
|
// 模块解析:优先查 app 自己的 node_modules,再查 monorepo 根
|
||||||
|
config.resolver.nodeModulesPaths = [
|
||||||
|
path.resolve(projectRoot, 'node_modules'),
|
||||||
|
path.resolve(monorepoRoot, 'node_modules'),
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = config
|
||||||
|
"#),
|
||||||
|
("apps/app/expo-env.d.ts", "/// <reference types=\"expo/types\" />\n\n// NOTE: This file should not be edited and should be in your git ignore\n"),
|
||||||
|
("apps/app/app/_layout.tsx", r#"import { Stack } from 'expo-router'
|
||||||
|
import { StatusBar } from 'expo-status-bar'
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen name="index" options={{ title: '{{project_name}}' }} />
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/app/app/index.tsx", r#"import { View, Text, StyleSheet } from 'react-native'
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.title}>{{project_name}}</Text>
|
||||||
|
<Text style={styles.subtitle}>Expo Router + pnpm Monorepo</Text>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, backgroundColor: '#fff' },
|
||||||
|
content: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: 8 },
|
||||||
|
title: { fontSize: 24, fontWeight: 'bold' },
|
||||||
|
subtitle: { fontSize: 14, color: '#666' },
|
||||||
|
})
|
||||||
|
"#),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// ── P1: React + Vite SPA ─────────────────────────────────────────────
|
||||||
|
Tpl {
|
||||||
|
id: "builtin-vite-react",
|
||||||
|
name: "React + Vite SPA",
|
||||||
|
description: "纯前端 Web 应用,React 19 + TypeScript + TailwindCSS v4 + React Router v7",
|
||||||
|
platform: "both",
|
||||||
|
tech_stack: "React,TypeScript,Vite,TailwindCSS",
|
||||||
|
init_commands: "pnpm install",
|
||||||
|
variants: None,
|
||||||
|
files: vec![
|
||||||
|
("package.json", r#"{
|
||||||
|
"name": "{{project_name_kebab}}",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^19",
|
||||||
|
"react-dom": "^19",
|
||||||
|
"react-router": "^7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"@vitejs/plugin-react": "^4",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5",
|
||||||
|
"vite": "^6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("vite.config.ts", r#"import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
});
|
||||||
|
"#),
|
||||||
|
("tsconfig.json", r#"{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("index.html", r#"<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{{project_name}}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#),
|
||||||
|
("src/main.tsx", r#"import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { BrowserRouter } from "react-router";
|
||||||
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
"#),
|
||||||
|
("src/App.tsx", r#"import { Routes, Route } from "react-router";
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("src/index.css", "@import \"tailwindcss\";\n"),
|
||||||
|
("src/pages/Home.tsx", r#"export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900">{{project_name}}</h1>
|
||||||
|
<p className="mt-2 text-gray-500">React + Vite + TailwindCSS</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
(".gitignore", "node_modules/\ndist/\n.env\n.env.*\n!.env.example\n"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// ── P1: Tauri + React + TailwindCSS ──────────────────────────────────
|
||||||
|
Tpl {
|
||||||
|
id: "builtin-tauri-react-tailwind",
|
||||||
|
name: "Tauri + React + Tailwind",
|
||||||
|
description: "桌面应用,Tauri v2 + React 19 + TypeScript + TailwindCSS v4",
|
||||||
|
platform: "both",
|
||||||
|
tech_stack: "Rust,Node,pnpm,React,TypeScript,Tauri,TailwindCSS",
|
||||||
|
init_commands: "pnpm install",
|
||||||
|
variants: None,
|
||||||
|
files: vec![
|
||||||
|
("package.json", r#"{
|
||||||
|
"name": "{{project_name_kebab}}",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tauri dev",
|
||||||
|
"build": "tauri build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2",
|
||||||
|
"react": "^19",
|
||||||
|
"react-dom": "^19"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4",
|
||||||
|
"@tauri-apps/cli": "^2",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"@vitejs/plugin-react": "^4",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5",
|
||||||
|
"vite": "^6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("vite.config.ts", r#"import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
|
export default defineConfig(async () => ({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
clearScreen: false,
|
||||||
|
server: {
|
||||||
|
port: 1420,
|
||||||
|
strictPort: true,
|
||||||
|
watch: {
|
||||||
|
ignored: ["**/src-tauri/**"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
"#),
|
||||||
|
("tsconfig.json", r#"{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("index.html", r#"<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{{project_name}}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#),
|
||||||
|
("src/main.tsx", r#"import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
"#),
|
||||||
|
("src/index.css", "@import \"tailwindcss\";\n"),
|
||||||
|
("src/App.tsx", r#"import { useState } from "react";
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||||
|
<div className="text-center space-y-4">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900">{{project_name}}</h1>
|
||||||
|
<p className="text-gray-500">Tauri v2 + React + TailwindCSS</p>
|
||||||
|
<button
|
||||||
|
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||||
|
onClick={() => setCount((c) => c + 1)}
|
||||||
|
>
|
||||||
|
count: {count}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
"#),
|
||||||
|
("src-tauri/tauri.conf.json", r#"{
|
||||||
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
|
"productName": "{{project_name_kebab}}",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"identifier": "com.example.{{project_name_snake}}",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "pnpm vite",
|
||||||
|
"devUrl": "http://localhost:1420",
|
||||||
|
"beforeBuildCommand": "pnpm vite build",
|
||||||
|
"frontendDist": "../dist"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "{{project_name}}",
|
||||||
|
"width": 1200,
|
||||||
|
"height": 800
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("src-tauri/Cargo.toml", r#"[package]
|
||||||
|
name = "{{project_name_snake}}"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = []
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "{{project_name_snake}}_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri = { version = "2", features = [] }
|
||||||
|
tauri-plugin-opener = "2"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
"#),
|
||||||
|
("src-tauri/build.rs", "fn main() {\n tauri_build::build()\n}\n"),
|
||||||
|
("src-tauri/src/main.rs", r#"// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
{{project_name_snake}}_lib::run()
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("src-tauri/src/lib.rs", r#"#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
pub fn run() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.invoke_handler(tauri::generate_handler![])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("src-tauri/capabilities/default.json", r#"{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Capability for the main window",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"opener:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
(".gitignore", "node_modules/\ndist/\n.env\n.env.*\n!.env.example\nsrc-tauri/target/\nsrc-tauri/gen/\n"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
// ── P3: Web + Mobile 共享 Monorepo ────────────────────────────────────
|
||||||
|
Tpl {
|
||||||
|
id: "builtin-web-mobile-monorepo",
|
||||||
|
name: "Web + Mobile Monorepo",
|
||||||
|
description: "跨平台代码复用:packages/core 共享业务逻辑,apps/web(Vite + Tailwind)+ apps/mobile(Expo + NativeWind)",
|
||||||
|
platform: "both",
|
||||||
|
tech_stack: "React,React Native,TypeScript,Expo,Vite,TailwindCSS,NativeWind,pnpm",
|
||||||
|
init_commands: "pnpm install",
|
||||||
|
variants: None,
|
||||||
|
files: vec![
|
||||||
|
// ── 根目录 ──────────────────────────────────────────────────────
|
||||||
|
("package.json", r#"{
|
||||||
|
"name": "{{project_name_kebab}}",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "pnpm --filter web dev",
|
||||||
|
"build:web": "pnpm --filter web build",
|
||||||
|
"start:mobile": "pnpm --filter mobile start",
|
||||||
|
"android": "pnpm --filter mobile android",
|
||||||
|
"ios": "pnpm --filter mobile ios",
|
||||||
|
"typecheck": "pnpm -r typecheck"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18",
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("pnpm-workspace.yaml", "packages:\n - 'apps/*'\n - 'packages/*'\n"),
|
||||||
|
("tsconfig.base.json", r#"{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist", "build"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
(".gitignore", "node_modules/\ndist/\nbuild/\n.expo/\nandroid/\nios/\n*.log\n.env\n.env.*\n!.env.example\n"),
|
||||||
|
(".npmrc", "strict-peer-dependencies=false\nauto-install-peers=true\n"),
|
||||||
|
|
||||||
|
// ── packages/types ──────────────────────────────────────────────
|
||||||
|
("packages/types/package.json", r#"{
|
||||||
|
"name": "@repo/types",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"import": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("packages/types/tsconfig.json", r#"{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("packages/types/src/index.ts", r#"// 在此定义跨平台共享的类型
|
||||||
|
export interface User {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiResponse<T> {
|
||||||
|
data: T
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
|
||||||
|
// ── packages/core ───────────────────────────────────────────────
|
||||||
|
("packages/core/package.json", r#"{
|
||||||
|
"name": "@repo/core",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"import": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@repo/types": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("packages/core/tsconfig.json", r#"{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("packages/core/src/index.ts", r#"// 在此导出跨平台共享的业务逻辑(纯 TS,不依赖任何平台 API)
|
||||||
|
export * from "./api"
|
||||||
|
"#),
|
||||||
|
("packages/core/src/api.ts", r#"import type { ApiResponse, User } from "@repo/types"
|
||||||
|
|
||||||
|
const BASE_URL = ""
|
||||||
|
|
||||||
|
export async function getUser(id: string): Promise<ApiResponse<User>> {
|
||||||
|
const res = await fetch(`${BASE_URL}/api/users/${id}`)
|
||||||
|
if (!res.ok) return { data: null as unknown as User, error: res.statusText }
|
||||||
|
const data = await res.json()
|
||||||
|
return { data }
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
|
||||||
|
// ── apps/web ────────────────────────────────────────────────────
|
||||||
|
("apps/web/package.json", r#"{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@repo/core": "workspace:*",
|
||||||
|
"@repo/types": "workspace:*",
|
||||||
|
"react": "^19",
|
||||||
|
"react-dom": "^19",
|
||||||
|
"react-router": "^7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"@vitejs/plugin-react": "^4",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5",
|
||||||
|
"vite": "^6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/web/tsconfig.json", r#"{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"paths": {
|
||||||
|
"@repo/core": ["../../packages/core/src/index.ts"],
|
||||||
|
"@repo/types": ["../../packages/types/src/index.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/web/vite.config.ts", r#"import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
});
|
||||||
|
"#),
|
||||||
|
("apps/web/index.html", r#"<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{{project_name}}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"#),
|
||||||
|
("apps/web/src/main.tsx", r#"import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { BrowserRouter } from "react-router";
|
||||||
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
"#),
|
||||||
|
("apps/web/src/index.css", "@import \"tailwindcss\";\n"),
|
||||||
|
("apps/web/src/App.tsx", r#"import { Routes, Route } from "react-router";
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/web/src/pages/Home.tsx", r#"export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900">{{project_name}}</h1>
|
||||||
|
<p className="mt-2 text-gray-500">Web + Mobile Monorepo</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
|
||||||
|
// ── apps/mobile ─────────────────────────────────────────────────
|
||||||
|
("apps/mobile/package.json", r#"{
|
||||||
|
"name": "mobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"main": "expo-router/entry",
|
||||||
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"android": "expo run:android",
|
||||||
|
"ios": "expo run:ios",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@repo/core": "workspace:*",
|
||||||
|
"@repo/types": "workspace:*",
|
||||||
|
"expo": "~52.0.0",
|
||||||
|
"expo-router": "~4.0.0",
|
||||||
|
"expo-status-bar": "~2.0.0",
|
||||||
|
"nativewind": "^4.0.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-native": "0.76.3",
|
||||||
|
"react-native-safe-area-context": "4.12.0",
|
||||||
|
"react-native-screens": "~4.1.0",
|
||||||
|
"tailwindcss": "^3.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@types/react": "~18.3.12",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/mobile/app.json", r##"{
|
||||||
|
"expo": {
|
||||||
|
"name": "{{project_name}}",
|
||||||
|
"slug": "{{project_name_kebab}}",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scheme": "{{project_name_kebab}}",
|
||||||
|
"userInterfaceStyle": "automatic",
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true,
|
||||||
|
"bundleIdentifier": "com.example.{{project_name_snake}}"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"package": "com.example.{{project_name_snake}}"
|
||||||
|
},
|
||||||
|
"plugins": ["expo-router"],
|
||||||
|
"experiments": {
|
||||||
|
"typedRoutes": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"##),
|
||||||
|
("apps/mobile/tsconfig.json", r#"{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"paths": {
|
||||||
|
"@repo/core": ["../../packages/core/src/index.ts"],
|
||||||
|
"@repo/types": ["../../packages/types/src/index.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/mobile/tailwind.config.js", r#"/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./app/**/*.{ts,tsx}"],
|
||||||
|
presets: [require("nativewind/preset")],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/mobile/global.css", "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"),
|
||||||
|
("apps/mobile/babel.config.js", r#"module.exports = function (api) {
|
||||||
|
api.cache(true)
|
||||||
|
return {
|
||||||
|
presets: ['babel-preset-expo'],
|
||||||
|
plugins: ['nativewind/babel'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/mobile/metro.config.js", r#"const { getDefaultConfig } = require('expo/metro-config')
|
||||||
|
const { withNativeWind } = require('nativewind/metro')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const projectRoot = __dirname
|
||||||
|
const monorepoRoot = path.resolve(projectRoot, '../..')
|
||||||
|
|
||||||
|
const config = getDefaultConfig(projectRoot)
|
||||||
|
|
||||||
|
config.watchFolders = [monorepoRoot]
|
||||||
|
config.resolver.nodeModulesPaths = [
|
||||||
|
path.resolve(projectRoot, 'node_modules'),
|
||||||
|
path.resolve(monorepoRoot, 'node_modules'),
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = withNativeWind(config, { input: './global.css' })
|
||||||
|
"#),
|
||||||
|
("apps/mobile/expo-env.d.ts", "/// <reference types=\"expo/types\" />\n"),
|
||||||
|
("apps/mobile/app/_layout.tsx", r#"import '../global.css'
|
||||||
|
import { Stack } from 'expo-router'
|
||||||
|
import { StatusBar } from 'expo-status-bar'
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen name="index" options={{ title: '{{project_name}}' }} />
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
("apps/mobile/app/index.tsx", r#"import { View, Text } from 'react-native'
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
return (
|
||||||
|
<View className="flex-1 items-center justify-center bg-gray-50">
|
||||||
|
<Text className="text-3xl font-bold text-gray-900">{{project_name}}</Text>
|
||||||
|
<Text className="mt-2 text-gray-500">Web + Mobile Monorepo</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for t in &templates {
|
for t in &templates {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user