spec.md 8.5 KB

ADDED Requirements

Requirement: 二进制与子命令入口

工具 SHALL 以单个二进制 cc-switch 分发,采用子命令架构。顶层 SHALL 至少暴露以下子命令:addlist(别名 ls)、editremove(别名 rm)、useconfigtemplateshelpversion。未识别的子命令 SHALL 以非零退出码退出并建议 cc-switch --help

Scenario: 列出帮助

  • WHEN 用户执行 cc-switch --help
  • THEN 输出包含上述所有子命令的一行摘要,且以 0 退出

Scenario: 未知子命令

  • WHEN 用户执行 cc-switch foobar
  • THEN 工具以非零退出码退出,并在 stderr 提示 foobar 未知、可查看 --help

Scenario: 版本信息

  • WHEN 用户执行 cc-switch version
  • THEN 输出至少包含语义化版本号;构建时可注入 commit 与日期信息

Requirement: 裸跑入口 = 交互式 use

执行 cc-switch(无参数)SHALL 等价于 cc-switch use(无参数),即进入交互式 provider 选择流程。

Scenario: tty 裸跑

  • WHEN 用户在终端裸跑 cc-switch
  • THEN 屏幕出现带编号的 provider 列表,等待用户输入编号或名称

Requirement: 交互式 provider 选择

cc-switch use(无参)在 stdin 为 tty 时 SHALL 展示编号列表让用户选择;若存在 default_provider,在列表中 SHALL 标注为默认,空输入(直接回车)SHALL 选中默认 provider;用户可输入编号或 provider 名。输入不匹配时 SHALL 重新提示(最多 3 次),全部失败后以非零退出码退出。

Scenario: 回车选默认

  • WHEN 存在 default_provider=foo,用户执行 cc-switch use 并直接回车
  • THEN foo 被选中、进入启动流程

Scenario: 输入编号

  • WHEN 列表中编号 2 对应 bar,用户输入 2 回车
  • THEN bar 被选中、进入启动流程

Scenario: 输入名称

  • WHEN 用户输入 bar 回车
  • THEN bar 被选中、进入启动流程

Scenario: 连续非法输入

  • WHEN 用户连续 3 次输入不存在的编号或名称
  • THEN 工具非零退出、不启动 claude

Requirement: 非交互式 provider 选择

cc-switch use <name> SHALL 直接选中指定 provider 并进入启动流程,无需任何 tty 交互。未指定 <name> 且 stdin 非 tty 时 SHALL 尝试使用 default_provider;若也未设置默认,SHALL 以非零退出码退出并提示需要显式 <name>

Scenario: 直接指定

  • WHEN 用户执行 cc-switch use foofoo 已配置
  • THEN 工具立即进入清理/注入/启动流程,不做任何交互

Scenario: 指定不存在 provider

  • WHEN 用户执行 cc-switch use nonexistent
  • THEN 工具非零退出、stderr 提示 nonexistent 不存在、建议 cc-switch list

Scenario: 管道模式使用默认

  • WHEN stdin 非 tty(例如被 :</dev/null 重定向),用户执行 cc-switch use,且 default_provider=foo
  • THEN 工具不进入交互,直接使用 foo 启动

Scenario: 管道模式且无默认

  • WHEN stdin 非 tty,未设置 default_provider,用户执行 cc-switch use
  • THEN 工具非零退出,提示需要 <name> 或先设置默认 provider

Requirement: 全局 flag

工具 SHALL 支持以下全局 flag:-v / --verbose(输出 trace 日志到 stderr)、--config <path>(等价于 CC_SWITCH_CONFIG 但优先级更高)、-h / --help。这些 flag SHALL 在任意子命令中可用。

Scenario: --config 覆盖 env

  • WHEN 已设置 CC_SWITCH_CONFIG=/a.yaml,用户执行 cc-switch --config /b.yaml list
  • THEN 工具读写 /b.yaml,忽略 env 中的路径

Scenario: verbose 生效

  • WHEN 用户执行 cc-switch -v use foo
  • THEN stderr 中可见解析到的配置路径、最终 claude 路径、最终 env key 列表(value 不打印)等 trace 信息

Requirement: config 子命令

