Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/rules/projects-tray-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ paths:
- "src-tauri/src/native_open.rs"
- "src-tauri/src/tray.rs"
- "src-tauri/src/terminal_focus.rs"
- "src-tauri/src/herdr.rs"
- "src-tauri/src/logging.rs"
- "src-tauri/src/config.rs"
- "src-tauri/src/lib.rs"
Expand Down Expand Up @@ -76,6 +77,13 @@ paths:
- 会话菜单项 id 需要能安全携带 `pid` 和 `cwd`;`cwd` 可能包含中文、空格、引号和 `::`。
- Terminal.app 与 iTerm2 通过 `pid -> tty -> AppleScript` 精确聚焦已有 tab。
- Ghostty 的 AppleScript 目前按 `working directory` 近似匹配,命中后直接 `focus term`;不要使用 `select tab t of w` 这类循环变量 specifier,容易触发 `-1700` 类型错误。
- herdr 会话(跑在 herdr pane 里的 Claude Code)走"两跳聚焦":先连 herdr unix socket API 按 pid 定位 pane 并 `pane.focus`,再找到附着的 herdr client 进程(其 tty 即宿主 tab 的 tty)复用宿主终端 AppleScript。逻辑集中在 `src-tauri/src/herdr.rs`,`terminal_focus.rs` 只做编排。
- herdr 检测:先读会话进程 env(`HERDR_SESSION` / `HERDR_SOCKET_PATH` 标记,命名会话才有),无标记时沿 ppid 链找名为 `herdr` 的祖先进程(默认会话没有 env 标记,只能靠进程树)。
- herdr socket 路径:`HERDR_SOCKET_PATH` > `<config>/herdr/sessions/<name>/herdr.sock`(命名会话)> `<config>/herdr/herdr.sock`;`HERDR_SESSION` 必须按 herdr 命名规则白名单校验,防路径穿越。
- herdr pane 匹配:pid 精确匹配优先(`pane.list` + 逐 pane `pane.process_info`,命中 foreground pid / shell_pid / 进程组 id),失配或歧义时用 `agent.list` 按 cwd 兜底;兜底也只允许唯一匹配。
- herdr 宿主跳:扫描 `herdr` 进程(comm 可能带完整路径,如 `/opt/homebrew/bin/herdr`,需同时匹配)+ 会话一致性(env 的 `HERDR_SESSION`/`HERDR_SOCKET_PATH`,或 argv 的 `--session <name>` / `session attach <name>`——herdr 0.7.x 的 client env 不带标记,会话名只在 argv)+ 真实 tty 过滤(daemon 无 tty 天然排除);Ghostty 宿主按 client 进程 cwd(`lsof`)匹配,不要用 pane 的 cwd。
- herdr 降级语义:socket 跳失败(`HerdrNotRunning` / `HerdrPaneNotFound`)才向用户报错;宿主跳失败或找不到 client(detach / ssh 远程附着)按"部分成功"只记 warn,不弹通知。
- 聚焦可用性门禁按会话判定(`session_focus_available`):macOS 且(默认终端支持聚焦,或 pid 自身宿主终端支持,或会话在 herdr 里);全局快捷键只挑可聚焦会话。
- 未命中或聚焦失败只记录 warn 日志,不要自动新开窗口或 tab。
- `osascript` 可能较慢,托盘点击 handler 不应阻塞 UI 事件循环。

