Skip to content

Commit bd0966c

Browse files
committed
feat(shell): 增加 Claude profile 切换工具
1 parent cb858c7 commit bd0966c

10 files changed

Lines changed: 1099 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Claude Code 多 key 切换工具设计
2+
3+
## Architecture and Boundaries
4+
5+
实现入口分为两处:
6+
7+
- `shell/shared.d/claude-profile.sh`:Bash/Zsh 通用 profile 命令集合。
8+
- `shell/shared.d/ai.sh`:保留通知/铃声等轻量 helper。
9+
10+
首版只实现 Bash/Zsh 侧命令,不修改 PowerShell profile。桌面账号级切换由 `ccswitch` 承担。工具包含三类入口:
11+
12+
- 临时启动入口:读取本机 profile,把 env 只注入当前 `claude` 进程,例如 `claude-profile run glm`
13+
- 项目持久化入口:读取本机 profile,更新当前项目的 Claude Code local settings,例如 `claude-profile use glm`
14+
- profile 创建入口:创建 `~/.claude/profiles/<name>.json` 模板并打开编辑器,例如 `claude-profile add glm`
15+
16+
工具不负责管理全局 Claude 配置同步。
17+
18+
本机 profile 目录:
19+
20+
```text
21+
~/.claude/profiles/
22+
├── glm.json
23+
└── official.json
24+
```
25+
26+
项目写入目标:
27+
28+
```text
29+
<project>/.claude/settings.local.json
30+
```
31+
32+
首版不写仓库可提交的 `.claude/settings.json`,避免误提交 secrets。
33+
34+
## Data Flow
35+
36+
### 临时启动
37+
38+
1. 用户在任意目录执行 `claude-profile run glm`
39+
2. 函数解析 `~/.claude/profiles/glm.json`
40+
3. 函数把 profile 的 `env` 注入当前 `claude` 子进程。
41+
- Bash/Zsh:以子进程环境变量方式执行 `claude "$@"`
42+
4. 函数将剩余参数原样透传给 `claude`,例如 `claude-profile run glm -p "检查状态"`
43+
5. 函数通过会话级 `--settings <file-or-json>` 覆盖或等价临时注入方式,让 profile 的 `env` 优先生效,同时保留 user/project/local 的其他配置。
44+
6. 函数不写 `.claude/settings.local.json`,因此没有项目目录也能正常使用。
45+
46+
### 项目持久化
47+
48+
1. 用户执行 `claude-profile use glm`
49+
2. 函数解析 `~/.claude/profiles/glm.json`
50+
3. 函数创建当前目录下 `.claude/`
51+
4. 函数读取已有 `.claude/settings.local.json`,若不存在则从空对象开始。
52+
- Bash/Zsh 使用 `jq` 做 JSON 解析与合并;若缺失,给出明确安装提示。
53+
5. 函数合并 profile 内容:
54+
- `env` 对象按键覆盖。
55+
- 额外记录当前 profile 名称,用于 `claude-profile current` 展示。
56+
6. 函数原子写回 `.claude/settings.local.json`
57+
7. 用户打开 / Reload VS Code 后使用 Claude 插件,或在新 Claude Code 会话中读取项目 settings。
58+
59+
### Profile 创建
60+
61+
1. 用户执行 `claude-profile add glm`
62+
2. 函数检查 `~/.claude/profiles/glm.json` 是否存在。
63+
3. 若不存在,函数生成最小模板文件,包含 `env` 占位结构与常见注释提示。
64+
4. 函数使用 `$VISUAL` / `$EDITOR` 或平台默认编辑器打开该文件。
65+
5. 用户保存后,profile 可立即被 `run``use` 使用。
66+
67+
## Contracts
68+
69+
profile JSON 首版合同:
70+
71+
```json
72+
{
73+
"env": {
74+
"ANTHROPIC_API_KEY": "",
75+
"ANTHROPIC_BASE_URL": "http://127.0.0.1:34000"
76+
}
77+
}
78+
```
79+
80+
`claude-profile current` 输出合同:
81+
82+
- 显示当前项目 settings 文件路径。
83+
- 显示当前 profile 名称。
84+
- 显示 `ANTHROPIC_BASE_URL`、模型相关变量。
85+
- API key 只显示是否存在和脱敏尾部,不输出完整值。
86+
87+
快捷命令合同:
88+
89+
- `claude-profile run <profile> [args...]` 是通用入口。
90+
- `claude-profile current` 返回当前项目所选 profile。
91+
- `claude-profile list` 列出 profile 目录。
92+
- `claude-profile add <profile>` 创建模板并打开编辑器。
93+
- 命令不存在或 profile 不存在时,输出可行动的错误提示。
94+
95+
## Compatibility Notes
96+
97+
- Context7 查询到的 Claude Code 文档显示 VS Code 扩展共享 Claude Code settings,用户级 settings 明确共享;项目级 settings 是 Claude Code 的正常配置来源。基于此,项目 `.claude/settings.local.json` 是比 shell export 更兼容 VS Code 插件的主路。
98+
- 官方 settings hierarchy 显示命令行参数优先级高于 local/project/user,因此 `run` 更适合做临时会话覆盖,而不是排除 user settings。
99+
- 现成工具也大多分成三类:`settings.local.json`/`settings.json` 管理器、`~/.claude` 目录级 profile/symlink 切换、以及只负责导出 env/启动 Claude 的 wrapper。
100+
- 如果某个版本的 VS Code 插件不加载项目 local settings,备用方案才考虑 `CLAUDE_CONFIG_DIR` 或全局 symlink 方案,但那会偏向机器全局,不适合作为 per-project 默认。
101+
- `shell/shared.d/ai.sh``shell/deploy.sh` 分发到 Bash/Zsh loader,函数实现应避免使用只在交互式 Bash 可用的特性。
102+
- `shell/shared.d/claude-profile.sh` 会和 `ai.sh` 一起被 `shell/deploy.sh` 链接到目标目录,文件粒度比继续堆在 `ai.sh` 更清晰。
103+
- 首版不修改 `profile/core/``profile/profile.ps1`,因此不触发 PowerShell profile 加载风险。
104+
- `cc` 在当前环境里已经是 `/usr/bin/cc`,所以把主入口做成 `cc use` 风格会有明显冲突风险。
105+
- `add` 的编辑器选择优先尊重 `$VISUAL`,其次 `$EDITOR`,再使用平台默认编辑器;若都不可用,需要报出可行动错误。
106+
- `run` 的核心是临时会话级覆盖,不是切换用户 settings 文件本身;这和官方 precedence 一致,也最接近主流工具的实际做法。
107+
108+
## Naming Options
109+
110+
- `claude-profile ...`:更像真正的 CLI 子命令,直观且冲突少,但名字更长。
111+
- `claude profile ...`:最像自然语言风格,不过会更像是在包装现有 `claude` CLI,且需要确认不会与官方命令空间冲突。
112+
- 短别名可作为后续附加入口,但不应成为主入口,以免再次靠近系统 `cc` 命令。
113+
- `add` 的编辑器弹出流程应保持一次命令完成,不需要再手动跑第二个命令。
114+
- 独立文件比继续扩充 `ai.sh` 更容易维护,也更容易给 `deploy.sh` / 文档一个清晰职责边界。
115+
116+
## Trade-offs
117+
118+
- 同时支持 wrapper 与项目 settings 会增加一点实现量,但能覆盖“没项目随手开 Claude”和“项目里持久绑定 profile”两个真实场景。
119+
- 只做 Bash/Zsh 版本会放弃 PowerShell 一致性,但实现更轻,桌面账号级切换可以复用 `ccswitch`
120+
- Bash/Zsh 版依赖 `jq` 更安全,仓库现有 shell helper 已有类似依赖和安装提示。
121+
- 不写全局 `~/.claude/settings.json` 会让全局默认不随 profile 变化,但避免破坏仓库既有“全局 settings 是生成产物”的约定。
122+
- 使用 JSON 处理工具能降低损坏 settings 的风险;若只依赖 shell 字符串拼接,短期快但后续维护成本高。
123+
124+
## Rollback
125+
126+
`claude-profile use` 写入前应保留临时文件写回策略。若更新失败,不覆盖原 `.claude/settings.local.json`。用户可手动删除项目 `.claude/settings.local.json` 中 profile 写入的 `env` 键恢复。
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Claude Code 多 key 切换工具实现计划
2+
3+
## Checklist
4+
5+
- [x] 阅读 `shell/shared.d/ai.sh``shell/deploy.sh` 与相关规范,确认函数风格和加载时机。
6+
- [x] 新增 `shell/shared.d/claude-profile.sh`,承载 Bash/Zsh 版 `claude-profile` 入口与 `use/run/current/list/add` 子命令。
7+
- [x] `claude-profile run` 读取 profile env 后直接启动 `claude "$@"`,不写项目 settings。
8+
- [x] `claude-profile run` 调用 `claude` 时通过会话级覆盖让 profile env 优先生效,同时不破坏 user/project/local 的其他配置。
9+
- [x] `claude-profile add` 创建 profile 模板,并优先通过 `$VISUAL` / `$EDITOR` 打开。
10+
- [x] 使用 `jq` 合并 profile 与 `.claude/settings.local.json`;如缺失则给出明确安装提示。
11+
- [x] 写入时创建 `.claude/`,通过临时文件 + rename 降低 settings 损坏风险。
12+
- [x] 输出时对 API key 脱敏。
13+
- [x] 更新相关文档或注释,说明 profile 文件示例与 VS Code Reload Window 使用方式。
14+
- [x] 添加 profile 模板创建与编辑器打开的验证场景。
15+
- [x] 确认 `shell/deploy.sh` 可继续加载新文件而无需特殊逻辑。
16+
- [x] 添加 `run` 在存在 `~/.claude/settings.json` 且包含 env 时仍能让 profile 覆盖同名键的验证场景。
17+
- [x] 运行根目录 `pnpm qa`
18+
19+
## Validation Commands
20+
21+
```bash
22+
pnpm qa
23+
```
24+
25+
手工验证建议:
26+
27+
```bash
28+
tmpdir=$(mktemp -d)
29+
cd "$tmpdir"
30+
mkdir -p ~/.claude/profiles
31+
claude-profile use glm
32+
claude-profile current
33+
cat .claude/settings.local.json
34+
claude-profile run glm --version
35+
claude-profile run official --version
36+
```
37+
38+
## Risky Files
39+
40+
- `shell/shared.d/ai.sh`:会被 Bash/Zsh 交互 shell source,语法错误会影响 shell 启动体验。
41+
- `shell/shared.d/claude-profile.sh`:会被 Bash/Zsh 交互 shell source,语法错误会影响 shell 启动体验。
42+
- 项目 `.claude/settings.local.json`:可能包含 secrets,工具输出和测试说明不得泄露真实 key。
43+
44+
## Review Gate Before Start
45+
46+
已确认:主入口采用 `claude-profile use/run/current/list/add` 子命令风格。
47+
`add` 作为创建入口会生成模板并立即打开编辑器。
48+
首版只做 Bash/Zsh 版本;PowerShell / 桌面账号级切换不做。
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Claude Code 多 key 切换工具
2+
3+
## Goal
4+
5+
在 Bash/Zsh 通用 shell 片段 `shell/shared.d/claude-profile.sh` 中提供 Claude Code profile 切换工具,让终端环境可以用一套轻量命令选择不同的 Anthropic API key、base URL、模型与 Claude Code 相关环境配置。PowerShell / 桌面账号级切换不纳入首版,桌面场景可继续使用 `ccswitch`
6+
7+
用户价值:
8+
9+
- 不需要在不同项目之间手动复制 `ANTHROPIC_API_KEY``ANTHROPIC_BASE_URL` 和模型变量。
10+
- 切换后可以通过 `claude-profile current` 明确看到当前项目使用的 profile。
11+
- 没有项目目录时,也可以用 `claude-profile run glm` 这类命令直接启动 Claude Code。
12+
- 在 Bash/Zsh 中使用一致的命令心智模型。
13+
- 需要项目持久化时,仍可写入项目 `.claude/settings.local.json`,但桌面账号级切换交给 `ccswitch`
14+
15+
## Confirmed Facts
16+
17+
- `shell/shared.d/ai.sh` 已存在,但它当前只承载通知/铃声类轻量 helper。
18+
- 新的 Claude profile 命令更适合独立文件,而不是继续堆进 `ai.sh`
19+
- `shell/deploy.sh` 会把 `shell/shared.d/*.sh` symlink 到 `~/.bashrc.d/`,并让 Bash/Zsh rc loader 加载这些片段。
20+
- 用户已将首版收敛为只做 shell 版本;PowerShell profile 与桌面账号级切换不纳入本任务。
21+
- 仓库已有 Claude 配置分层:`ai/coding/claude/config/settings.json` 是可提交共享模板,`ai/coding/claude/config/settings.local.json` 是本机私有覆盖,`~/.claude/settings.json` 是生成产物。
22+
- 仓库既有 Claude 文档明确要求不要直接手改 `~/.claude/settings.json` 作为长期入口。
23+
- Context7 查询到的 Claude Code 文档确认:
24+
- `ANTHROPIC_API_KEY` 可作为环境变量使用,并会覆盖订阅登录。
25+
- `ANTHROPIC_BASE_URL` 是官方支持的网关 / proxy 入口。
26+
- Claude Code settings 的 `env` 可承载环境变量。
27+
- `--settings <file-or-json>` 是单次会话级覆盖,优先级高于 user/project/local,且可只覆盖指定键。
28+
- VS Code Claude 扩展会共享 Claude Code settings;项目级 settings 也是 Claude Code 支持的配置来源。
29+
- VS Code 侧还会读取 `~/.claude/settings.json`,并能通过 `CLAUDE_CONFIG_DIR` 迁移全局配置目录。
30+
- 因此,单纯在当前 shell 里 `export` 变量只对当前终端进程树可靠,不是最兼容 VS Code 插件的方式。
31+
- 将 profile 写入项目 `.claude/settings.local.json` 或项目 `.claude/settings.json``env`,更符合“每个项目选择 provider/profile”的目标;其中 `settings.local.json` 更适合 secrets 与本机差异。
32+
- `claude-profile run` 更适合走会话级 `--settings` 覆盖或等价临时注入方式,把 profile 的 `env` 叠到当前启动参数里,同时保留 user/project/local 的其他配置。
33+
34+
## Requirements
35+
36+
- `shell/shared.d/claude-profile.sh` 承载 Bash/Zsh 版本的 profile 命令族,`shell/shared.d/ai.sh` 保持原有轻量 helper。
37+
- 提供 `claude-profile use <profile>` 命令,从 `~/.claude/profiles/<profile>.json` 读取 profile。
38+
- profile 文件采用 JSON,首版至少支持顶层 `env` 对象,用于写入 Claude Code 所需环境变量。
39+
- `claude-profile use` 默认作用于当前工作目录所在项目,写入项目级 `.claude/settings.local.json`,避免把 secrets 写入可提交的 `.claude/settings.json`
40+
- `claude-profile use` 写入时必须创建 `.claude/` 目录,并保留已有 `.claude/settings.local.json` 中非 profile 管理字段。
41+
- `claude-profile use` 写入时必须用 JSON 合并,而不是脆弱的字符串拼接。
42+
- 提供 `claude-profile run <profile> [claude args...]`,使用指定 profile 的 env 直接启动 `claude`,不要求当前目录是项目,也不写项目 settings。
43+
- `claude-profile run` 调用 `claude` 时应优先使用会话级 `--settings` 覆盖或等价临时注入方式,避免误伤用户已有 settings。
44+
- 为常用 profile 提供一条命令启动的快捷入口,例如 `claude-profile run glm [claude args...]`
45+
- 提供 `claude-profile current` 命令,显示当前项目选择的 profile,以及生效的关键 env 键;输出不得泄露完整 API key。
46+
- 提供 `claude-profile list` 或等价能力,列出 `~/.claude/profiles/*.json` 中可用 profile。
47+
- 提供 `claude-profile add <profile>`,创建新的 profile 模板并立即打开编辑器。
48+
- 提供清晰错误提示:profile 不存在、profile JSON 非法、缺少 `env`、当前目录不可写。
49+
- 函数需要兼容 Bash/Zsh source,不依赖仅 Bash 可用的复杂语法,除非已有片段约定允许。
50+
- 不把 profile JSON 或实际 key 写入仓库。
51+
52+
## Acceptance Criteria
53+
54+
- [ ] 在任意项目目录执行 `claude-profile use glm` 后,会生成或更新 `.claude/settings.local.json`,其中包含 profile 的 `env` 配置。
55+
- [ ] 在任意非项目目录执行 `claude-profile run glm` 时,会使用 `glm` profile 的 env 启动 `claude`,且不会创建项目 `.claude/`
56+
- [ ] `claude-profile run` 在保留 user settings 的前提下,仍能让 profile 的 `env` 优先生效。
57+
- [ ] `shell/shared.d/ai.sh` 仍只包含轻量 helper,不承载 profile 命令族。
58+
- [ ] `shell/shared.d/claude-profile.sh` 可以被 `shell/deploy.sh` 正确加载到 Bash/Zsh。
59+
- [ ] 已有 `.claude/settings.local.json` 的其他顶层配置不会被删除。
60+
- [ ] `claude-profile current` 能显示当前 profile 名称和关键 provider/model 信息,并对 key 做脱敏。
61+
- [ ] VS Code Claude 插件在项目中 Reload Window 后可通过 Claude Code settings 读取同一份项目配置。
62+
- [ ] `claude-profile list` 能列出 `~/.claude/profiles/` 下的 profile。
63+
- [ ] `claude-profile add glm` 会生成 profile 模板并打开编辑器,方便直接填写 `env`
64+
- [ ] 非法 profile 会失败且不破坏原有 `.claude/settings.local.json`
65+
- [ ] 根目录 `pnpm qa` 通过;如 QA 暴露与本改动相关问题,需要修复。
66+
67+
## Out of Scope
68+
69+
- 首版不开发交互式密钥录入或密钥加密存储。
70+
- 首版不修改 `ai/coding/claude/Sync-ClaudeConfig.ps1` 的全局配置生成流程。
71+
- 首版不把 Claude profile 命令继续塞进 `shell/shared.d/ai.sh`
72+
- 首版不直接写 `~/.claude/settings.json`
73+
- 首版不开发 PowerShell profile 版本。
74+
- 首版不实现桌面账号级切换;该场景优先使用 `ccswitch`
75+
- 首版不自动启动、重载或控制 VS Code。
76+
77+
## Options Considered
78+
79+
- 当前 shell `export`:实现最简单,适合只在终端临时运行 `claude`;缺点是 VS Code 插件通常不是该 shell 的子进程,兼容性弱。
80+
- 全局 `~/.claude/settings.json` 切换:CLI 与 VS Code 都容易读到;缺点是跨项目会互相覆盖,且仓库已有约定把它视为生成产物,不推荐手改。
81+
- 项目 `.claude/settings.json`:项目内稳定可共享;缺点是容易把 secrets 提交,除非只放非敏感 provider 名称。
82+
- 项目 `.claude/settings.local.json`:最适合 per-project + secrets + VS Code 兼容目标;缺点是需要确保 Claude Code / 插件加载 local settings,并且文件通常不提交,换机器需要重新设置。
83+
- profile 目录 + symlink:很多现成工具会把不同配置拆成独立目录,再把 `~/.claude``~/.claude/settings.json` 指向当前 profile;优点是切换简单,缺点是更偏全局,不够项目化。
84+
- 包装命令 `cc <profile>`:不会落盘 secrets,适合一次性终端运行;缺点是无法自然影响 VS Code 插件。
85+
86+
## Recommendation
87+
88+
首版采用 Bash/Zsh shell 双入口:
89+
90+
- `claude-profile run glm`:日常交互默认入口,一条命令临时带 profile env 启动 `claude`,没有项目也能使用。
91+
- `claude-profile use glm`:需要让某个项目长期绑定 profile,尤其是希望 VS Code Claude 插件 Reload Window 后也读取同一 profile 时使用。
92+
93+
同一语义需要在 Bash/Zsh 中成立。这个组合比“先切换再手动启动 Claude”的两步流更贴近日常使用;也比只做 wrapper 更照顾项目持久化。
94+
`run` 使用会话级覆盖或等价临时注入方式,让 profile env 优先生效,同时保留 user/project/local 的其他配置。
95+
96+
## Resolved Decisions
97+
98+
- 主入口采用 `claude-profile use/run/current/list` 这种子命令风格。
99+
- `claude-profile add <profile>` 作为新 profile 的创建入口,生成模板后立即打开编辑器。
100+
- Bash/Zsh 版的 profile 命令族从 `ai.sh` 拆到独立文件 `claude-profile.sh`
101+
- 首版只实现 Bash/Zsh 版本;PowerShell / 桌面账号级切换不做,桌面使用 `ccswitch`
102+
103+
## Open Questions
104+
105+
- 是否额外保留短别名,例如 `cp use/run/current/list``cc-*`,作为主入口之外的快捷方式?
106+
107+
## Notes
108+
109+
- 用户示例中的 `glm``official` profile 是目标使用体验的核心参考。
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"id": "claude-code-profile-switcher",
3+
"name": "claude-code-profile-switcher",
4+
"title": "Claude Code 多 key 切换工具",
5+
"description": "",
6+
"status": "in_progress",
7+
"dev_type": null,
8+
"scope": null,
9+
"package": null,
10+
"priority": "P2",
11+
"creator": "mudssky",
12+
"assignee": "mudssky",
13+
"createdAt": "2026-05-21",
14+
"completedAt": null,
15+
"branch": null,
16+
"base_branch": "master",
17+
"worktree_path": null,
18+
"commit": null,
19+
"pr_url": null,
20+
"subtasks": [],
21+
"children": [],
22+
"parent": null,
23+
"relatedFiles": [],
24+
"notes": "",
25+
"meta": {}
26+
}

0 commit comments

Comments
 (0)