diff --git a/.github/workflows/macos-build.yml b/.github/workflows/macos-build.yml new file mode 100644 index 0000000..3413595 --- /dev/null +++ b/.github/workflows/macos-build.yml @@ -0,0 +1,222 @@ +name: macOS Build · app/ClaudeCodeHistory + +# 构建 macOS 桌面应用 · 输出 .app 包 + .dmg +# +# 触发: +# - push 到 main 且动了 app/ 目录 → 构建 + 上传 artifact(不打 Release) +# - 打 tag v*.*.* → 构建 + 自动创建 GitHub Release 附 .dmg +# - workflow_dispatch → 手动触发 +# +# 签名 / 公证逻辑: +# - 无 AC_* secret → 跳过签名,产出 unsigned .dmg(用户首次打开要右键绕 Gatekeeper) +# - 有 AC_* secret → 用 codesign + notarytool 签名 + 公证 +# - 签名相关 secret: +# - AC_TEAM_ID · Apple Developer Team ID +# - AC_SIGNING_CERT · base64 编码的 .p12 证书 +# - AC_SIGNING_CERT_PASSWORD · .p12 密码 +# - AC_APPLE_ID · Apple ID 邮箱 +# - AC_APPLE_ID_PASSWORD · App-specific password +# +# 说明:目前 secret 未配置,workflow 会走无签名分支;未来配好后自动切签名版。 + +on: + push: + branches: [main] + paths: + - 'app/**' + - '.github/workflows/macos-build.yml' + workflow_dispatch: + inputs: + create-release: + description: 是否创建 GitHub Release(非 tag 触发时用) + required: false + default: 'false' + push: + tags: + - 'v*.*.*' + +permissions: + contents: write # 创建 Release 要写权限 + +jobs: + build: + name: Build macOS .app + .dmg + runs-on: macos-14 # Apple Silicon runner + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode.app + + - name: Show Xcode version + run: xcodebuild -version + + - name: 检测签名 Secret 是否配置 + id: check-signing + run: | + if [ -n "${{ secrets.AC_TEAM_ID }}" ] && [ -n "${{ secrets.AC_SIGNING_CERT }}" ]; then + echo "signing=true" >> $GITHUB_OUTPUT + echo "🔐 将使用 Apple Developer 证书签名 + 公证" + else + echo "signing=false" >> $GITHUB_OUTPUT + echo "⚠️ 无 AC_TEAM_ID / AC_SIGNING_CERT secret,产出未签名 .dmg" + echo " 用户首次打开需右键点'打开'绕过 Gatekeeper" + fi + + # ============ 签名分支(有 Secret 时)============ + - name: 导入签名证书(有 secret) + if: steps.check-signing.outputs.signing == 'true' + env: + SIGNING_CERT: ${{ secrets.AC_SIGNING_CERT }} + SIGNING_CERT_PASSWORD: ${{ secrets.AC_SIGNING_CERT_PASSWORD }} + run: | + # 解码 base64 证书 + echo "$SIGNING_CERT" | base64 -d > /tmp/cert.p12 + + # 创建临时 keychain 避免污染 runner + KEYCHAIN="build.keychain" + security create-keychain -p "tempPass" "$KEYCHAIN" + security default-keychain -s "$KEYCHAIN" + security unlock-keychain -p "tempPass" "$KEYCHAIN" + security set-keychain-settings -t 3600 -l "$KEYCHAIN" + + # 导入证书 + security import /tmp/cert.p12 -k "$KEYCHAIN" \ + -P "$SIGNING_CERT_PASSWORD" \ + -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "tempPass" "$KEYCHAIN" + + # 清理 p12 + rm -f /tmp/cert.p12 + + # ============ 构建 ============ + - name: Build (xcodebuild archive) + working-directory: app + run: | + SIGNING=${{ steps.check-signing.outputs.signing }} + + if [ "$SIGNING" = "true" ]; then + CODE_SIGN_ARGS="-allowProvisioningUpdates DEVELOPMENT_TEAM=${{ secrets.AC_TEAM_ID }} CODE_SIGN_STYLE=Automatic" + else + CODE_SIGN_ARGS="CODE_SIGN_IDENTITY=- CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO" + fi + + xcodebuild \ + -project ClaudeCodeHistory.xcodeproj \ + -scheme ClaudeCodeHistory \ + -configuration Release \ + -destination 'generic/platform=macOS' \ + -archivePath /tmp/build/ClaudeCodeHistory.xcarchive \ + $CODE_SIGN_ARGS \ + archive + + - name: 导出 .app(有签名,正式) + if: steps.check-signing.outputs.signing == 'true' + working-directory: app + run: | + cat > /tmp/exportOptions.plist < + + + + methoddeveloper-id + teamID${{ secrets.AC_TEAM_ID }} + + + EOF + xcodebuild -exportArchive \ + -archivePath /tmp/build/ClaudeCodeHistory.xcarchive \ + -exportPath /tmp/build/export \ + -exportOptionsPlist /tmp/exportOptions.plist + + - name: 导出 .app(无签名,直接从 archive 拷贝) + if: steps.check-signing.outputs.signing == 'false' + run: | + mkdir -p /tmp/build/export + cp -r /tmp/build/ClaudeCodeHistory.xcarchive/Products/Applications/*.app /tmp/build/export/ + + # ============ 打包 .dmg ============ + - name: 装 create-dmg 工具 + run: brew install create-dmg + + - name: 打包 .dmg + id: dmg + run: | + VERSION=${GITHUB_REF#refs/tags/v} + [ "$GITHUB_REF" = "$VERSION" ] && VERSION="0.0.0-dev" + DMG_NAME="EP-Code-AI-macOS-${VERSION}.dmg" + APP_PATH=$(ls -d /tmp/build/export/*.app | head -1) + + create-dmg \ + --volname "EP Code AI" \ + --window-size 600 400 \ + --icon-size 100 \ + --icon "$(basename $APP_PATH)" 150 200 \ + --app-drop-link 450 200 \ + --hdiutil-quiet \ + "/tmp/build/${DMG_NAME}" \ + "$APP_PATH" || true # create-dmg 有时返回非零但文件已生成 + + ls -la /tmp/build/*.dmg + echo "dmg_path=/tmp/build/${DMG_NAME}" >> $GITHUB_OUTPUT + echo "dmg_name=${DMG_NAME}" >> $GITHUB_OUTPUT + + # ============ 公证(有 secret 时)============ + - name: Notarize(公证 · 有 secret) + if: steps.check-signing.outputs.signing == 'true' + env: + APPLE_ID: ${{ secrets.AC_APPLE_ID }} + APPLE_PW: ${{ secrets.AC_APPLE_ID_PASSWORD }} + TEAM_ID: ${{ secrets.AC_TEAM_ID }} + run: | + # 提交公证 + xcrun notarytool submit "${{ steps.dmg.outputs.dmg_path }}" \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_PW" \ + --team-id "$TEAM_ID" \ + --wait + + # 把公证 staple 到 dmg + xcrun stapler staple "${{ steps.dmg.outputs.dmg_path }}" + + # ============ 上传 artifact ============ + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macos-dmg + path: ${{ steps.dmg.outputs.dmg_path }} + retention-days: 30 + + # ============ 创建 Release(tag 触发)============ + - name: Create Release(tag 推送时) + if: startsWith(github.ref, 'refs/tags/v') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION=${GITHUB_REF#refs/tags/} + SIGNING=${{ steps.check-signing.outputs.signing }} + NOTE="" + if [ "$SIGNING" = "false" ]; then + NOTE=$'\n\n⚠️ **本版本未签名**。首次打开请右键点"打开"绕过 Gatekeeper。签名版本在 Apple Developer 账号配置好后自动启用。' + fi + + # 如果 Release 不存在就建,存在就追加 + if gh release view "$VERSION" > /dev/null 2>&1; then + gh release upload "$VERSION" "${{ steps.dmg.outputs.dmg_path }}" --clobber + else + gh release create "$VERSION" \ + "${{ steps.dmg.outputs.dmg_path }}" \ + --title "$VERSION" \ + --notes "macOS Beta 构建${NOTE}" + fi + + - name: 运行结束摘要 + run: | + echo "## 构建完成" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| 项 | 值 |" >> $GITHUB_STEP_SUMMARY + echo "|----|-----|" >> $GITHUB_STEP_SUMMARY + echo "| 签名 | ${{ steps.check-signing.outputs.signing == 'true' && '✅ 已签名+公证' || '⚠️ 无签名' }} |" >> $GITHUB_STEP_SUMMARY + echo "| 产物 | ${{ steps.dmg.outputs.dmg_name }} |" >> $GITHUB_STEP_SUMMARY + echo "| artifact | [下载](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) |" >> $GITHUB_STEP_SUMMARY diff --git a/CHANGELOG.md b/CHANGELOG.md index 0be42be..f994caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,50 @@ --- -## [Unreleased] · Sprint 7 进行中 +## [Unreleased] · Sprint 7 规划完成 · 实施 S7.1-7.4 滚动推进 + +S7 按"规划 + 框架"范围完成:跨平台栈决策 + Swift 改造清单 + 打包 workflow。 +实际 Swift 代码修改分 4 轮 PR(S7.1-7.4)滚动推进,每轮发布一个 Beta milestone。 + +### [0.7.0-draft] · Sprint 7 规划完成(2026-04-20) + +**主题**: ADR 决策 · macOS 打包框架 · Swift 改造路线图 + +#### 新增 · 3 个 ADR +- **ADR-0001** 采用 4 种接入模式作为框架一等公民(回溯 Sprint 1 的决策)· Accepted +- **ADR-0002** 跨平台桌面应用技术栈 · **混合方案**:macOS 保留 Swift(资产保护)+ Linux/Windows 用 Tauri+Svelte(快速多平台)· Proposed + - 对比 Tauri / Electron / 保持原生 / 混合 4 方案,基于"团队规模 1 人 + 已有 Swift 骨架 2500 行 + 维护成本敏感"选混合 +- **ADR-0003** 自动更新策略 · 统一用 GitHub Release API 作为更新源(零付费 + 三平台一致)· Proposed + - severity frontmatter 区分 critical / recommended / optional 强制级别 + +#### 新增 · 实施规划 +- **Swift 改造清单** `docs/design/ui/swift-refactor-plan.md` · 列出 22 个 Swift 文件的具体改动: + - P0 · 8 项 · Beta 前必做(主视图/侧栏/聊天/向导/设置/供应商/命令面板/状态栏) + - P1 · 5 项 · 重要但可稍晚(登录/项目列表/新建向导/工作流/Prompt 注册表) + - P2 · 6 项 · Beta 后补(搜索/Artifact 增强/自动更新/收藏调整) + - 拆成 S7.1 - S7.4 四轮 PR 推进 + +#### 新增 · 构建基础设施 +- **`.github/workflows/macos-build.yml`** · macOS Beta 自动构建: + - **无签名分支**(当前默认):用户需右键打开绕 Gatekeeper + - **签名+公证分支**(预留):secrets 配好 AC_TEAM_ID 等 5 个后自动启用 + - push tag v*.*.* 自动创建 GitHub Release 附 .dmg +- **ADR 索引** `docs/adr/README.md` 由 `generate-adr-index.js` 生成 + +#### Sprint 7.x 推进路线 +| 轮次 | 目标 | 文件数 | +|------|------|--------| +| S7.1 | 基础三栏 + 向导 + 设置 | P0 #1-5(5) | +| S7.2 | 命令面板 + 状态栏 + 供应商 | P0 #6-8(3) | +| S7.3 | 登录 + 项目列表 + 新建向导 | P1 #9-11(3) | +| S7.4 | 场景工作流 + Prompt 注册表 | P1 #12-13(2) | + +Sprint 7 结束时:S7.1 + S7.2 macOS Beta 可下载(无签名)。 + +--- -Phase 2 第一 Sprint (S6) 完成。S7 启动: macOS Beta 打包 + 跨平台栈决策。 -### [0.7.0-draft] · Sprint 6 完成(2026-04-20) +### [Sprint 6] · 2026-04-20 · 完成 **主题**: 架构盘点 + UI/UX 设计稿 + 信息共享文档化 + 用户手册起步 diff --git a/PLAN.md b/PLAN.md index 45d6d13..b720610 100644 --- a/PLAN.md +++ b/PLAN.md @@ -15,7 +15,7 @@ > | S4 | 场景联动 + 度量闭环 | ✅ 完成(2026-04-18) | > | S5 | 统一 CLI + 对外发布 + 试点复盘 | ✅ 完成(2026-04-18) 🎉 | > | S6 | 架构盘点 + UI/UX 设计稿 + 手册起步 + 信息流文档化 | ✅ 完成(2026-04-20) | -> | **S7** | **macOS Beta 打包 + 跨平台栈决策** | **📋 Phase 2 计划中** | +> | **S7** | **跨平台栈决策(ADR-0002)+ macOS 改造清单 + 打包 workflow** | **🟡 规划/框架完成 · 代码留 S7.1-7.4** | > | S8 | 用户手册完整版 + v1.0 正式发布 | 📋 Phase 2 计划中 | > | S9 | 服务端 RFC + OTA 协议设计(不实现) | 📋 Phase 2 计划中 | > | S10 | 真实试点项目复盘 | 📋 Phase 2 计划中 | diff --git a/ROADMAP.md b/ROADMAP.md index e851d6e..24c54a9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,7 +19,7 @@ | S5 | Week 5 | 统一 CLI + 对外发布 + 试点复盘 | ✅ 完成 | | **Phase 2 · 落地与产品化** | | | | | S6 | Week 6 | 架构图 + UI/UX 设计稿 + 信息流 + 手册起步 | ✅ 完成 | -| S7 | Week 7 | macOS Beta(对齐设计稿)+ 跨平台栈决策 | 📋 计划中 | +| S7 | Week 7 | 跨平台栈决策(ADR-0002)+ macOS 改造清单 + 打包 workflow | 🟡 规划完成 · 代码实施 S7.1-7.4 | | S8 | Week 8 | 用户手册完整版 + Linux/Win Beta + v1.0 GA | 📋 计划中 | | S9 | Week 9 | 服务端同步 RFC + OTA 协议设计(不实现) | 📋 计划中 | | S10 | Week 10 | 真实试点项目复盘 | 📋 计划中 | @@ -28,6 +28,14 @@ ## 近期变更 +### 2026-04-20(Sprint 7 规划完成 · 代码留 S7.1-7.4) +- **ADR-0001** 采用 4 种接入模式作为框架一等公民(回溯 Sprint 1)· Accepted +- **ADR-0002** 跨平台桌面应用技术栈 · **混合方案**(macOS 保留 Swift + Linux/Win 用 Tauri+Svelte)· Proposed +- **ADR-0003** 自动更新策略 · GitHub Release 为统一更新源(三平台共享)· Proposed +- **Swift 改造清单** `docs/design/ui/swift-refactor-plan.md` · 19 项改动 + 4 轮 PR 推进计划 +- **macOS 打包 workflow** `.github/workflows/macos-build.yml` · 支持签名/无签名双路径(secret 未配时走无签名) +- **ADR 索引** `docs/adr/README.md` 自动生成 + ### 2026-04-20(Sprint 6 完成 · Phase 2 第一 Sprint) - **⑥ UI/UX 设计稿**: 17 张 HTML 原型 + 13 wireframes 文档(覆盖 17 个主题)+ 5 Mermaid flows + 映射表 - 核心: 主视图 / 向导 / 设置 / 命令面板 / Artifact / 供应商切换 / 搜索 / 右键菜单 / 空态 / 状态栏 diff --git a/docs/adr/0001-four-adoption-modes.md b/docs/adr/0001-four-adoption-modes.md new file mode 100644 index 0000000..aa0ff9c --- /dev/null +++ b/docs/adr/0001-four-adoption-modes.md @@ -0,0 +1,94 @@ +# ADR-0001: 采用 4 种接入模式作为框架一等公民 + +## 状态 + +**Accepted** — 已采纳,Sprint 1 起实施 + +## 日期 + +2026-04-17(决策日,对应 Sprint 1 开始)· 2026-04-20(补记为 ADR,此前以方法论文档存在) + +## 参与决策 + +| 姓名 | 角色 | +|------|------| +| 张工 | 架构师 / 主理人 | + +## 背景与上下文 + +EP Code AI 方法论最初只考虑"绿地项目"(从 0 建新项目)。但真实世界的项目处于四种不同阶段: +- 刚立项、未编码 +- 代码已写未上线 +- 已上线、定期迭代 +- 运行 1 年+、稳态运维 + +把全套方法论一次性套到**已上线项目**,会引起强烈抵触(团队不愿补 1 年前的 PRD)。 +把**运维稳态项目**强制走全套,是浪费(没有新需求了)。 + +### 要解决的问题 + +- 如何让不同阶段的项目都有**清晰的接入路径**? +- 如何避免"方法论 = 必须全吃下去"的错觉? + +### 约束 + +- 不能把方法论拆得太碎(降低内聚) +- 不能每个项目都定制一套(无法复用) +- 判定必须简单(5 分钟内能自判) + +## 备选方案 + +### 方案 A · 按团队规模分(小/中/大) + +- ❌ 规模和项目阶段是正交的,混用导致判定困难 + +### 方案 B · 按业务类型分(CRUD / 数据 / 算法 ...) + +- ❌ 业务类型和方法论契合度关系不大 + +### 方案 C · 按项目阶段分 4 种模式(选此) + +- ✅ 判定简单 · 5 分钟 +- ✅ 和方法论每场景的启用/裁剪对应 +- ✅ 支持模式间升级(A → B → C → D 不是单向) + +### 方案 D · 不区分,一套通吃 + +- ❌ 已被验证不可行(实际用户抵触) + +## 决定 + +采用**方案 C**:四种模式并列,用户在 `epcode init --mode=` 时显式选择。 + +- **A · 绿地项目**(Greenfield): 全套上车 +- **B · 开发中项目**(Mid-Dev): 从现在追溯补齐 +- **C · 运行迭代项目**(Iterating): 每版本加一层(5 迭代渐进) +- **D · 稳态运维项目**(Maintenance): 只用运维篇 + 最小开发规范 + +## 后果 + +### 正面 + +- 用户首次用框架时的"门槛"从"读完 5 篇方法论"降到"5 分钟判定 + 起步清单" +- CLI 的 `init --mode=X` 变成用户第一个命令,自然引导 +- 每种模式有独立的脚手架(`tools/cli/scaffolds/mode-{a,b,c,d}/`) +- 模板 / 脚本 / 集成 四种模式**共享**,只是"启用集合"不同 + +### 负面 + +- 文档和原型里需要到处标注"仅 X 模式适用",维护成本上升 +- 四个模式的脚手架模板需要单独维护 +- 未来新增模式(如"研究项目"?)会引入复杂度 + +### 需持续关注 + +- 每个 Sprint 评估:新加的工具/模板/脚本是否**真的四种模式都能用**? +- 如果某项只服务 1-2 种模式,必须在文档显式标注 + +## 相关 + +- `docs/chapters/00-adoption/` 四种模式的详细方法论 +- `tools/cli/commands/init.js` 模式选择的 CLI 实现 +- `tools/cli/scaffolds/mode-{a,b,c,d}/` 各模式脚手架 +- `docs/design/ui/prototype/17-new-project.html` GUI 向导的设计原型 +- [PLAN.md § 接入模式抽象](../../PLAN.md) 规划依据 diff --git a/docs/adr/0002-cross-platform-desktop-stack.md b/docs/adr/0002-cross-platform-desktop-stack.md new file mode 100644 index 0000000..2886f92 --- /dev/null +++ b/docs/adr/0002-cross-platform-desktop-stack.md @@ -0,0 +1,164 @@ +# ADR-0002: 跨平台桌面应用技术栈 · 混合方案 (macOS 保留 Swift + Linux/Windows 用 Tauri) + +## 状态 + +**Proposed** — Sprint 7 提议,评审窗口至 Sprint 8 启动前 + +## 日期 + +2026-04-20 + +## 参与决策 + +| 姓名 | 角色 | +|------|------| +| 张工 | 架构师 / 主理人 | +| _(待签)_ | 评审人 | + +## 背景与上下文 + +Phase 1 已产出 `app/ClaudeCodeHistory/` macOS Swift 骨架(22 个 .swift 文件)。 +Phase 2 Sprint 7-8 计划发布三平台桌面应用 Beta。必须决定: + +- **Linux / Windows 用什么技术栈?** +- **已有的 macOS Swift 代码保留还是扔掉重写?** + +这个决策会影响后续 2-3 个月的开发方向和团队技能投资。 + +### 要解决的问题 + +1. 三平台(macOS/Linux/Windows)**一致体验** vs **维护成本**的平衡 +2. 已有 Swift 代码(约 2500 行)是资产还是包袱 +3. 未来一年的人员学习成本(Rust / Web 前端 / 各平台原生 SDK) +4. 包体积(影响下载意愿)· 性能 · 内存占用 +5. 和 Phase 3 服务端 RFC(PLAN ④)的衔接 + +### 约束 + +- 团队目前 1 人主理,不能同时维护 3 套原生代码库 +- 项目核心价值在**方法论 + 工具链 + Prompt**,桌面应用只是"GUI 壳",不应占用过多时间 +- 零付费原则(已决定不用 Docusaurus 付费版等) +- 必须和 Sprint 6 UI 设计稿对齐(17 个原型页) + +## 备选方案 + +### 方案 A · Tauri(Rust + Web 前端)· 一套代码跨三平台 + +- **优点**: + - 包小(约 5-15MB · vs Electron 100MB+) + - Rust 安全 + 性能好 + - 一套代码跨三平台,维护成本最低 + - Tauri 2.x 生态已稳定 +- **缺点**: + - 现有 Swift 代码**几乎全部作废**(2500 行重写,4-6 周) + - 团队需学 Rust + Web 前端(React/Vue/Svelte 选一) + - 原生功能集成(Touch Bar / Spotlight 等 macOS 特色)需 Rust plugin +- **包**: ~10MB +- **重写成本**: 4-6 周 + +### 方案 B · Electron(JS + Web 前端)· 一套代码跨三平台 + +- **优点**: + - 生态最熟(VSCode / Slack / Discord / ChatGPT 桌面版 都用它) + - 团队 Node.js 已熟,Web 前端学习曲线浅 + - 丰富的 npm 插件生态 +- **缺点**: + - 包大(100MB+)· 内存占用高(~300MB 空闲) + - 现有 Swift 代码作废 + - Chromium 每次升级都要跟 +- **包**: ~120MB +- **重写成本**: 3-4 周(比 Tauri 快,生态成熟) + +### 方案 C · 保持三平台原生(Swift + WinUI3 + GTK)· 不共享代码 + +- **优点**: 每平台最佳体验 +- **缺点**: + - **三套代码库**,维护成本 3x + - 一个功能要实现 3 次,UI 更新要同步 3 次 + - 需要 3 种技术栈专家(Swift / C# / C++ 或 Vala) +- **重写成本**: 实质是继续 Swift(保持) + WinUI3 全新 + GTK 全新 = 6+ 周 + +### 方案 D · 混合方案(**选此**):macOS 保留 Swift + Linux/Windows 用 Tauri + +- **优点**: + - **macOS Swift 代码不浪费**(Phase 1 投入保留) + - Linux/Windows 用 Tauri 快速起步(2-3 周) + - 设计稿(Sprint 6 17 原型)是**两套实现共同源** + - 未来 Tauri 版本稳定后,macOS 可平滑切 Tauri 实现大一统(保留选择权) + - 团队学习新栈(Rust)规模小,先从 Linux/Win 开始,macOS 继续维护已会的 Swift +- **缺点**: + - 短期**维护两套代码** + - 两套实现可能出现功能漂移,需要设计稿严格约束 + 定期对齐 + - Sprint 7 实际分两条线(macOS Swift 调整 + Tauri 从零起) +- **包**: macOS DMG ~20MB + Tauri ~10MB · 各平台独立安装 +- **重写成本**: macOS Swift 调整 2 周 + Tauri Linux/Win 3 周 = **总 5 周,可并行缩到 3 周** + +## 决定 + +采用 **方案 D · 混合方案**: +- **macOS**(Sprint 7): 继续用现有 Swift,按 UI 设计稿调整,Beta 打包 +- **Linux/Windows**(Sprint 8): 用 Tauri 从零开始,参照同一份 UI 设计稿 +- **共同源**: `docs/design/ui/prototype/` 作为两套实现的**权威 UI 规范** +- **长期选项**: 12 个月内观察 Tauri 版是否在功能完整度和性能上**追平** Swift 版;若是,考虑 macOS 也切 Tauri 实现统一 + +## 后果 + +### 正面 + +- **资产保护**: Swift 代码不浪费,Phase 1 投入延续 +- **快速多平台**: Linux/Win 用 Tauri 从零起,速度比原生快 2-3 倍 +- **低团队学习成本**: Rust 只在 Tauri 侧学,macOS 侧继续 Swift +- **未来可统一**: 保留 12 个月后切换到统一 Tauri 方案的选项 +- **设计稿 ROI 最大化**: 17 个原型两套实现都参照,设计稿的价值放大 2 倍 + +### 负面 + +- **维护两套代码**至少 12 个月(直到决定是否大一统) +- **功能漂移风险**:macOS 可能先发新功能,Linux/Win 跟不上 + - **缓解**: 所有新功能必须先改设计稿 → 两个 issue(macOS 实现 + Tauri 实现)同时开 +- **测试负担**: CI 要跑两套 build + 两套测试 +- **CHANGELOG 复杂**: 记录"macOS 侧改了什么 · Tauri 侧改了什么" + +### 需持续关注 + +- **每月对齐一次**:看 macOS 和 Tauri 版本功能 gap,写到 PLAN.md 的"待对齐清单" +- **Tauri 版本成熟度**:等 Tauri v3 发布(预计 2026 年底)时重新评估是否大一统 +- **用户反馈**:如果两套体验差异太大,提前切换统一方案 + +### 技术栈细化(Tauri 侧) + +- **后端**: Rust + Tauri v2.x +- **前端**: **Svelte 5 + TypeScript**(选 Svelte 理由: 包更小 / 语法更接近 HTML 原型 / 不强制虚拟 DOM / 团队学习曲线最浅) +- **样式**: CSS · 复用 `docs/design/ui/prototype/styles.css` 的 design tokens +- **状态管理**: Svelte stores(原生,不引入 Redux 类库) +- **HTTP/AI 调用**: 统一走 Rust 侧 command(避免前端处理 API Key) + +## 实施路径 + +### Sprint 7(本 Sprint)· 只做规划 + +- ✅ 写本 ADR +- ✅ 写 ADR-0003 自动更新策略 +- ✅ 写 Swift 改造清单(`docs/design/ui/swift-refactor-plan.md`) +- ✅ 写 macOS 打包 workflow(无签名 Beta) +- ❌ 实际修改 Swift 代码 · 留给 Sprint 7.x 实施 + +### Sprint 8 · Linux/Win Tauri 项目起步 + +- 新建 `app-tauri/` 目录 +- Tauri 初始化 + Svelte 骨架 +- 实现主视图三栏 + 首次启动向导 + 设置页(3 个最核心页面) +- Linux AppImage / Windows MSI 打包 workflow + +### Sprint 9-10 · 两平台功能追平 macOS + +- Tauri 补齐所有 17 个页面 +- macOS Swift 完成设计稿对齐 +- 统一 Release v1.0 + +## 相关 + +- Sprint 6 ⑥ [UI/UX 设计稿](../design/ui/) · 两套实现的共同源 +- [ADR-0003](./0003-auto-update-strategy.md) · 自动更新策略 +- [swift-refactor-plan.md](../design/ui/swift-refactor-plan.md) · Swift 改造清单 +- PLAN.md Phase 2 Sprint 7-8 · 实施时间表 diff --git a/docs/adr/0003-auto-update-strategy.md b/docs/adr/0003-auto-update-strategy.md new file mode 100644 index 0000000..9d8fb5d --- /dev/null +++ b/docs/adr/0003-auto-update-strategy.md @@ -0,0 +1,164 @@ +# ADR-0003: 桌面应用自动更新策略 · GitHub Release 为源 + +## 状态 + +**Proposed** — Sprint 7 提议 + +## 日期 + +2026-04-20 + +## 参与决策 + +| 姓名 | 角色 | +|------|------| +| 张工 | 架构师 | + +## 背景与上下文 + +桌面应用要有自动更新通道,否则用户永远停留在装机时的版本,出新功能 / 修 bug 推不下去。 + +跨平台 + 无付费服务器 + 混合方案(ADR-0002 macOS Swift + Linux/Win Tauri) 的约束下, +需要一套**统一**的更新发现 + 下载 + 应用机制。 + +### 要解决的问题 + +- 新版发布后,如何让**已安装用户**知道? +- 下载安装包从哪里来(不能用企业 CDN 因为零付费)? +- macOS / Linux / Windows 三平台的更新机制差异很大,如何统一? +- 强制更新 vs 可选更新如何区分(安全漏洞 / 一般新功能)? + +### 约束 + +- 零付费服务器,不能用 Cloudflare/AWS CDN +- 必须跨 3 平台一致 +- 用户隐私:更新检查不上报任何 PII + +## 备选方案 + +### 方案 A · Sparkle (macOS 原生)+ 各平台自建 + +- **优点**: macOS 生态最成熟,VSCode / 众多 Mac 应用用它 +- **缺点**: 只管 macOS;Linux/Win 要各自造轮子 +- **不适合混合方案** + +### 方案 B · Tauri Updater(Tauri 官方) + +- **优点**: Tauri 2.x 内置,跨三平台一致 +- **缺点**: + - 只在 Tauri 侧工作;**macOS Swift 版本要单独实现** + - 需要一个"update server"(但支持静态文件托管,可用 GitHub Release) + +### 方案 C · 自研统一协议(选此,简化版) + +基于 GitHub Release 的 JSON manifest + HTTP 下载,三平台共用: + +- **服务端**: GitHub Release · 完全免费 · API 公开 +- **客户端**: 每次启动 + 每 24h 检查一次 manifest,发现新版提示用户 +- **manifest**: `https://api.github.com/repos/epcode-ai/ep-code-ai/releases/latest` + - 自动包含 `tag_name / body / assets` +- **增量更新**: Phase 3 再考虑,目前全量下载(Tauri ~10MB 可接受) + +## 决定 + +采用 **方案 C · 基于 GitHub Release 的统一协议**,三平台共用核心逻辑 + 平台特定安装器。 + +### 流程 + +```mermaid +sequenceDiagram + participant App as 桌面应用 + participant GH as GitHub Releases API + participant User as 用户 + + App->>GH: GET /releases/latest + GH-->>App: { tag_name, body, assets: [.dmg, .AppImage, .msi] } + App->>App: 比对本地版本 vs tag_name + + alt 有新版 + App->>User: 通知("v0.7.0 可用,更新日志...") + User->>App: 点"更新" + App->>GH: 下载对应平台 asset + GH-->>App: 安装包 + App->>App: 替换自身(平台特定方式) + App->>User: 请重启生效 + else 无新版 + App->>App: 24h 后再查 + end +``` + +### 平台特定的"替换自身" + +| 平台 | 方式 | +|------|------| +| macOS | 解压 .dmg → 替换 `/Applications/EP Code AI.app` → 要求用户重启 | +| Linux | AppImage 直接替换文件 + 重启;deb 调 `dpkg -i` | +| Windows | 下载 .msi → 启动 msiexec · 应用关闭自身 | + +### 强制更新 vs 可选更新 + +Release 的 body 用 frontmatter 约定: + +```yaml +--- +severity: critical | recommended | optional +min-supported: 0.5.0 +--- + +## 新版本特性 +... +``` + +- `severity: critical` 或本地版本 < `min-supported` → **强制**,应用无法使用直到更新 +- `severity: recommended` → 弹通知,用户可选"稍后" +- `severity: optional` → 仅在设置页显示"有新版可用" + +## 后果 + +### 正面 + +- **零服务器成本**: 完全依赖 GitHub +- **三平台统一**: 核心协议相同,只有"替换自身"差异 +- **签名验证**: GitHub Release asset 的 SHA256 由 API 自带,可验完整性 +- **用户透明**: 下载 URL 是 github.com,用户知道来源 + +### 负面 + +- **依赖 GitHub**: GitHub 出事时无法更新(但整个开源生态都这样) +- **rate limit**: GitHub API 匿名请求 60/h,用户量大时需要加 token 或缓存 + - 缓解: 客户端缓存 24h,不重复查 +- **增量更新**: 暂不支持,每次全量下载 10-20MB +- **企业内网**: 内网用户访问不到 github.com,需要企业镜像 —— Phase 3 考虑 + +### 需持续关注 + +- **日活 > 1000 用户时**,考虑加 GitHub App token 提升 rate limit +- **强制更新的合理性**:只用于严重安全漏洞,不能用于"运营需要" +- **遥测**(谁在用哪个版本):本 ADR 不做,Phase 3 服务端 RFC(PLAN § ④)里考虑 + +## 实施路径 + +### Sprint 7(规划) + +- ✅ 写本 ADR +- ❌ 实际代码留给 Sprint 7.x + +### Sprint 8(macOS 实现) + +- 在 Swift 应用里实现 `UpdateChecker` 类 +- 启动时 + 每 24h 查 GitHub API +- 有新版弹通知 + 用户点更新 → 下载 + 替换 + +### Sprint 8-9(Tauri 实现) + +- Tauri 侧 Rust `update_checker` command +- 复用 Tauri 官方 updater 插件(底层仍调 GitHub) + +### Sprint 10(可选) + +- 压力测试:模拟 1000 并发检查,看 GitHub rate limit 是否触发 + +## 相关 + +- [ADR-0002](./0002-cross-platform-desktop-stack.md) · 跨平台栈决策(本 ADR 的前置) +- [RELEASE_PROCESS.md](../../RELEASE_PROCESS.md) · 发版流程 · 需加 "Release Body 必须含 severity frontmatter" diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000..d16239c --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,19 @@ +# Architecture Decision Records + +> 本目录由 `tools/cross-platform/scripts/generate-adr-index.js` 自动生成,请勿手动编辑索引表。 +> 新增/修改 ADR 后,重跑命令即可刷新。 + +## 列表 + +| # | 标题 | 状态 | 日期 | +|---|------|------|------| +| [0001](./0001-four-adoption-modes.md) | ADR-0001: 采用 4 种接入模式作为框架一等公民 | ✅ Accepted | 2026-04-17 | +| [0002](./0002-cross-platform-desktop-stack.md) | ADR-0002: 跨平台桌面应用技术栈 · 混合方案 (macOS 保留 Swift + Linux/Windows 用 Tauri) | 📋 Proposed | 2026-04-20 | +| [0003](./0003-auto-update-strategy.md) | ADR-0003: 桌面应用自动更新策略 · GitHub Release 为源 | 📋 Proposed | 2026-04-20 | + +## 怎么写新 ADR + +1. 复制 `templates/development/adr-template.md` 到当前目录 +2. 文件名按 `NNNN-简短描述.md`(NNNN 递增) +3. 填写内容 +4. 重跑 `node tools/cross-platform/scripts/generate-adr-index.js --target docs/adr` 刷新索引 diff --git a/docs/design/ui/swift-refactor-plan.md b/docs/design/ui/swift-refactor-plan.md new file mode 100644 index 0000000..2706cc1 --- /dev/null +++ b/docs/design/ui/swift-refactor-plan.md @@ -0,0 +1,290 @@ +# Swift 改造清单 · 对齐 UI 设计稿 · Sprint 7 实施计划 + +> Sprint 7 产出。针对 `app/ClaudeCodeHistory/` 22 个现有 Swift 文件, +> 列出**具体要改什么 / 改多少 / 优先级**,让 Sprint 7.x 多轮 PR 能按清单推进。 + +**前提**: +- 已决定 macOS 保留 Swift(见 [ADR-0002](../../adr/0002-cross-platform-desktop-stack.md)) +- 对齐的设计稿: [`docs/design/ui/prototype/`](./prototype/) 17 个 HTML 原型 +- 当前 Swift 代码仅是 Sprint 1 前的快速原型,功能未严格按设计稿 + +--- + +## 一、改造总览 + +| 类别 | 文件数 | 工作量 | +|------|-------|--------| +| ➕ 新增 Swift 文件 | 7 | ~4 周 | +| ✏️ 重构现有文件 | 10 | ~2 周 | +| 🔍 小幅调整 | 5 | ~3 天 | +| 🗑 可能废弃 | 0 | - | + +**总估算**: 约 6 周工作量,分 3-4 轮 PR。 + +--- + +## 二、按优先级分级(P0 → P2) + +### P0 · 必须 Beta 发布前完成 + +#### 1. ContentView.swift(三栏主视图 · 改造) + +- ✅ 现状: 有三栏雏形 +- 🎯 改造: + - 顶栏从单一"标题"扩展为: 🚀 brand + 项目切换器 + 场景导航 + 供应商选择 + ⚙ + 用户头像 + - 底部加持久化状态栏(引用 wireframe 10) +- 参考原型: [`01-main-three-pane.html`](./prototype/01-main-three-pane.html) +- 工作量: **中 · 2-3 天** + +#### 2. SidebarView.swift(侧边栏 · 改造) + +- ✅ 现状: 有会话列表 +- 🎯 改造: + - 分组头(▼ 今天 / 昨天 / 本周 / ...) + - 每项左侧加 6px 圆点 + active 状态蓝色左边框 + - 收藏 ⭐ 分组固定置顶 + - 右键菜单(wireframe 08) + - 底部 "+ 新会话 ⌘N" 按钮 +- 参考原型: [`01`](./prototype/01-main-three-pane.html) + [`08`](./prototype/08-conversation-context-menu.html) +- 工作量: **大 · 4-5 天** + +#### 3. ChatView.swift(聊天区 · 改造) + +- ✅ 现状: 基础消息显示 +- 🎯 改造: + - 消息头: 时间 + "思考 12s · 输出 2.3k tokens" 信息 + - 用户消息右对齐 + AI 消息左对齐 + - 附件 chip(`[📎 prd-v1.2.md 132 行]`) + - 底部输入栏工具条: [/] [📎] [⚡ opus] · 右侧 `⌘↵ 发送` + - 流式输出逐字显示(现有逻辑需核对) +- 参考: [`01`](./prototype/01-main-three-pane.html) § 聊天区 +- 工作量: **中 · 3 天** + +#### 4. SetupWizardView.swift(首次向导 · 大改) + +- ✅ 现状: 步骤 1-2 +- 🎯 改造: + - 加步骤 3(供应商配置)+ 步骤 4(完成) + - 4 步顶部进度条点击可跳转 + - 步骤 2 每个环境检查项旁加"▶ 一键安装"按钮 + - 步骤 3 测试连接功能 + 余额/延迟展示 +- 参考: [`02`](./prototype/02-setup-wizard.html) +- 工作量: **大 · 3-4 天** + +#### 5. SettingsView.swift(设置页 · 重构) + +- ✅ 现状: 单页混合 +- 🎯 改造为 **4 tab** 布局: + - 通用 / 供应商 / 快捷键 / 实验性 + - 每 tab 独立视图 · 左侧导航条 · 底部"取消 / 保存"按钮 + - "保存"按钮 dirty 状态感知(有修改才高亮) +- 参考: [`03`](./prototype/03-settings.html) +- 工作量: **大 · 4 天** + +#### 6. ProviderManager.swift(供应商管理 · 扩展) + +- ✅ 现状: 单供应商 +- 🎯 改造: + - 支持多供应商(Anthropic / AWS Bedrock / Google Vertex / 自定义代理) + - 每个供应商独立的连接测试 + 余额/延迟 + - 切换供应商浮层的数据源 + - 切换时的上下文处理(新会话 vs 当前会话) +- 参考: [`03 Provider tab`](./prototype/03-settings.html) + [`06`](./prototype/06-provider-switcher.html) +- 工作量: **大 · 5 天** + +#### 7. 🆕 SlashCommandPalette.swift(新增) + +- ⭐ 全新组件 +- 功能: + - 输入框首字符 `/` 触发浮层 + - 从 `tools/cli/commands/*.js` 扫描 frontmatter 建命令注册表 + - 模糊搜索 + 参数补齐 + - 执行后把结果作为系统消息插入聊天流 +- 参考: [`04`](./prototype/04-slash-command-palette.html) · [`flow-03`](./interaction-flows/03-flow-slash-command.md) +- 工作量: **极大 · 6-8 天** + +#### 8. 🆕 EnvStatusBar.swift(从 EHUBInfoBar.swift 重构) + +- ✅ 现状: EHUBInfoBar 简单提示 +- 🎯 重构为完整状态栏: + - 左半: 环境健康 · hover 弹详情 tooltip + - 右半: 网络/供应商状态 · hover 弹延迟/余额 + - 异常时整个状态栏变色(绿/黄/红) +- 参考: [`10`](./prototype/10-env-status-bar.html) +- 工作量: **中 · 3 天** + +--- + +### P1 · 重要但可稍晚 + +#### 9. 🆕 LoginView.swift + AuthManager.swift(新增) + +- 功能: 本地账号(Keychain) + 角色选择 + 预留 SSO 接口 +- 登录后跳项目列表(不是直接进主视图) +- 参考: [`11`](./prototype/11-login.html) +- 工作量: **中 · 3 天** + +#### 10. 🆕 ProjectListView.swift + ProjectStore.swift + ProjectCard.swift + +- 功能: + - 项目列表,每个带模式徽章(A/B/C/D) + - 筛选 + 排序 + - 项目的本地持久化(`~/Library/Application Support/EP Code AI/projects.json`) +- 参考: [`16`](./prototype/16-project-list.html) +- 工作量: **大 · 4 天** + +#### 11. 🆕 NewProjectWizardView.swift + +- 功能: 4 步向导(选模式 → 基本信息 → 起步清单 → 完成) +- 底层调用 `epcode init --mode=X --name=Y`(通过 CLI spawn) +- 参考: [`17`](./prototype/17-new-project.html) +- 工作量: **大 · 4 天** + +#### 12. 🆕 WorkflowView.swift + WorkflowStepModel.swift(场景工作流) + +- 功能: 12-15 四个场景 HTML 的 Swift 实现(一个视图数据驱动,不是 4 个独立视图) +- 时序 + 当前步骤 + Prompt 卡片 +- 参考: [`12-15`](./prototype/12-workflow-business.html) +- 工作量: **极大 · 8 天** + +#### 13. 🆕 PromptRegistry.swift + PromptRunner.swift + +- 功能: + - 扫描 `skills/**/*.md` 的 frontmatter 建 Prompt 注册表 + - Prompt 卡片的参数表单 + 组合 prompt + 发送 +- 参考: [`wireframe 12-15`](./wireframes/12-15-workflow-scenes.md) 的 "Prompt 元数据" +- 工作量: **大 · 5 天** + +--- + +### P2 · Beta 后补 + +#### 14. SearchOverlay.swift(⌘K 全局搜索) + +- 功能: 跨会话 / Artifact / 命令 / 消息 搜索 +- 依赖: ConversationStore 加 FTS5 索引 +- 参考: [`07`](./prototype/07-search-overlay.html) +- 工作量: **大 · 5 天** + +#### 15. ArtifactPanel.swift 增强 + +- 现状: 基础列表 +- 改造: + - 右栏自动折叠(无 Artifact 时) + - 点击 Artifact 进全屏视图(wireframe 05) + - 版本对比(v2 ↔ v3)- **P2 可延后** +- 参考: [`05`](./prototype/05-artifact-panel-expanded.html) +- 工作量: **中 · 3 天(不含版本对比)** + +#### 16. ArtifactDetector.swift 增强 + +- 现状: 检测代码块 +- 改造: + - 加 Markdown 文档识别(长度 + 结构阈值) + - 流式增量更新 Artifact(不等流结束) +- 参考: [`flow-04`](./interaction-flows/04-flow-artifact-detect.md) +- 工作量: **小 · 2 天** + +#### 17. ConversationStore.swift 扩展 + +- 加: + - 项目归属(每个会话所属 project_id) + - 分组(工作 / 学习等自定义) + - 批量导出(.md / .json / .zip) + - 软删除(回收站 30 天) + - FTS5 搜索索引 +- 工作量: **中 · 3 天** + +#### 18. 自动更新 UpdateChecker.swift + +- 参考: [ADR-0003](../../adr/0003-auto-update-strategy.md) +- 功能: 启动时 + 每 24h 检查 GitHub Releases API +- 工作量: **中 · 3 天** + +#### 19. FavoritesManager.swift 小调整 + +- 对接新的分组系统 +- 工作量: **小 · 1 天** + +--- + +## 三、小幅调整(5 个文件) + +| 文件 | 调整 | +|------|------| +| `ClaudeCodeHistoryApp.swift` | 启动流程改:未登录 → 登录 → 项目列表;已登录 → 项目列表 | +| `AppSettings.swift` | 扩展 schema:加快捷键自定义字段 + 实验性 feature flags | +| `FileWatcher.swift` | 可能不用改,确认仍在用 | +| `ClaudeProcessManager.swift` | 支持多 Claude 实例(不同供应商) | +| `ConversationNameManager.swift` | 可能不用改 | + +--- + +## 四、Sprint 7.x 推进建议(分 4 轮 PR) + +| 轮次 | 目标 | 涉及文件 | 产物 | +|------|------|---------|------| +| **S7.1** | 基础三栏 + 向导 + 设置 | P0 #1-5 | 可启动的 Beta 骨架 | +| **S7.2** | 命令面板 + 状态栏 + 供应商 | P0 #6-8 | 功能完整的 Beta | +| **S7.3** | 登录 + 项目列表 + 新建向导 | P1 #9-11 | 完整用户旅程可走 | +| **S7.4** | 场景工作流 + Prompt 注册表 | P1 #12-13 | Prompt as Feature 上线 | + +Sprint 7 结束时: **S7.1 + S7.2 Beta 可下载**(无签名)。 +Sprint 8: 完成 S7.3 + S7.4 · 同时启动 Tauri 侧 Linux/Win。 +Sprint 9+: P2 逐步补齐 + 搜索 + 自动更新。 + +--- + +## 五、跨文件依赖注意 + +```mermaid +graph TD + ContentView --> SidebarView + ContentView --> ChatView + ContentView --> ArtifactPanel + ContentView --> EnvStatusBar + + ChatView --> SlashCommandPalette + ChatView --> ProviderManager + + SlashCommandPalette --> PromptRegistry + WorkflowView --> PromptRegistry + PromptRegistry --> PromptRunner + PromptRunner --> ChatView + + ProjectListView --> ProjectStore + NewProjectWizardView --> ProjectStore + ContentView --> ProjectStore + + LoginView --> AuthManager + ContentView --> AuthManager + + ConversationStore --> ProjectStore + SearchOverlay --> ConversationStore +``` + +**启动依赖**: `ClaudeCodeHistoryApp` → `AuthManager` → `ProjectStore` → `ContentView` + +--- + +## 六、测试策略 + +每个 PR 必须包含: +- **单元测试**(Xcode XCTest): 关键 manager / store 类 +- **UI 测试**(XCUITest): 每个关键用户旅程 1 条(登录 → 项目 → 主视图) +- **集成测试**: CLI spawn 调用的场景(PromptRunner 最复杂) + +目标覆盖率: 60% (Phase 1 方法论要求 ≥ 70%,桌面应用 Phase 2 放宽) + +--- + +## 七、Sprint 7 本轮只做的事(不实际改代码) + +本 Sprint 产出: +- ✅ 本文档(改造清单) +- ✅ ADR-0002 跨平台决策 +- ✅ ADR-0003 自动更新策略 +- ✅ macOS 打包 workflow(无签名版) +- ❌ **不实际修改 Swift 代码** + +改代码留给 Sprint 7.1-7.4 多轮推进,需要 Xcode 环境 + 手动测试,适合每天持续推进。