Expand Down
3 changes: 2 additions & 1 deletion .claude/rules/tauri-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ paths:
| `claude_directory.rs` | `~/.claude` 文件树、文件预览、创建、重命名、删除与外部打开 |
| `claude_directory_watcher.rs` | `~/.claude` 变更监听并广播 `claude-directory-changed` |
| `native_open.rs` | 默认终端 / 编辑器跨平台启动、本机检测受支持工具清单 |
| `terminal_focus.rs` | macOS 上 `pid -> tty -> AppleScript` 聚焦 Terminal.app / iTerm / Ghostty |
| `terminal_focus.rs` | macOS 上 `pid -> tty -> AppleScript` 聚焦 Terminal.app / iTerm / Ghostty;herdr 会话两跳聚焦编排 |
| `herdr.rs` | herdr 会话聚焦:socket API 客户端(NDJSON)、pane 定位(pid 精确 + cwd 兜底)、附着 client 进程发现 |
| `tray.rs` | 系统托盘:配置切换、会话视图、页面导航 |
| `logging.rs` | tauri-plugin-log 配置、日志脱敏 helper、panic hook |
| `plugins.rs` | 插件市场后端操作:触发 `claude plugin list --available --json`,让 claude 按 24h TTL 默认策略刷新插件安装数缓存(不主动删缓存、不强制刷新) |
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
- 记忆与 Skills:`src-tauri/src/memory.rs`、`src-tauri/src/skills.rs`
- 历史、统计、用量:`src-tauri/src/history.rs`、`src-tauri/src/stats.rs`、`src-tauri/src/usage.rs`
- 项目、打开本机应用、托盘:`src-tauri/src/project.rs`、`src-tauri/src/native_open.rs`、`src-tauri/src/tray.rs`
- 会话终端聚焦:`src-tauri/src/terminal_focus.rs`(AppleScript)、`src-tauri/src/herdr.rs`(herdr 两跳聚焦)
- 会话终端聚焦:`src-tauri/src/terminal_focus.rs`(AppleScript)、`src-tauri/src/herdr.rs`(herdr 两跳聚焦)
- 日志与诊断:`src-tauri/src/logging.rs`
- 内置 provider、模型价格和状态行脚本:`src-tauri/resources/`
- Tauri capability:`src-tauri/capabilities/default.json`
Expand Down Expand Up @@ -122,9 +124,7 @@ macOS 上应用数据刻意复用 `~/.config/code-manager/`,便于跨平台备
## 已知陷阱

- CodeMirror 多版本冲突会导致空白页或配置预览渲染崩溃(`@codemirror/state` 必须全局单例)。排查:`grep "'@codemirror/state@" pnpm-lock.yaml`,预期只有一个版本;如出现多个版本,在 `pnpm-workspace.yaml` 的 `overrides` 段统一(pnpm 11 已不再读取 `package.json` 的 `pnpm.overrides`)。
- 业务代码不要直接调用 `invoke`;新增 IPC 先更新 Specta command 集合并走 `src/ipc.ts`。`src/bindings.ts` 是生成文件,不手改。
- 不要自实现浮层:抽屉、设置面板、模态框、下拉菜单和 Toast 都用 shadcn `Sheet` / `Dialog` / `DropdownMenu` / `Popover` / sonner。
- 不要混淆 Projects、Stats 与 Usage 的数据源。
- 不要把日志当成配置数据:日志目录由 Tauri 的 `app_log_dir()` 解析。
- 当前配置 schema 是 `src/schemas/claude-settings.schema.json`。
- Tauri 事件监听器必须在组件卸载时清理;使用 `useTauriEvent` hook 而非直接调用 `listen()`。
Expand Down
6 changes: 6 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ _Avoid_: 自动模式
一个处于 running 类状态(`running / busy / active / starting`)的 Claude Code 会话。**waiting(等待用户操作)不计入**——那时 Claude 卡在等人,机器休眠也不会杀死会话。"仅活动时"模式据此判断是否保持唤醒。
_Avoid_: 运行中会话(running session,只是其中一个具体状态)

### 会话聚焦(Session Focus)

