{"owner":"zhanglun","repo":"lettura","hasSkills":true,"totalSkillsCount":2,"totalTokensCount":3745,"categories":["root-instruction","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\n面向 AI 编码代理的仓库速查笔记。内容与可执行配置冲突时，以配置为准。\n\n## 项目形态\n\n- Lettura 是 Tauri v2 桌面订阅阅读器：React/Vite 前端 + Rust 后端（Cargo\n  workspace 成员 `apps/desktop/src-tauri`）。\n- 两套 workspace 共享仓库树：pnpm 管理 JS 包（`apps/desktop`、`apps/docs`、\n  未来的 `packages/*`），根 Cargo.toml 管理全部 Rust（成员\n  `apps/desktop/src-tauri` 与 `packages/*`）。pnpm 会静默忽略 Rust 目录；\n  Cargo.lock 只保留根目录一份。\n- 订阅抓取器是 `packages/fetcher-*` 下的独立 crate（`fetcher-core` =\n  `Fetcher` trait + `FetchedArticle`；`fetcher-rss` = 解析/发现层；\n  `fetcher-mail` = IMAP（async-imap 0.10 + mail-parser 0.11，两者均已锁\n  feature）；`fetcher-bilibili` = wbi 签名 Web API）。抓取器必须无状态，\n  禁止依赖 diesel/tauri/actix；注册表、探测分发（`claims` 顺序、rss 兜底）\n  与 `FetchedArticle → NewArticle` 转换器在 `src-tauri/src/fetchers/mod.rs`；\n  按 `feeds.provider` 的同步分发在 `feed/channel.rs::sync_articles`。\n  新增源类型 = 新 crate + 注册表加一行。每源配置存在\n  `feeds.provider/account_uuid/source_config`；凭据在 `source_accounts`\n  表（`sources/account_service.rs`，命令\n  `list/save/delete/test_source_account`）。邮件同步会把 `last_uid`\n  水位写回 `source_config`。\n- 前端数据访问只走 localhost HTTP（`src/helpers/http.ts` 的\n  `apiGet/apiPost`，对准内嵌 Actix 服务 `http://127.0.0.1:{port}/api`，\n  实现在 `src-tauri/src/server/handlers/`）。dataAgent 已移除（2026-09-30，\n  提交 5a8278b4 回滚了此前的 IPC-only 收拢）。Tauri `invoke` 仅保留非数据\n  命令（窗口/端口、OPML、来源账户、订阅添加/预览）。新数据操作 =\n  新增 Actix handler。\n- 前端数据流是两条车道（保持现状）：文章列表查询走 `src/hooks/useArticle.ts`\n  （模块级缓存 + 在途去重，列表状态的唯一真相源）；命令类 POST 可直接\n  `apiPost`，然后经 `store.getSubscribes()` 和/或\n  `busChannel.emit(\"getChannels\")` 刷新（AppLayout 监听一次）。改变文章\n  全集的命令（退订/删除源等）成功后必须调\n  `invalidateArticleCache()`（useArticle.ts 导出，含在途世代守卫），否则\n  返回列表页会命中陈旧缓存。不要引入第三层缓存，不要把列表状态塞回\n  Zustand slice。\n- React Compiler 1.0 已接入 vite.config.ts 与 vitest.config.ts\n  （babel-plugin-react-compiler）：src 下所有组件/钩子都被编译（零\n  bail-out）并自动记忆化。禁止再写 useCallback/useMemo/React.memo——\n  记忆化由编译器负责，手写hook 曾经直接阻断编译（\"Existing memoization\n  could not be preserved\"）。塑造了现有代码形态的编译器约束（务必继续\n  遵守）：渲染期禁止写 ref（在 useEffect 里同步）；组件/钩子内禁止\n  try/finally 语句（用 promise 的 .catch()/.finally()）；组件体内禁止\n  import() 表达式（提升到模块级）；渲染期禁止读可变模块级状态（缓存、\n  单例）——编译器会把这类读当作纯计算缓存住陈旧值。跨渲染数据一律走\n  state 传递，缓存在 effect 里同步（键切换不允许画出旧帧时用\n  useLayoutEffect）。\n- 前端入口：`src/index.tsx` 定义路由并在 Tauri 内等待 `get_server_port`；\n  `src/App.tsx` 是应用外壳与 Tauri 事件监听；路由名在 `src/config.ts`。\n- Rust 入口：`src-tauri/src/main.rs` 调用 `lettura_lib::run()`（定义在\n  `src-tauri/src/lib.rs`）：加载配置、打开 SQLite、跑内嵌 Diesel 迁移、\n  启动 Actix 服务、注册 Tauri 命令、托盘/菜单处理器与调度器。\n- 状态是单一 Zustand store（`useAppStore`，`src/stores/index.ts`），由\n  feed、article、user config、podcast 四个 slice 组合而成。\n\n## 常用命令\n\n- 安装：`pnpm install`（`.npmrc` 设了 `auto-install-peers=true`）。\n- 仅前端开发：`pnpm dev`（Vite 固定端口 9527）。\n- 桌面完整开发：`pnpm tauri dev`（经 `src-tauri/tauri.conf.json` 运行\n  `pnpm dev`）。\n- 前端构建/类型检查：`pnpm build`（`tsc && vite build`）；Vite 输出到\n  `build/`，不是 `dist/`。\n- 桌面构建：`pnpm tauri build`。\n- 前端测试：`pnpm test`；单文件：`pnpm test path/to/file.test.ts`。\n- Rust 测试：在仓库根跑 `cargo test`（workspace 成员是 `src-tauri` 与\n  `packages/*`）；`pnpm cargo:check|cargo:test|cargo:fmt` 是便捷封装。\n- Lint/格式化用 Biome（`apps/desktop/biome.json`），不是 ESLint/Prettier：\n  `npx biome check src/` 与 `npx biome format src/`（在 `apps/desktop/`\n  下执行）。\n\n## 运行时与存储要点\n\n- Vite 固定 `server.strictPort = true`，端口 9527。Actix API 服务先用配置\n  端口，被占用则回落 8000-9000。\n- Tauri 之外，`src/index.tsx` 把 `localStorage.port` 默认为 `3456`；Tauri\n  内渲染前调用 `invoke(\"get_server_port\")`。\n- 生产 SQLite 库是 `~/.lettura/lettura.db`。设置 `LETTURA_ENV` 后，Rust 经\n  `dotenv` 从环境变量读 `DATABASE_URL`。\n- 用户配置是 TOML（正常运行为 `~/.lettura/lettura.toml`，dev 模式为本地\n  文件），不在 SQLite schema 内。\n- 播客数据是独立的浏览器端 Dexie/IndexedDB（`src/helpers/podcastDB.ts`），\n  读写统一收口在 `stores/createPodcastSlice.ts` 的 action 与渲染端\n  useLiveQuery。\n- 关闭主窗口是隐藏到系统托盘，不是退出（release；dev 直接退出）。窗口控制\n  跨平台：macOS 走 `tauri.macos.conf.json`（平台配置合并，windows 数组\n  整体替换，须带全量字段）在创建期即 `decorations: true` + Overlay——\n  decorations:false 的 borderless 窗口没有标准按钮，且运行时补\n  set_decorations 会与 Overlay 异步竞态（实测内容不延伸）；红绿灯由 lib.rs\n  `layout_traffic_lights` 摆位（wry 的 trafficLightPosition 纵向无效），\n  顶栏经 `body.platform-mac` 让位左侧 100px。Windows/Linux 用自绘\n  `components/layout/WindowControls`（顶栏右端三钮）。拖拽区仍由\n  `data-tauri-drag-region` 承担（双击 = 最大化切换）。\n\n## 测试要点\n\n- Vitest 使用 `vitest.config.ts`：开启 globals，jsdom 环境，setup 文件为\n  `src/__tests__/setup.ts`（测试管线同样挂了 React Compiler 编译器）。\n- setup 文件 mock 了 `localStorage`、`@tauri-apps/api/core` 的 `invoke`、\n  `@tauri-apps/api/event`、`@tauri-apps/api/webviewWindow`、\n  `@tauri-apps/plugin-shell`、`@tauri-apps/plugin-fs`、\n  `@tauri-apps/plugin-dialog` 和全局 `fetch`；测试在没有真实 Tauri 后端时\n  也能通过，判断测试结果时要记住这一点。\n- 前端测试主要在 `src/stores/__tests__/`、`src/helpers/__tests__/`、\n  `src/hooks/__tests__/`；Rust 测试如 `src-tauri/src/cmd.rs`、\n  `src-tauri/src/core/scheduler.rs`。\n\n## 样式与 i18n\n\n- Tailwind v3 + Radix UI 主题令牌配置在 `tailwind.config.js`；暗色模式基于\n  class/data-attribute，应用切换 `body.dark-theme`。\n- shadcn 风格组件在 `src/components/ui/`；组合类名优先用\n  `src/helpers/cn.tsx` 的 `cn`。\n- 0.2.0 的 UI 由 `src/styles/fusion.css`（全局 `fusion-*` 类）承载，经\n  `src/index.css` → `styles/index.css` 分层引入；Astryx 主题来自 npm 包\n  `@astryxdesign/theme-*`。无 PWA/service worker（vite-plugin-pwa 已移除，\n  忽略提及它的旧笔记）。\n- i18n 在 `src/i18n.ts` 初始化；语言包为 `src/locales/en.json` 与\n  `src/locales/zh.json`。\n\n## Rust 后端要点\n\n- Diesel schema 输出在 `src-tauri/src/schema.rs`；迁移在\n  `src-tauri/migrations/`，由 `embed_migrations!` 内嵌。\n- 调度器（`core/scheduler.rs`）按到期驱动：每 60s tick 一次，同步\n  `last_sync_date + (sync_interval 或 update_interval)` 已到期的源，然后每\n  个源 emit `sync://completed` `{uuid, title, inserted, error}`（在\n  `src/App.tsx` 监听）。源行的 `last_sync_date` 以本地时间存\n  \"YYYY-MM-DD HH:MM:SS\" 字符串。\n- Rust 模块按职责拆分：`core/`（配置/菜单/调度/托盘）、`feed/`（文章/\n  频道/文件夹/OPML）、`server/`（Actix 路由）。托盘与菜单在 `setup()` 里\n  用 `TrayIconBuilder`/`MenuBuilder` 构建。\n- Tauri v2 是插件架构：shell、fs、dialog、http、process、updater、log、\n  single-instance 都是独立插件（Rust crate 与 npm 包成对）。权限声明在\n  `src-tauri/capabilities/default.json`（取代 v1 的 allowlist）。\n- `LETTURA_ENV` 开启调试日志并改变配置/数据库行为；不要假设 dev 与生产\n  路径一致。\n\n## CI 与发布\n\n- `.github/workflows/release.yml` 在 push 到 `release` 时运行：创建 draft\n  release，再用 Tauri 构建 macOS（arm/x64/universal）、Ubuntu、Windows 产物。\n  自动更新链路：conf 开了 `bundle.createUpdaterArtifacts`（安装包 + 签名的\n  updater 产物），多平台 latest.json 由 tauri-action 合并上传——publish 前\n  人工核对 draft 里 latest.json 应含全部平台条目；客户端入口在设置页\n  「行为与数据 → 检查更新」（`UpdaterRow`，手动检查，无启动静默检查）。\n- `.github/workflows/deploy-doc.yml` 在 `master` 上运行，把 `docs/` 的\n  Astro 文档站部署到 GitHub Pages。\n- `package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml` 三处\n  版本号保持同步（bump 后本地 `pnpm cargo:check` 刷 Cargo.lock）。\n- **版本号规则（2026-10-11 用户定）**：除非用户明确指定版本号，发布一律在\n  0.2.x 线内 patch 递增（0.2.1 → 0.2.2 → …）；升 minor（如 0.3.0）必须\n  用户点名。\n- 发布顺序：三处版本号 bump 提交 → 推 `0.2.0` 分支与 `master`（ff）→\n  `git push origin 0.2.0:release` 触发构建 → 人工核对 draft 的 latest.json\n  全平台条目 → publish。\n","CLAUDE.md":"# Claude Code 项目配置\n\n## 提交规范\n\n- 使用中文 Conventional Commit 格式：`type(scope): 描述`\n- **不要**在提交信息中添加 `Co-Authored-By` 行\n- 每个主题独立提交，不要合并不相关的变更\n\n## 注意事项\n\n- 不要提交 `.sisyphus/run-continuation/*.json`\n- 生产 SQLite 在 `~/.lettura/lettura.db`，不要执行清库或迁移重建\n- 不要恢复 DnD 订阅树\n- 用中文沟通\n"},"files":{"AGENTS.md":"# AGENTS.md\n\n面向 AI 编码代理的仓库速查笔记。内容与可执行配置冲突时，以配置为准。\n\n## 项目形态\n\n- Lettura 是 Tauri v2 桌面订阅阅读器：React/Vite 前端 + Rust 后端（Cargo\n  workspace 成员 `apps/desktop/src-tauri`）。\n- 两套 workspace 共享仓库树：pnpm 管理 JS 包（`apps/desktop`、`apps/docs`、\n  未来的 `packages/*`），根 Cargo.toml 管理全部 Rust（成员\n  `apps/desktop/src-tauri` 与 `packages/*`）。pnpm 会静默忽略 Rust 目录；\n  Cargo.lock 只保留根目录一份。\n- 订阅抓取器是 `packages/fetcher-*` 下的独立 crate（`fetcher-core` =\n  `Fetcher` trait + `FetchedArticle`；`fetcher-rss` = 解析/发现层；\n  `fetcher-mail` = IMAP（async-imap 0.10 + mail-parser 0.11，两者均已锁\n  feature）；`fetcher-bilibili` = wbi 签名 Web API）。抓取器必须无状态，\n  禁止依赖 diesel/tauri/actix；注册表、探测分发（`claims` 顺序、rss 兜底）\n  与 `FetchedArticle → NewArticle` 转换器在 `src-tauri/src/fetchers/mod.rs`；\n  按 `feeds.provider` 的同步分发在 `feed/channel.rs::sync_articles`。\n  新增源类型 = 新 crate + 注册表加一行。每源配置存在\n  `feeds.provider/account_uuid/source_config`；凭据在 `source_accounts`\n  表（`sources/account_service.rs`，命令\n  `list/save/delete/test_source_account`）。邮件同步会把 `last_uid`\n  水位写回 `source_config`。\n- 前端数据访问只走 localhost HTTP（`src/helpers/http.ts` 的\n  `apiGet/apiPost`，对准内嵌 Actix 服务 `http://127.0.0.1:{port}/api`，\n  实现在 `src-tauri/src/server/handlers/`）。dataAgent 已移除（2026-09-30，\n  提交 5a8278b4 回滚了此前的 IPC-only 收拢）。Tauri `invoke` 仅保留非数据\n  命令（窗口/端口、OPML、来源账户、订阅添加/预览）。新数据操作 =\n  新增 Actix handler。\n- 前端数据流是两条车道（保持现状）：文章列表查询走 `src/hooks/useArticle.ts`\n  （模块级缓存 + 在途去重，列表状态的唯一真相源）；命令类 POST 可直接\n  `apiPost`，然后经 `store.getSubscribes()` 和/或\n  `busChannel.emit(\"getChannels\")` 刷新（AppLayout 监听一次）。改变文章\n  全集的命令（退订/删除源等）成功后必须调\n  `invalidateArticleCache()`（useArticle.ts 导出，含在途世代守卫），否则\n  返回列表页会命中陈旧缓存。不要引入第三层缓存，不要把列表状态塞回\n  Zustand slice。\n- React Compiler 1.0 已接入 vite.config.ts 与 vitest.config.ts\n  （babel-plugin-react-compiler）：src 下所有组件/钩子都被编译（零\n  bail-out）并自动记忆化。禁止再写 useCallback/useMemo/React.memo——\n  记忆化由编译器负责，手写hook 曾经直接阻断编译（\"Existing memoization\n  could not be preserved\"）。塑造了现有代码形态的编译器约束（务必继续\n  遵守）：渲染期禁止写 ref（在 useEffect 里同步）；组件/钩子内禁止\n  try/finally 语句（用 promise 的 .catch()/.finally()）；组件体内禁止\n  import() 表达式（提升到模块级）；渲染期禁止读可变模块级状态（缓存、\n  单例）——编译器会把这类读当作纯计算缓存住陈旧值。跨渲染数据一律走\n  state 传递，缓存在 effect 里同步（键切换不允许画出旧帧时用\n  useLayoutEffect）。\n- 前端入口：`src/index.tsx` 定义路由并在 Tauri 内等待 `get_server_port`；\n  `src/App.tsx` 是应用外壳与 Tauri 事件监听；路由名在 `src/config.ts`。\n- Rust 入口：`src-tauri/src/main.rs` 调用 `lettura_lib::run()`（定义在\n  `src-tauri/src/lib.rs`）：加载配置、打开 SQLite、跑内嵌 Diesel 迁移、\n  启动 Actix 服务、注册 Tauri 命令、托盘/菜单处理器与调度器。\n- 状态是单一 Zustand store（`useAppStore`，`src/stores/index.ts`），由\n  feed、article、user config、podcast 四个 slice 组合而成。\n\n## 常用命令\n\n- 安装：`pnpm install`（`.npmrc` 设了 `auto-install-peers=true`）。\n- 仅前端开发：`pnpm dev`（Vite 固定端口 9527）。\n- 桌面完整开发：`pnpm tauri dev`（经 `src-tauri/tauri.conf.json` 运行\n  `pnpm dev`）。\n- 前端构建/类型检查：`pnpm build`（`tsc && vite build`）；Vite 输出到\n  `build/`，不是 `dist/`。\n- 桌面构建：`pnpm tauri build`。\n- 前端测试：`pnpm test`；单文件：`pnpm test path/to/file.test.ts`。\n- Rust 测试：在仓库根跑 `cargo test`（workspace 成员是 `src-tauri` 与\n  `packages/*`）；`pnpm cargo:check|cargo:test|cargo:fmt` 是便捷封装。\n- Lint/格式化用 Biome（`apps/desktop/biome.json`），不是 ESLint/Prettier：\n  `npx biome check src/` 与 `npx biome format src/`（在 `apps/desktop/`\n  下执行）。\n\n## 运行时与存储要点\n\n- Vite 固定 `server.strictPort = true`，端口 9527。Actix API 服务先用配置\n  端口，被占用则回落 8000-9000。\n- Tauri 之外，`src/index.tsx` 把 `localStorage.port` 默认为 `3456`；Tauri\n  内渲染前调用 `invoke(\"get_server_port\")`。\n- 生产 SQLite 库是 `~/.lettura/lettura.db`。设置 `LETTURA_ENV` 后，Rust 经\n  `dotenv` 从环境变量读 `DATABASE_URL`。\n- 用户配置是 TOML（正常运行为 `~/.lettura/lettura.toml`，dev 模式为本地\n  文件），不在 SQLite schema 内。\n- 播客数据是独立的浏览器端 Dexie/IndexedDB（`src/helpers/podcastDB.ts`），\n  读写统一收口在 `stores/createPodcastSlice.ts` 的 action 与渲染端\n  useLiveQuery。\n- 关闭主窗口是隐藏到系统托盘，不是退出（release；dev 直接退出）。窗口控制\n  跨平台：macOS 走 `tauri.macos.conf.json`（平台配置合并，windows 数组\n  整体替换，须带全量字段）在创建期即 `decorations: true` + Overlay——\n  decorations:false 的 borderless 窗口没有标准按钮，且运行时补\n  set_decorations 会与 Overlay 异步竞态（实测内容不延伸）；红绿灯由 lib.rs\n  `layout_traffic_lights` 摆位（wry 的 trafficLightPosition 纵向无效），\n  顶栏经 `body.platform-mac` 让位左侧 100px。Windows/Linux 用自绘\n  `components/layout/WindowControls`（顶栏右端三钮）。拖拽区仍由\n  `data-tauri-drag-region` 承担（双击 = 最大化切换）。\n\n## 测试要点\n\n- Vitest 使用 `vitest.config.ts`：开启 globals，jsdom 环境，setup 文件为\n  `src/__tests__/setup.ts`（测试管线同样挂了 React Compiler 编译器）。\n- setup 文件 mock 了 `localStorage`、`@tauri-apps/api/core` 的 `invoke`、\n  `@tauri-apps/api/event`、`@tauri-apps/api/webviewWindow`、\n  `@tauri-apps/plugin-shell`、`@tauri-apps/plugin-fs`、\n  `@tauri-apps/plugin-dialog` 和全局 `fetch`；测试在没有真实 Tauri 后端时\n  也能通过，判断测试结果时要记住这一点。\n- 前端测试主要在 `src/stores/__tests__/`、`src/helpers/__tests__/`、\n  `src/hooks/__tests__/`；Rust 测试如 `src-tauri/src/cmd.rs`、\n  `src-tauri/src/core/scheduler.rs`。\n\n## 样式与 i18n\n\n- Tailwind v3 + Radix UI 主题令牌配置在 `tailwind.config.js`；暗色模式基于\n  class/data-attribute，应用切换 `body.dark-theme`。\n- shadcn 风格组件在 `src/components/ui/`；组合类名优先用\n  `src/helpers/cn.tsx` 的 `cn`。\n- 0.2.0 的 UI 由 `src/styles/fusion.css`（全局 `fusion-*` 类）承载，经\n  `src/index.css` → `styles/index.css` 分层引入；Astryx 主题来自 npm 包\n  `@astryxdesign/theme-*`。无 PWA/service worker（vite-plugin-pwa 已移除，\n  忽略提及它的旧笔记）。\n- i18n 在 `src/i18n.ts` 初始化；语言包为 `src/locales/en.json` 与\n  `src/locales/zh.json`。\n\n## Rust 后端要点\n\n- Diesel schema 输出在 `src-tauri/src/schema.rs`；迁移在\n  `src-tauri/migrations/`，由 `embed_migrations!` 内嵌。\n- 调度器（`core/scheduler.rs`）按到期驱动：每 60s tick 一次，同步\n  `last_sync_date + (sync_interval 或 update_interval)` 已到期的源，然后每\n  个源 emit `sync://completed` `{uuid, title, inserted, error}`（在\n  `src/App.tsx` 监听）。源行的 `last_sync_date` 以本地时间存\n  \"YYYY-MM-DD HH:MM:SS\" 字符串。\n- Rust 模块按职责拆分：`core/`（配置/菜单/调度/托盘）、`feed/`（文章/\n  频道/文件夹/OPML）、`server/`（Actix 路由）。托盘与菜单在 `setup()` 里\n  用 `TrayIconBuilder`/`MenuBuilder` 构建。\n- Tauri v2 是插件架构：shell、fs、dialog、http、process、updater、log、\n  single-instance 都是独立插件（Rust crate 与 npm 包成对）。权限声明在\n  `src-tauri/capabilities/default.json`（取代 v1 的 allowlist）。\n- `LETTURA_ENV` 开启调试日志并改变配置/数据库行为；不要假设 dev 与生产\n  路径一致。\n\n## CI 与发布\n\n- `.github/workflows/release.yml` 在 push 到 `release` 时运行：创建 draft\n  release，再用 Tauri 构建 macOS（arm/x64/universal）、Ubuntu、Windows 产物。\n  自动更新链路：conf 开了 `bundle.createUpdaterArtifacts`（安装包 + 签名的\n  updater 产物），多平台 latest.json 由 tauri-action 合并上传——publish 前\n  人工核对 draft 里 latest.json 应含全部平台条目；客户端入口在设置页\n  「行为与数据 → 检查更新」（`UpdaterRow`，手动检查，无启动静默检查）。\n- `.github/workflows/deploy-doc.yml` 在 `master` 上运行，把 `docs/` 的\n  Astro 文档站部署到 GitHub Pages。\n- `package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml` 三处\n  版本号保持同步（bump 后本地 `pnpm cargo:check` 刷 Cargo.lock）。\n- **版本号规则（2026-10-11 用户定）**：除非用户明确指定版本号，发布一律在\n  0.2.x 线内 patch 递增（0.2.1 → 0.2.2 → …）；升 minor（如 0.3.0）必须\n  用户点名。\n- 发布顺序：三处版本号 bump 提交 → 推 `0.2.0` 分支与 `master`（ff）→\n  `git push origin 0.2.0:release` 触发构建 → 人工核对 draft 的 latest.json\n  全平台条目 → publish。\n","CLAUDE.md":"# Claude Code 项目配置\n\n## 提交规范\n\n- 使用中文 Conventional Commit 格式：`type(scope): 描述`\n- **不要**在提交信息中添加 `Co-Authored-By` 行\n- 每个主题独立提交，不要合并不相关的变更\n\n## 注意事项\n\n- 不要提交 `.sisyphus/run-continuation/*.json`\n- 生产 SQLite 在 `~/.lettura/lettura.db`，不要执行清库或迁移重建\n- 不要恢复 DnD 订阅树\n- 用中文沟通\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/zhanglun/lettura/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\n面向 AI 编码代理的仓库速查笔记。内容与可执行配置冲突时，以配置为准。\n\n## 项目形态\n\n- Lettura 是 Tauri v2 桌面订阅阅读器：React/Vite 前端 + Rust 后端（Cargo\n  workspace 成员 `apps/desktop/src-tauri`）。\n- 两套 workspace 共享仓库树：pnpm 管理 JS 包（`apps/desktop`、`apps/docs`、\n  未来的 `packages/*`），根 Cargo.toml 管理全部 Rust（成员\n  `apps/desktop/src-tauri` 与 `packages/*`）。pnpm 会静默忽略 Rust 目录；\n  Cargo.lock 只保留根目录一份。\n- 订阅抓取器是 `packages/fetcher-*` 下的独立 crate（`fetcher-core` =\n  `Fetcher` trait + `FetchedArticle`；`fetcher-rss` = 解析/发现层；\n  `fetcher-mail` = IMAP（async-imap 0.10 + mail-parser 0.11，两者均已锁\n  feature）；`fetcher-bilibili` = wbi 签名 Web API）。抓取器必须无状态，\n  禁止依赖 diesel/tauri/actix；注册表、探测分发（`claims` 顺序、rss 兜底）\n  与 `FetchedArticle → NewArticle` 转换器在 `src-tauri/src/fetchers/mod.rs`；\n  按 `feeds.provider` 的同步分发在 `feed/channel.rs::sync_articles`。\n  新增源类型 = 新 crate + 注册表加一行。每源配置存在\n  `feeds.provider/account_uuid/source_config`；凭据在 `source_accounts`\n  表（`sources/account_service.rs`，命令\n  `list/save/delete/test_source_account`）。邮件同步会把 `last_uid`\n  水位写回 `source_config`。\n- 前端数据访问只走 localhost HTTP（`src/helpers/http.ts` 的\n  `apiGet/apiPost`，对准内嵌 Actix 服务 `http://127.0.0.1:{port}/api`，\n  实现在 `src-tauri/src/server/handlers/`）。dataAgent 已移除（2026-09-30，\n  提交 5a8278b4 回滚了此前的 IPC-only 收拢）。Tauri `invoke` 仅保留非数据\n  命令（窗口/端口、OPML、来源账户、订阅添加/预览）。新数据操作 =\n  新增 Actix handler。\n- 前端数据流是两条车道（保持现状）：文章列表查询走 `src/hooks/useArticle.ts`\n  （模块级缓存 + 在途去重，列表状态的唯一真相源）；命令类 POST 可直接\n  `apiPost`，然后经 `store.getSubscribes()` 和/或\n  `busChannel.emit(\"getChannels\")` 刷新（AppLayout 监听一次）。改变文章\n  全集的命令（退订/删除源等）成功后必须调\n  `invalidateArticleCache()`（useArticle.ts 导出，含在途世代守卫），否则\n  返回列表页会命中陈旧缓存。不要引入第三层缓存，不要把列表状态塞回\n  Zustand slice。\n- React Compiler 1.0 已接入 vite.config.ts 与 vitest.config.ts\n  （babel-plugin-react-compiler）：src 下所有组件/钩子都被编译（零\n  bail-out）并自动记忆化。禁止再写 useCallback/useMemo/React.memo——\n  记忆化由编译器负责，手写hook 曾经直接阻断编译（\"Existing memoization\n  could not be preserved\"）。塑造了现有代码形态的编译器约束（务必继续\n  遵守）：渲染期禁止写 ref（在 useEffect 里同步）；组件/钩子内禁止\n  try/finally 语句（用 promise 的 .catch()/.finally()）；组件体内禁止\n  import() 表达式（提升到模块级）；渲染期禁止读可变模块级状态（缓存、\n  单例）——编译器会把这类读当作纯计算缓存住陈旧值。跨渲染数据一律走\n  state 传递，缓存在 effect 里同步（键切换不允许画出旧帧时用\n  useLayoutEffect）。\n- 前端入口：`src/index.tsx` 定义路由并在 Tauri 内等待 `get_server_port`；\n  `src/App.tsx` 是应用外壳与 Tauri 事件监听；路由名在 `src/config.ts`。\n- Rust 入口：`src-tauri/src/main.rs` 调用 `lettura_lib::run()`（定义在\n  `src-tauri/src/lib.rs`）：加载配置、打开 SQLite、跑内嵌 Diesel 迁移、\n  启动 Actix 服务、注册 Tauri 命令、托盘/菜单处理器与调度器。\n- 状态是单一 Zustand store（`useAppStore`，`src/stores/index.ts`），由\n  feed、article、user config、podcast 四个 slice 组合而成。\n\n## 常用命令\n\n- 安装：`pnpm install`（`.npmrc` 设了 `auto-install-peers=true`）。\n- 仅前端开发：`pnpm dev`（Vite 固定端口 9527）。\n- 桌面完整开发：`pnpm tauri dev`（经 `src-tauri/tauri.conf.json` 运行\n  `pnpm dev`）。\n- 前端构建/类型检查：`pnpm build`（`tsc && vite build`）；Vite 输出到\n  `build/`，不是 `dist/`。\n- 桌面构建：`pnpm tauri build`。\n- 前端测试：`pnpm test`；单文件：`pnpm test path/to/file.test.ts`。\n- Rust 测试：在仓库根跑 `cargo test`（workspace 成员是 `src-tauri` 与\n  `packages/*`）；`pnpm cargo:check|cargo:test|cargo:fmt` 是便捷封装。\n- Lint/格式化用 Biome（`apps/desktop/biome.json`），不是 ESLint/Prettier：\n  `npx biome check src/` 与 `npx biome format src/`（在 `apps/desktop/`\n  下执行）。\n\n## 运行时与存储要点\n\n- Vite 固定 `server.strictPort = true`，端口 9527。Actix API 服务先用配置\n  端口，被占用则回落 8000-9000。\n- Tauri 之外，`src/index.tsx` 把 `localStorage.port` 默认为 `3456`；Tauri\n  内渲染前调用 `invoke(\"get_server_port\")`。\n- 生产 SQLite 库是 `~/.lettura/lettura.db`。设置 `LETTURA_ENV` 后，Rust 经\n  `dotenv` 从环境变量读 `DATABASE_URL`。\n- 用户配置是 TOML（正常运行为 `~/.lettura/lettura.toml`，dev 模式为本地\n  文件），不在 SQLite schema 内。\n- 播客数据是独立的浏览器端 Dexie/IndexedDB（`src/helpers/podcastDB.ts`），\n  读写统一收口在 `stores/createPodcastSlice.ts` 的 action 与渲染端\n  useLiveQuery。\n- 关闭主窗口是隐藏到系统托盘，不是退出（release；dev 直接退出）。窗口控制\n  跨平台：macOS 走 `tauri.macos.conf.json`（平台配置合并，windows 数组\n  整体替换，须带全量字段）在创建期即 `decorations: true` + Overlay——\n  decorations:false 的 borderless 窗口没有标准按钮，且运行时补\n  set_decorations 会与 Overlay 异步竞态（实测内容不延伸）；红绿灯由 lib.rs\n  `layout_traffic_lights` 摆位（wry 的 trafficLightPosition 纵向无效），\n  顶栏经 `body.platform-mac` 让位左侧 100px。Windows/Linux 用自绘\n  `components/layout/WindowControls`（顶栏右端三钮）。拖拽区仍由\n  `data-tauri-drag-region` 承担（双击 = 最大化切换）。\n\n## 测试要点\n\n- Vitest 使用 `vitest.config.ts`：开启 globals，jsdom 环境，setup 文件为\n  `src/__tests__/setup.ts`（测试管线同样挂了 React Compiler 编译器）。\n- setup 文件 mock 了 `localStorage`、`@tauri-apps/api/core` 的 `invoke`、\n  `@tauri-apps/api/event`、`@tauri-apps/api/webviewWindow`、\n  `@tauri-apps/plugin-shell`、`@tauri-apps/plugin-fs`、\n  `@tauri-apps/plugin-dialog` 和全局 `fetch`；测试在没有真实 Tauri 后端时\n  也能通过，判断测试结果时要记住这一点。\n- 前端测试主要在 `src/stores/__tests__/`、`src/helpers/__tests__/`、\n  `src/hooks/__tests__/`；Rust 测试如 `src-tauri/src/cmd.rs`、\n  `src-tauri/src/core/scheduler.rs`。\n\n## 样式与 i18n\n\n- Tailwind v3 + Radix UI 主题令牌配置在 `tailwind.config.js`；暗色模式基于\n  class/data-attribute，应用切换 `body.dark-theme`。\n- shadcn 风格组件在 `src/components/ui/`；组合类名优先用\n  `src/helpers/cn.tsx` 的 `cn`。\n- 0.2.0 的 UI 由 `src/styles/fusion.css`（全局 `fusion-*` 类）承载，经\n  `src/index.css` → `styles/index.css` 分层引入；Astryx 主题来自 npm 包\n  `@astryxdesign/theme-*`。无 PWA/service worker（vite-plugin-pwa 已移除，\n  忽略提及它的旧笔记）。\n- i18n 在 `src/i18n.ts` 初始化；语言包为 `src/locales/en.json` 与\n  `src/locales/zh.json`。\n\n## Rust 后端要点\n\n- Diesel schema 输出在 `src-tauri/src/schema.rs`；迁移在\n  `src-tauri/migrations/`，由 `embed_migrations!` 内嵌。\n- 调度器（`core/scheduler.rs`）按到期驱动：每 60s tick 一次，同步\n  `last_sync_date + (sync_interval 或 update_interval)` 已到期的源，然后每\n  个源 emit `sync://completed` `{uuid, title, inserted, error}`（在\n  `src/App.tsx` 监听）。源行的 `last_sync_date` 以本地时间存\n  \"YYYY-MM-DD HH:MM:SS\" 字符串。\n- Rust 模块按职责拆分：`core/`（配置/菜单/调度/托盘）、`feed/`（文章/\n  频道/文件夹/OPML）、`server/`（Actix 路由）。托盘与菜单在 `setup()` 里\n  用 `TrayIconBuilder`/`MenuBuilder` 构建。\n- Tauri v2 是插件架构：shell、fs、dialog、http、process、updater、log、\n  single-instance 都是独立插件（Rust crate 与 npm 包成对）。权限声明在\n  `src-tauri/capabilities/default.json`（取代 v1 的 allowlist）。\n- `LETTURA_ENV` 开启调试日志并改变配置/数据库行为；不要假设 dev 与生产\n  路径一致。\n\n## CI 与发布\n\n- `.github/workflows/release.yml` 在 push 到 `release` 时运行：创建 draft\n  release，再用 Tauri 构建 macOS（arm/x64/universal）、Ubuntu、Windows 产物。\n  自动更新链路：conf 开了 `bundle.createUpdaterArtifacts`（安装包 + 签名的\n  updater 产物），多平台 latest.json 由 tauri-action 合并上传——publish 前\n  人工核对 draft 里 latest.json 应含全部平台条目；客户端入口在设置页\n  「行为与数据 → 检查更新」（`UpdaterRow`，手动检查，无启动静默检查）。\n- `.github/workflows/deploy-doc.yml` 在 `master` 上运行，把 `docs/` 的\n  Astro 文档站部署到 GitHub Pages。\n- `package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml` 三处\n  版本号保持同步（bump 后本地 `pnpm cargo:check` 刷 Cargo.lock）。\n- **版本号规则（2026-10-11 用户定）**：除非用户明确指定版本号，发布一律在\n  0.2.x 线内 patch 递增（0.2.1 → 0.2.2 → …）；升 minor（如 0.3.0）必须\n  用户点名。\n- 发布顺序：三处版本号 bump 提交 → 推 `0.2.0` 分支与 `master`（ff）→\n  `git push origin 0.2.0:release` 触发构建 → 人工核对 draft 的 latest.json\n  全平台条目 → publish。\n","isInternal":false,"tokens":3595,"sizeBytes":10048},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/zhanglun/lettura/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Claude Code 项目配置\n\n## 提交规范\n\n- 使用中文 Conventional Commit 格式：`type(scope): 描述`\n- **不要**在提交信息中添加 `Co-Authored-By` 行\n- 每个主题独立提交，不要合并不相关的变更\n\n## 注意事项\n\n- 不要提交 `.sisyphus/run-continuation/*.json`\n- 生产 SQLite 在 `~/.lettura/lettura.db`，不要执行清库或迁移重建\n- 不要恢复 DnD 订阅树\n- 用中文沟通\n","isInternal":false,"tokens":150,"sizeBytes":425}],"systemPromptSnippet":"<yakaai_skills repo=\"zhanglun/lettura\">\n<!-- File: AGENTS.md (Tokens: ~3595 | Category: root-instruction) -->\n# AGENTS.md\n\n面向 AI 编码代理的仓库速查笔记。内容与可执行配置冲突时，以配置为准。\n\n## 项目形态\n\n- Lettura 是 Tauri v2 桌面订阅阅读器：React/Vite 前端 + Rust 后端（Cargo\n  workspace 成员 `apps/desktop/src-tauri`）。\n- 两套 workspace 共享仓库树：pnpm 管理 JS 包（`apps/desktop`、`apps/docs`、\n  未来的 `packages/*`），根 Cargo.toml 管理全部 Rust（成员\n  `apps/desktop/src-tauri` 与 `packages/*`）。pnpm 会静默忽略 Rust 目录；\n  Cargo.lock 只保留根目录一份。\n- 订阅抓取器是 `packages/fetcher-*` 下的独立 crate（`fetcher-core` =\n  `Fetcher` trait + `FetchedArticle`；`fetcher-rss` = 解析/发现层；\n  `fetcher-mail` = IMAP（async-imap 0.10 + mail-parser 0.11，两者均已锁\n  feature）；`fetcher-bilibili` = wbi 签名 Web API）。抓取器必须无状态，\n  禁止依赖 diesel/tauri/actix；注册表、探测分发（`claims` 顺序、rss 兜底）\n  与 `FetchedArticle → NewArticle` 转换器在 `src-tauri/src/fetchers/mod.rs`；\n  按 `feeds.provider` 的同步分发在 `feed/channel.rs::sync_articles`。\n  新增源类型 = 新 crate + 注册表加一行。每源配置存在\n  `feeds.provider/account_uuid/source_config`；凭据在 `source_accounts`\n  表（`sources/account_service.rs`，命令\n  `list/save/delete/test_source_account`）。邮件同步会把 `last_uid`\n  水位写回 `source_config`。\n- 前端数据访问只走 localhost HTTP（`src/helpers/http.ts` 的\n  `apiGet/apiPost`，对准内嵌 Actix 服务 `http://127.0.0.1:{port}/api`，\n  实现在 `src-tauri/src/server/handlers/`）。dataAgent 已移除（2026-09-30，\n  提交 5a8278b4 回滚了此前的 IPC-only 收拢）。Tauri `invoke` 仅保留非数据\n  命令（窗口/端口、OPML、来源账户、订阅添加/预览）。新数据操作 =\n  新增 Actix handler。\n- 前端数据流是两条车道（保持现状）：文章列表查询走 `src/hooks/useArticle.ts`\n  （模块级缓存 + 在途去重，列表状态的唯一真相源）；命令类 POST 可直接\n  `apiPost`，然后经 `store.getSubscribes()` 和/或\n  `busChannel.emit(\"getChannels\")` 刷新（AppLayout 监听一次）。改变文章\n  全集的命令（退订/删除源等）成功后必须调\n  `invalidateArticleCache()`（useArticle.ts 导出，含在途世代守卫），否则\n  返回列表页会命中陈旧缓存。不要引入第三层缓存，不要把列表状态塞回\n  Zustand slice。\n- React Compiler 1.0 已接入 vite.config.ts 与 vitest.config.ts\n  （babel-plugin-react-compiler）：src 下所有组件/钩子都被编译（零\n  bail-out）并自动记忆化。禁止再写 useCallback/useMemo/React.memo——\n  记忆化由编译器负责，手写hook 曾经直接阻断编译（\"Existing memoization\n  could not be preserved\"）。塑造了现有代码形态的编译器约束（务必继续\n  遵守）：渲染期禁止写 ref（在 useEffect 里同步）；组件/钩子内禁止\n  try/finally 语句（用 promise 的 .catch()/.finally()）；组件体内禁止\n  import() 表达式（提升到模块级）；渲染期禁止读可变模块级状态（缓存、\n  单例）——编译器会把这类读当作纯计算缓存住陈旧值。跨渲染数据一律走\n  state 传递，缓存在 effect 里同步（键切换不允许画出旧帧时用\n  useLayoutEffect）。\n- 前端入口：`src/index.tsx` 定义路由并在 Tauri 内等待 `get_server_port`；\n  `src/App.tsx` 是应用外壳与 Tauri 事件监听；路由名在 `src/config.ts`。\n- Rust 入口：`src-tauri/src/main.rs` 调用 `lettura_lib::run()`（定义在\n  `src-tauri/src/lib.rs`）：加载配置、打开 SQLite、跑内嵌 Diesel 迁移、\n  启动 Actix 服务、注册 Tauri 命令、托盘/菜单处理器与调度器。\n- 状态是单一 Zustand store（`useAppStore`，`src/stores/index.ts`），由\n  feed、article、user config、podcast 四个 slice 组合而成。\n\n## 常用命令\n\n- 安装：`pnpm install`（`.npmrc` 设了 `auto-install-peers=true`）。\n- 仅前端开发：`pnpm dev`（Vite 固定端口 9527）。\n- 桌面完整开发：`pnpm tauri dev`（经 `src-tauri/tauri.conf.json` 运行\n  `pnpm dev`）。\n- 前端构建/类型检查：`pnpm build`（`tsc && vite build`）；Vite 输出到\n  `build/`，不是 `dist/`。\n- 桌面构建：`pnpm tauri build`。\n- 前端测试：`pnpm test`；单文件：`pnpm test path/to/file.test.ts`。\n- Rust 测试：在仓库根跑 `cargo test`（workspace 成员是 `src-tauri` 与\n  `packages/*`）；`pnpm cargo:check|cargo:test|cargo:fmt` 是便捷封装。\n- Lint/格式化用 Biome（`apps/desktop/biome.json`），不是 ESLint/Prettier：\n  `npx biome check src/` 与 `npx biome format src/`（在 `apps/desktop/`\n  下执行）。\n\n## 运行时与存储要点\n\n- Vite 固定 `server.strictPort = true`，端口 9527。Actix API 服务先用配置\n  端口，被占用则回落 8000-9000。\n- Tauri 之外，`src/index.tsx` 把 `localStorage.port` 默认为 `3456`；Tauri\n  内渲染前调用 `invoke(\"get_server_port\")`。\n- 生产 SQLite 库是 `~/.lettura/lettura.db`。设置 `LETTURA_ENV` 后，Rust 经\n  `dotenv` 从环境变量读 `DATABASE_URL`。\n- 用户配置是 TOML（正常运行为 `~/.lettura/lettura.toml`，dev 模式为本地\n  文件），不在 SQLite schema 内。\n- 播客数据是独立的浏览器端 Dexie/IndexedDB（`src/helpers/podcastDB.ts`），\n  读写统一收口在 `stores/createPodcastSlice.ts` 的 action 与渲染端\n  useLiveQuery。\n- 关闭主窗口是隐藏到系统托盘，不是退出（release；dev 直接退出）。窗口控制\n  跨平台：macOS 走 `tauri.macos.conf.json`（平台配置合并，windows 数组\n  整体替换，须带全量字段）在创建期即 `decorations: true` + Overlay——\n  decorations:false 的 borderless 窗口没有标准按钮，且运行时补\n  set_decorations 会与 Overlay 异步竞态（实测内容不延伸）；红绿灯由 lib.rs\n  `layout_traffic_lights` 摆位（wry 的 trafficLightPosition 纵向无效），\n  顶栏经 `body.platform-mac` 让位左侧 100px。Windows/Linux 用自绘\n  `components/layout/WindowControls`（顶栏右端三钮）。拖拽区仍由\n  `data-tauri-drag-region` 承担（双击 = 最大化切换）。\n\n## 测试要点\n\n- Vitest 使用 `vitest.config.ts`：开启 globals，jsdom 环境，setup 文件为\n  `src/__tests__/setup.ts`（测试管线同样挂了 React Compiler 编译器）。\n- setup 文件 mock 了 `localStorage`、`@tauri-apps/api/core` 的 `invoke`、\n  `@tauri-apps/api/event`、`@tauri-apps/api/webviewWindow`、\n  `@tauri-apps/plugin-shell`、`@tauri-apps/plugin-fs`、\n  `@tauri-apps/plugin-dialog` 和全局 `fetch`；测试在没有真实 Tauri 后端时\n  也能通过，判断测试结果时要记住这一点。\n- 前端测试主要在 `src/stores/__tests__/`、`src/helpers/__tests__/`、\n  `src/hooks/__tests__/`；Rust 测试如 `src-tauri/src/cmd.rs`、\n  `src-tauri/src/core/scheduler.rs`。\n\n## 样式与 i18n\n\n- Tailwind v3 + Radix UI 主题令牌配置在 `tailwind.config.js`；暗色模式基于\n  class/data-attribute，应用切换 `body.dark-theme`。\n- shadcn 风格组件在 `src/components/ui/`；组合类名优先用\n  `src/helpers/cn.tsx` 的 `cn`。\n- 0.2.0 的 UI 由 `src/styles/fusion.css`（全局 `fusion-*` 类）承载，经\n  `src/index.css` → `styles/index.css` 分层引入；Astryx 主题来自 npm 包\n  `@astryxdesign/theme-*`。无 PWA/service worker（vite-plugin-pwa 已移除，\n  忽略提及它的旧笔记）。\n- i18n 在 `src/i18n.ts` 初始化；语言包为 `src/locales/en.json` 与\n  `src/locales/zh.json`。\n\n## Rust 后端要点\n\n- Diesel schema 输出在 `src-tauri/src/schema.rs`；迁移在\n  `src-tauri/migrations/`，由 `embed_migrations!` 内嵌。\n- 调度器（`core/scheduler.rs`）按到期驱动：每 60s tick 一次，同步\n  `last_sync_date + (sync_interval 或 update_interval)` 已到期的源，然后每\n  个源 emit `sync://completed` `{uuid, title, inserted, error}`（在\n  `src/App.tsx` 监听）。源行的 `last_sync_date` 以本地时间存\n  \"YYYY-MM-DD HH:MM:SS\" 字符串。\n- Rust 模块按职责拆分：`core/`（配置/菜单/调度/托盘）、`feed/`（文章/\n  频道/文件夹/OPML）、`server/`（Actix 路由）。托盘与菜单在 `setup()` 里\n  用 `TrayIconBuilder`/`MenuBuilder` 构建。\n- Tauri v2 是插件架构：shell、fs、dialog、http、process、updater、log、\n  single-instance 都是独立插件（Rust crate 与 npm 包成对）。权限声明在\n  `src-tauri/capabilities/default.json`（取代 v1 的 allowlist）。\n- `LETTURA_ENV` 开启调试日志并改变配置/数据库行为；不要假设 dev 与生产\n  路径一致。\n\n## CI 与发布\n\n- `.github/workflows/release.yml` 在 push 到 `release` 时运行：创建 draft\n  release，再用 Tauri 构建 macOS（arm/x64/universal）、Ubuntu、Windows 产物。\n  自动更新链路：conf 开了 `bundle.createUpdaterArtifacts`（安装包 + 签名的\n  updater 产物），多平台 latest.json 由 tauri-action 合并上传——publish 前\n  人工核对 draft 里 latest.json 应含全部平台条目；客户端入口在设置页\n  「行为与数据 → 检查更新」（`UpdaterRow`，手动检查，无启动静默检查）。\n- `.github/workflows/deploy-doc.yml` 在 `master` 上运行，把 `docs/` 的\n  Astro 文档站部署到 GitHub Pages。\n- `package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml` 三处\n  版本号保持同步（bump 后本地 `pnpm cargo:check` 刷 Cargo.lock）。\n- **版本号规则（2026-10-11 用户定）**：除非用户明确指定版本号，发布一律在\n  0.2.x 线内 patch 递增（0.2.1 → 0.2.2 → …）；升 minor（如 0.3.0）必须\n  用户点名。\n- 发布顺序：三处版本号 bump 提交 → 推 `0.2.0` 分支与 `master`（ff）→\n  `git push origin 0.2.0:release` 触发构建 → 人工核对 draft 的 latest.json\n  全平台条目 → publish。\n\n<!-- File: CLAUDE.md (Tokens: ~150 | Category: claude-rule) -->\n# Claude Code 项目配置\n\n## 提交规范\n\n- 使用中文 Conventional Commit 格式：`type(scope): 描述`\n- **不要**在提交信息中添加 `Co-Authored-By` 行\n- 每个主题独立提交，不要合并不相关的变更\n\n## 注意事项\n\n- 不要提交 `.sisyphus/run-continuation/*.json`\n- 生产 SQLite 在 `~/.lettura/lettura.db`，不要执行清库或迁移重建\n- 不要恢复 DnD 订阅树\n- 用中文沟通\n</yakaai_skills>"}