cc-switch config SHALL 提供 get / set / show 子命令:

  • config show:打印当前配置文件路径与内容(env value 脱敏为 ***
  • config set claude-path <path>:设置 claude_path
  • config set default <name>:设置 default_provider
  • config get <key>:打印单个配置项值(同样脱敏 env value)

Scenario: show

  • WHEN 用户执行 cc-switch config show
  • THEN 第一行打印配置文件绝对路径,之后打印脱敏后的 YAML

Scenario: set default

  • WHEN 用户执行 cc-switch config set default foo
  • THENfoo 存在,写入配置并打印确认;若不存在,非零退出

Requirement: Add 向导(可选交互)

cc-switch add <name> SHALL 支持三种用法:

  1. 通过 --env KEY=VALUE(可重复)与 --description "..." 一次性填入;
  2. 通过 --from-template <tpl> 以内置模板为起点,tty 下逐 key 提示输入 value(显示 hint、回车接受 default);可与 --description 共存;
  3. 不带任何 --env / --from-template 时进入交互式自由向导,逐个提示输入 key/value(输入空 key 结束),tty 下可用。

所有方式 MUST 最终落入同一份校验逻辑(至少一个 env key、provider 名合法)。--from-template--env 可以组合使用——先以模板为骨架,--env 再覆盖或补充同名/新 key。value 输入阶段 MUST 允许用户输入 env:VAR_NAME 形式的引用(见 provider-config spec 对应 Requirement),工具 MUST NOT 在 add 阶段尝试解析该引用、MUST NOT 校验该 VAR 是否存在。

Scenario: 一次性命令行

  • WHEN 用户执行 cc-switch add foo --env K1=v1 --env K2=v2 --description "desc"
  • THEN foo 被写入,env 含两个键,description=desc

Scenario: 交互式向导

  • WHEN 用户在 tty 下执行 cc-switch add foo,依次输入 K1 / v1 / K2 / v2 / 空键
  • THEN foo 被写入与一次性命令行等价的结构

Scenario: 非 tty 且无 --env 无模板

  • WHEN stdin 非 tty 用户执行 cc-switch add foo 且未提供任何 --env--from-template
  • THEN 工具非零退出,提示在非交互模式下需给出 --env KEY=VALUE--from-template

Scenario: 使用模板(tty)

  • WHEN 用户在 tty 下执行 cc-switch add official --from-template anthropic-official,依次对每个模板 key 按回车接受 default / 输入新 value
  • THEN official 被写入,env 包含模板定义的所有 key,value 为用户确认后的值

Scenario: 模板 + --env 覆盖

  • WHEN 用户执行 cc-switch add x --from-template openrouter --env ANTHROPIC_MODEL=anthropic/claude-opus-4 --non-interactive
  • THEN x 被写入,env 结构为"模板骨架 ∪ --env 覆盖",ANTHROPIC_MODEL 取自 --env;模板中未被 --env 提供的 key 保留模板 default 或占位空字符串(value 为空的 key 在落盘前 MUST 报错要求补齐)

Scenario: 引用语法直接写入

  • WHEN 用户执行 cc-switch add foo --env ANTHROPIC_API_KEY=env:MY_KEY
  • THEN 配置写入的字面 value 即为 env:MY_KEY,不在 add 阶段校验 MY_KEY 是否存在

Scenario: 未知模板名

  • WHEN 用户执行 cc-switch add foo --from-template nonexistent
  • THEN 工具非零退出,stderr 列出可用模板名并建议 cc-switch templates list

Requirement: templates 子命令

cc-switch templates SHALL 暴露两个子命令:listshow <tpl>。模板为内嵌资源,工具 MUST 不访问网络、MUST 不读取用户目录之外的文件。

Scenario: 列出模板

  • WHEN 用户执行 cc-switch templates list
  • THEN 输出每个内置模板的名称与一行 description

Scenario: 查看模板细节

  • WHEN 用户执行 cc-switch templates show openrouter
  • THEN 输出该模板的 description、env key 列表、每个 key 的 hint 与 default(若有)

Scenario: 查看不存在的模板

  • WHEN 用户执行 cc-switch templates show nonexistent
  • THEN 工具非零退出,提示不存在并建议 cc-switch templates list

Requirement: v1 内嵌模板集

二进制 SHALL 内嵌至少以下模板:anthropic-officialopenrouterdeepseekmoonshotzhipucustom-base。每个模板 MUST 至少包含一条 env key;模板 key 列表 MUST 只描述键名与 hint/default,MUST NOT 预置任何真实 token 或 API key。

Scenario: 种子模板齐全

  • WHEN 用户执行 cc-switch templates list
  • THEN 输出至少包含上述 6 个模板名