**会话聚焦(Session Focus)**:
把承载[活动会话](#活动会话active-session)的终端视图带到前台的动作:宿主终端窗口/tab 激活,以及多路复用器(如 herdr)内部的 pane 选中。聚焦的载体随会话所在环境不同而不同(终端 tab、herdr pane、Ghostty term),但语义不变:用户眼睛看到承载该会话的视图并可直接交互。
_Avoid_: 聚焦终端 tab(herdr 场景没有 tab 概念)、激活窗口(只覆盖一半语义)

### 目录总览(Directory Overview)

**目录总览(Directory Overview)**:
Expand Down
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Code Manager does not replace Claude Code; it provides a management layer for lo
| Capability | Description |
| --- | --- |
| `~/.claude` Overview | Browse, preview, edit, and locate the Claude Code user directory. |
| Profiles / Built-in Providers | Manage the profile layer ultimately written to `~/.claude/settings.json`, picking connection endpoints and model mappings from built-in (read-only) providers. Edit models, environment variables, permissions, Sandbox, hooks, plugins, and the status line. Preview, copy, test models, apply in one click, import an existing settings file, export (optionally with secrets, with a pre-save preview), compare diffs, and sync common options / marketplaces / plugins to other profiles. |
| Profiles / Built-in Providers | Manage the profile layer ultimately written to `~/.claude/settings.json`, picking connection endpoints and model mappings from built-in (read-only) providers. Edit models, environment variables, permissions, Sandbox, hooks, plugins, and the status line; preview, copy, test models, apply in one click, import / export (optionally with secrets, with a pre-save preview), compare diffs, and sync common options / marketplaces / plugins to other profiles in one click. |
| Memory Management | Manage user-level `CLAUDE.md` and `rules/*.md`, with support for the Karpathy behavior guide preset, import, enable, disable, copy, preview, and path validation. |
| Skills Management | Create, edit, delete, enable, and disable Claude Code Skills, and sync them as `~/.codex/skills/<id>` symlinks. |
| History & Sessions | Read `~/.claude/history.jsonl` and view history details by project and session. |
Expand Down Expand Up @@ -88,7 +88,7 @@ Code Manager mainly reads and writes local files. Profile merging, directory sca
| Usage SQLite | `~/Library/Application Support/com.gotobeta.app.code-manager/usage.db` | `$XDG_CONFIG_HOME/com.gotobeta.app.code-manager/usage.db` or `~/.config/com.gotobeta.app.code-manager/usage.db` | `%APPDATA%\com.gotobeta.app.code-manager\usage.db` |
| Log directory | `~/Library/Logs/com.gotobeta.app.code-manager/` | `$XDG_DATA_HOME/com.gotobeta.app.code-manager/logs/` or `~/.local/share/com.gotobeta.app.code-manager/logs/` | `%LOCALAPPDATA%\com.gotobeta.app.code-manager\logs\` |

The application data directory contains `config-registry.json`, `memories.json`, `model-pricing.json`, and `skills-disabled/`. On macOS, application data deliberately uses `~/.config/code-manager/` for easier cross-platform backup and script access; SQLite uses Tauri's `app_config_dir()`, and logs use the Tauri plugin's default path.
The application data directory contains `config-registry.json`, `memories.json`, `model-pricing.json`, and `skills-disabled/`. On macOS, application data deliberately reuses `~/.config/code-manager/` for easier cross-platform backup and script access.

## Local Development

Expand Down Expand Up @@ -129,15 +129,11 @@ Build artifacts are located by default in `src-tauri/target/release/bundle/`.

### Repository at a Glance

| Path | Purpose |
| --- | --- |
| `src/` | React frontend pages, components, hooks, schemas, and tests. |
| `src/components/` | Page-level components and reusable UI; finer component entry points are in `CLAUDE.md`. |
| `src-tauri/src/` | Rust backend, Tauri commands, and local file and data capabilities. |
| `src-tauri/resources/` | Built-in providers, model pricing, status line scripts, and other resources. |
| `src-tauri/capabilities/` | Tauri permission declarations. |
| `docs/` | User manual, platform differences, and extended documentation. |
| `.claude/rules/` | Path-scoped maintenance rules for AI agents. |
- `src/`: React frontend pages, components, hooks, schemas, and tests.
- `src-tauri/`: Rust backend, Tauri commands, built-in resources, and permission declarations.
- `docs/`: user manual, platform differences, and extended documentation.

For fine-grained component entry points, module responsibilities, and path navigation for AI agents, see [CLAUDE.md](./CLAUDE.md).

## Contributing and Feedback

Expand All @@ -152,7 +148,6 @@ When filing an issue, please include as much as possible:

- [docs/user-manual.md](./docs/user-manual.md): the complete user manual
- [docs/platform-support.md](./docs/platform-support.md): platform support differences
- [docs/claude-code-best-practices.md](./docs/claude-code-best-practices.md): extended best practices for Claude Code / Codex in this repo
- [CLAUDE.md](./CLAUDE.md): the repository execution manual for AI agents
- [LICENSE](./LICENSE): license

Expand Down
19 changes: 7 additions & 12 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Code Manager 不替代 Claude Code,而是提供一个本机配置、会话数
| 能力 | 说明 |
| --- | --- |
| `~/.claude` 总览 | 浏览、预览、编辑和定位 Claude Code 用户目录。 |
| 配置 / 内置供应商 | 管理最终写入 `~/.claude/settings.json` 的配置层,从内置供应商(只读)选择连接地址与模型映射。可编辑模型、环境变量、权限、Sandbox、Hooks、插件与状态行支持预览、复制、模型测试、一键应用、导入现有 settings、导出(可选含密钥、落盘前预览)、差异对比,以及一键把常用选项 / 市场 / 插件同步到其他配置。 |
| 配置 / 内置供应商 | 管理最终写入 `~/.claude/settings.json` 的配置层,从内置供应商(只读)选择连接地址与模型映射。可编辑模型、环境变量、权限、Sandbox、Hooks、插件与状态行支持预览、复制、模型测试、一键应用、导入 / 导出(可选含密钥、落盘前预览、差异对比,以及把常用选项 / 市场 / 插件一键同步到其他配置。 |
| 记忆管理 | 管理用户级 `CLAUDE.md` 与 `rules/*.md`,支持 Karpathy 行为指南预设、导入、启用、禁用、复制、预览和路径校验。 |
| Skills 管理 | 新建、编辑、删除、启用、禁用 Claude Code Skills,并可同步为 `~/.codex/skills/<id>` 软链接。 |
| 历史与会话 | 读取 `~/.claude/history.jsonl`,按项目和会话查看历史详情。 |
Expand Down Expand Up @@ -88,7 +88,7 @@ Code Manager 主要读写本机文件。配置合并、目录扫描、用量聚
| 用量 SQLite | `~/Library/Application Support/com.gotobeta.app.code-manager/usage.db` | `$XDG_CONFIG_HOME/com.gotobeta.app.code-manager/usage.db` 或 `~/.config/com.gotobeta.app.code-manager/usage.db` | `%APPDATA%\com.gotobeta.app.code-manager\usage.db` |
| 日志目录 | `~/Library/Logs/com.gotobeta.app.code-manager/` | `$XDG_DATA_HOME/com.gotobeta.app.code-manager/logs/` 或 `~/.local/share/com.gotobeta.app.code-manager/logs/` | `%LOCALAPPDATA%\com.gotobeta.app.code-manager\logs\` |

应用数据目录包含 `config-registry.json`、`memories.json`、`model-pricing.json` 和 `skills-disabled/`。macOS 上应用数据刻意使用 `~/.config/code-manager/`,便于跨平台备份和脚本访问;SQLite 使用 Tauri `app_config_dir()`,日志使用 Tauri 插件默认路径
应用数据目录包含 `config-registry.json`、`memories.json`、`model-pricing.json` 和 `skills-disabled/`。macOS 上应用数据刻意复用 `~/.config/code-manager/`,便于跨平台备份和脚本访问。

## 本地开发

Expand Down Expand Up @@ -129,15 +129,11 @@ make test-frontend # 运行前端测试

### 仓库速览

| 路径 | 用途 |
| --- | --- |
| `src/` | React 前端页面、组件、hooks、schema 与测试。 |
| `src/components/` | 页面级组件和复用 UI;更细组件入口见 `CLAUDE.md`。 |
| `src-tauri/src/` | Rust 后端、Tauri command、本地文件与数据能力。 |
| `src-tauri/resources/` | 内置 provider、模型价格和状态行脚本等资源。 |
| `src-tauri/capabilities/` | Tauri 权限声明。 |
| `docs/` | 用户手册、平台差异和扩展文档。 |
| `.claude/rules/` | 面向 AI Agent 的 path-scoped 维护规则。 |
- `src/`:React 前端页面、组件、hooks、schema 与测试。
- `src-tauri/`:Rust 后端、Tauri command、内置资源与权限声明。
- `docs/`:用户手册、平台差异和扩展文档。

细粒度的组件入口、模块职责和面向 AI Agent 的路径导航见 [CLAUDE.md](./CLAUDE.md)。

## 贡献与反馈

Expand All @@ -152,7 +148,6 @@ make test-frontend # 运行前端测试

- [docs/user-manual.zh-CN.md](./docs/user-manual.zh-CN.md):完整用户说明书
- [docs/platform-support.zh-CN.md](./docs/platform-support.zh-CN.md):平台支持差异
- [docs/claude-code-best-practices.md](./docs/claude-code-best-practices.md):Claude Code / Codex 在本仓库的扩展最佳实践
- [docs/claude-code/plugin-update.md](./docs/claude-code/plugin-update.md):Claude Code 插件更新方式
- [CLAUDE.md](./CLAUDE.md):面向 AI Agent 的仓库执行手册
- [LICENSE](./LICENSE):许可证
Expand Down
22 changes: 22 additions & 0 deletions docs/adr/0004-herdr-session-focus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# herdr 会话聚焦走"两跳":socket 定位 pane + 宿主终端激活

## Context

会话托盘"聚焦终端"依赖 `pid -> tty -> AppleScript`:把会话进程的 tty 拿去宿主终端里按 tab 匹配。herdr(agent 多路复用器,跑在宿主终端内部,类 tmux)打破了这个前提——Claude Code 进程跑在 herdr 自己创建的 pty 上,宿主终端 tab 列表里根本没有这个 tty,聚焦必然失配(TabNotFound)。

调查 herdr 源码后确认三个事实:server 是 detached 的 headless daemon,pane 是它的子进程;socket API(NDJSON over unix socket)只改 herdr 内部焦点,没有任何接口能激活宿主窗口;TUI client 进程跑在宿主终端 tab 里,其 tty 就是宿主 tab 的 tty,但它不是 pane 进程的祖先,pid 反查不到。此外默认会话的 pane 进程没有任何环境标记(只有命名会话带 `HERDR_SESSION`)。

## Decision

1. **两跳聚焦**:第一跳连 herdr socket API,`pane.list` + 逐 pane `pane.process_info` 按 pid 精确匹配(命中 foreground pid / shell_pid / 进程组 id),失配时 `agent.list` 按 cwd 兜底且只允许唯一匹配;命中后 `pane.focus`。第二跳扫描本机 `herdr` 进程,按会话一致性(env 标记或 argv 会话参数)+ 真实 tty 过滤找到 client(daemon 无 tty 天然排除),其 tty 即宿主 tab tty,复用现有 Terminal/iTerm AppleScript;Ghostty 宿主按 client 进程 cwd(`lsof`)匹配。socket 跳失败即报错并跳过宿主跳。
2. **降级语义**:socket 跳成功但宿主跳失败或找不到 client(detach / ssh 远程附着)按"部分成功"处理,只记 warn 不弹通知——herdr 内部焦点已切换,没有窗口可激活不是错误。
3. **检测**:先读 pane 进程 env(`HERDR_SESSION` / `HERDR_SOCKET_PATH` 标记),无标记再沿 ppid 链找名为 `herdr` 的祖先进程(覆盖默认会话);`HERDR_SESSION` 按 herdr 命名规则白名单校验防路径穿越。
4. **门禁放宽**:聚焦可用性从"只看默认终端 slug"改为按会话判定(macOS 且默认终端支持,或 pid 自身宿主终端支持,或会话在 herdr 里);全局快捷键只挑可聚焦会话。不做"菜单扫描成本"的缓存——ps 调用在菜单重建频率下可忽略。
5. **实现位置**:herdr 侧逻辑独立成 `herdr.rs`,`terminal_focus.rs` 只做编排,AppleScript 模板不重复。socket 用 std `UnixStream`,零新依赖,1s 读写超时防后台线程卡死。

## Consequences

- 协议按 herdr 源码快照实现(无官方 schema 文件);herdr 协议若变动,socket 调用失败会走 `HerdrNotRunning` / 通用 `ScriptError` 兜底,不会崩溃,但需要跟进。
- 宿主终端必须仍受支持(Terminal / iTerm / Ghostty)才有完整两跳;herdr 跑在 Warp 等无 AppleScript 宿主里只能得到内部聚焦 + warn。
- 多 tab 附着同一 herdr 会话时取第一个 client(UI 内容相同,聚焦任一都正确);本地找不到 client 时不激活窗口。
- 后续其它多路复用器(如 tmux)支持会以"检测 → 内部聚焦 API → 宿主激活"三段式结构为参照,但各自机制不同,不应直接套用 herdr 实现。
Loading