## ADDED Requirements ### Requirement: 配置文件定位与初始化 工具 SHALL 按如下优先级解析配置文件路径:`$CC_SWITCH_CONFIG` > `$XDG_CONFIG_HOME/cc-switch/config.yaml` > `~/.config/cc-switch/config.yaml`。若目标文件不存在,SHALL 在首次需要写入时创建父目录(权限 `0700`)与空骨架文件(权限 `0600`)。读操作遇文件不存在 MUST 返回"空配置"而非错误。 #### Scenario: 默认路径首次读 - **WHEN** 用户未设置 `CC_SWITCH_CONFIG`、`XDG_CONFIG_HOME`,也从未运行过 `cc-switch` - **THEN** `cc-switch list` 成功执行,输出 "(no providers configured)" 提示,且不在磁盘创建任何文件 #### Scenario: 默认路径首次写 - **WHEN** 用户执行 `cc-switch add foo ...` 且配置文件从未被创建 - **THEN** 工具创建 `~/.config/cc-switch/`(权限 `0700`)与 `config.yaml`(权限 `0600`),并写入新增 provider #### Scenario: XDG 路径覆盖 - **WHEN** 环境变量 `XDG_CONFIG_HOME=/tmp/xdg` 已设置 - **THEN** 工具使用 `/tmp/xdg/cc-switch/config.yaml` 作为配置文件路径 #### Scenario: 显式路径覆盖 - **WHEN** 环境变量 `CC_SWITCH_CONFIG=/custom/path.yaml` 已设置 - **THEN** 无论 XDG 如何设置,工具均使用 `/custom/path.yaml` #### Scenario: 宽松权限 warning - **WHEN** 配置文件存在且权限为 `0644` - **THEN** 工具继续执行命令,并向 stderr 打印一条 warning 提示权限过宽、建议 `chmod 600` ### Requirement: 配置 schema 配置文件 SHALL 使用 YAML 编码,顶层字段包括:`claude_path`(string,可选)、`default_provider`(string,可选)、`providers`(map,key 为 provider 名)。每个 provider SHALL 包含 `env`(map[string]string,必填且至少一个键)与可选 `description`(string)。Provider 名 SHALL 为非空且仅含字母、数字、`-`、`_`。 #### Scenario: 合法配置解析 - **WHEN** 配置文件内容合法,包含两个 provider 及 `default_provider: foo` - **THEN** 工具解析成功并在后续命令中识别这两个 provider #### Scenario: 非法 provider 名 - **WHEN** 配置文件中 provider 名包含空格或非法字符 - **THEN** 工具返回非零退出码并指出哪一个 provider 名不合法 #### Scenario: 空 env map - **WHEN** 某个 provider 的 `env` 字段为空 map - **THEN** 工具返回非零退出码并提示该 provider 至少需要一个环境变量 ### Requirement: Env 值的间接引用语法 Provider 的 `env` map 中,value 为字符串类型;若 value 严格匹配正则 `^env:[A-Za-z_][A-Za-z0-9_]*$`,SHALL 被识别为"间接引用",表示"启动时从父进程环境中读取名为 `VAR_NAME` 的变量的值作为实际 value"。所有其他 value MUST 被视为字面字符串,不做任何展开。引用仅在"启动阶段"解析(见 provider-launch spec),在 `list -v` / `config show` / `config get` 等展示命令中,引用 value 按原字符串展示(不脱敏、不展开),让用户一眼识别"这一项是指针"。 #### Scenario: 字面 value 保留 - **WHEN** provider 的 `env.FOO` 值为 `hello` - **THEN** 工具视为字面 `hello`;`list -v` 显示脱敏后的 `he***` 或等价样式 #### Scenario: env 引用被识别 - **WHEN** provider 的 `env.ANTHROPIC_API_KEY` 值为 `env:MY_ANTHROPIC_KEY` - **THEN** 工具视为对父环境变量 `MY_ANTHROPIC_KEY` 的引用;`list -v` 显示 `env:MY_ANTHROPIC_KEY`(按原字符串,不脱敏、不解析) #### Scenario: 非法引用形式回退为字面 - **WHEN** provider 的 `env.FOO` 值为 `env:` 或 `env:1NAME`(不符合正则) - **THEN** 工具视为普通字面字符串,不报错也不解析 ### Requirement: 原子写入 工具修改配置文件时 SHALL 采用"写临时文件 → fsync → 重命名"模式;MUST 不允许部分写入后崩溃导致的文件损坏。 #### Scenario: 写入中途崩溃 - **WHEN** 在 `cc-switch add` 写入 `config.yaml` 过程中进程被强制终止 - **THEN** `config.yaml` 要么保留原内容、要么是完整的新内容,不会出现截断或空文件 ### Requirement: Provider 增删改查 工具 SHALL 支持以下 provider 管理操作: - **add**:新增 provider,禁止与已有同名冲突 - **list**:列出所有 provider,标记 `default`,可选 `-v` 显示 env 键名(value 脱敏) - **edit**:修改已有 provider 的 env 或 description - **remove**:按名称删除 provider;若删除的是当前 `default_provider`,`default_provider` 字段 SHALL 被一并清空 - **use-default** 支持(见 provider-launch 与 cli 能力) 所有写操作 SHALL 在修改成功后原子落盘。 #### Scenario: 新增 provider - **WHEN** 用户执行 `cc-switch add official --env ANTHROPIC_API_KEY=sk-xxx --env ANTHROPIC_BASE_URL=https://api.anthropic.com` - **THEN** `providers.official.env` 写入两个键,`cc-switch list` 能列出 `official` #### Scenario: 重名 add - **WHEN** 已存在名为 `official` 的 provider,用户再次 `cc-switch add official ...` - **THEN** 工具返回非零退出码并提示使用 `edit` 或换一个名字 #### Scenario: list 脱敏 - **WHEN** 用户执行 `cc-switch list -v` - **THEN** 输出中对每个 env 值展示为 `***` 或前 4 位 + `***`,不打印完整 value #### Scenario: 删除默认 provider - **WHEN** 当前 `default_provider=foo`,用户执行 `cc-switch remove foo` - **THEN** `foo` 被删除,`default_provider` 被置空,`cc-switch list` 不再展示 default 标记 #### Scenario: 删除不存在 provider - **WHEN** 用户执行 `cc-switch remove nonexistent` - **THEN** 工具返回非零退出码并提示 provider 不存在 ### Requirement: 全局 claude 可执行文件路径 工具 SHALL 支持通过 `cc-switch config set claude-path ` 命令设置 `claude_path`;支持 `~` 展开;SHALL 在设置时校验路径存在且可执行,不通过则拒绝写入。未设置时后续启动流程回落到 `exec.LookPath("claude")`。 #### Scenario: 设置有效路径 - **WHEN** 用户执行 `cc-switch config set claude-path ~/.claude/local/claude` 且该文件存在且可执行 - **THEN** `claude_path` 被写入展开后的绝对路径 #### Scenario: 设置无效路径 - **WHEN** 用户执行 `cc-switch config set claude-path /not/exist` - **THEN** 工具返回非零退出码且不修改配置文件 ### Requirement: 默认 provider 工具 SHALL 支持通过 `cc-switch config set default ` 设置默认 provider,前提是该 provider 存在。`cc-switch use` 在未指定 `` 且处于非 tty 环境时 SHALL 使用默认 provider;tty 下仍进入交互选择(默认值为 `default_provider`)。 #### Scenario: 设置默认 - **WHEN** 用户已有 `foo` provider,执行 `cc-switch config set default foo` - **THEN** `default_provider: foo` 被写入配置 #### Scenario: 设置不存在 provider 为默认 - **WHEN** 用户执行 `cc-switch config set default bar` 且 `bar` 不存在 - **THEN** 工具返回非零退出码且不修改配置