go mod init github.com/kotoyuuko/cc-switch-cli,设 go 1.22+github.com/spf13/cobra、gopkg.in/yaml.v3cmd/cc-switch/main.go、internal/config/、internal/provider/、internal/runner/、internal/cli/cmd/cc-switch/main.go 写空的 cobra root command,可运行 go run ./cmd/cc-switch --helpMakefile 或 justfile:build、test、vet、fmt 常用 target.gitignore(忽略二进制、dist/、本地 config.yaml)Config / Provider 结构体,字段与 specs/provider-config/spec.md 一致(claude_path、default_provider、providers、env、description)CC_SWITCH_CONFIG > XDG_CONFIG_HOME > ~/.config/cc-switch/config.yaml;支持 ~ 展开Load():文件不存在返回空 Config,存在则 yaml 反序列化;对权限 > 0600 的文件打印 warningSave():写 config.yaml.tmp → fsync → os.Rename;父目录缺失则以 0700 创建;新建文件以 0600 权限^[A-Za-z0-9_-]+$)与 env 非空校验AddProvider、RemoveProvider、UpdateProvider、ListProviders、SetDefault、SetClaudePath(后者校验路径存在且可执行)IsEnvRef(s string) (varName string, ok bool) 基于正则 ^env:[A-Za-z_][A-Za-z0-9_]*$;展示层(list -v、config show)遇到引用 value 原样输出、不脱敏IsEnvRef 的正反例(包括 env:、env:1x、env:FOO 等)UnionEnvKeys(providers) []string:遍历所有 provider 的 env key 去重ResolveEnvRefs(selected Provider, parentSnapshot map[string]string) (resolved map[string]string, err error):对 selected.env 中匹配 env:VAR 的 value 逐项从 parentSnapshot 查找;缺失则返回 error 指出哪个 key 引用哪个 VAR 缺失;只解析一次不做链式BuildChildEnv(parent []string, union []string, resolved map[string]string) []string:从 parent 剔除 union 中的 key,再 append resolved 的 key=value(resolved 的值覆盖同名)ResolveEnvRefs 正常解析、VAR 缺失报错、被引用 VAR 恰好在并集中(快照先于清理)、不做链式解析ResolveClaudePath(cfg Config) (string, error):优先 claude_path(带 ~ 展开 + 可执行位校验),否则 exec.LookPath("claude");都失败则返回带引导信息的错误Run(ctx, claudePath string, childEnv []string, args []string) (exitCode int, err error):
cmd.Env = childEnv;cmd.Stdin/Stdout/Stderr = os.Std*os/signal.Notify 捕获 SIGINT/SIGTERM/SIGHUP 并 cmd.Process.Signal(sig) 转发cmd.Wait() 后返回 ExitCode();信号终止时返回 128 + signumexec.Command("/bin/sh", "-c", "echo $FOO") 作为替身验证 env 注入;用 short-lived 子进程验证 exit code 透传;用 sleep + SIGINT 验证信号转发runner_unix.go 中实现信号转发;预留 runner_other.go 返回"unsupported platform"错误(为 Windows/Plan9 兜底)internal/cli/root.go 定义 root cmd 与全局 flag:-v/--verbose、--configroot.go 的 PersistentPreRunE 中加载配置(若文件不存在则持有空配置)cc-switch add:flag 模式 --env KEY=VALUE(可重复)、--description、--from-template <tpl>、--non-interactive;
--env 也无 --from-template 时报错--env/--from-template 时进入自由向导(空 key 结束)--from-template 带出模板 key 骨架,tty 下逐 key 提示 value(显示 hint、回车接受 default、允许 env:VAR 引用),--non-interactive + 模板时为占位空字符串--env 覆盖/追加cc-switch list / ls:普通模式只列名称 + default 标记;-v 列出 env key 与脱敏 value(*** 或首 4 字符 + ***)cc-switch edit <name>:支持 --env、--description、--remove-env KEY 等 flagcc-switch remove <name> / rm <name>:删除后若等于 default_provider 则清空该字段cc-switch config show|set|get:set claude-path、set default、get <key>、show(脱敏)cc-switch use [name]:
default_provider;无 default 则报错os.Environ() 快照 → ResolveEnvRefs(缺失即退出、不启动)→ UnionEnvKeys → BuildChildEnv → runner 启动 → 等待 → 退出码透传cc-switch(root 裸跑)→ 转发到 use 子命令cc-switch version:注入构建时的 version/commit/date(通过 ldflags)verbose 时在 stderr 打印 trace:解析到的 config 路径、claude 路径、并集 key、选中 provider、cleanup completecc-switch templates list / templates show <tpl>,只读内嵌模板数据cobra 的 SetArgs + SetOut/Err 捕获):add/list/remove/use/templates 的各 scenario,包含 --from-template、env:VAR 写入、引用解析失败的 use 路径***;否则前 4 字符 + ***env:VAR)原样输出不脱敏Template{Name, Description string; Env []TemplateEnvKey{Name, Hint, Default string}}internal/templates/templates.yaml(go:embed),填充 v1 种子模板:anthropic-official、openrouter、deepseek、moonshot、zhipu、custom-base,只填键名 + hint + 可选 default(官方 base URL 等),绝不预置 tokenLoad() []Template、Get(name string) (Template, bool)、List() []TemplateGet 正反例、yaml 解析错误路径go test 启动 cc-switch,用一个简短 shell 脚本充当"fake claude"(打印所有 ANTHROPIC_* 变量后退出),验证:
ANTHROPIC_API_KEY=old、provider 未设置该 key,fake claude 输出中不包含该 keyANTHROPIC_API_KEY=new,fake claude 输出中值为 new$HOME 时,fake claude 输出字面 $HOMEexit 42,cc-switch 也 42exit 130,cc-switch 收 SIGINT 后也 130 退出(覆盖于 internal/runner 单测)stdin</dev/null 运行 cc-switch use,无 default 时报错;有 default 时正确启动MY_KEY=sk-ok,provider env.ANTHROPIC_API_KEY=env:MY_KEY,fake claude 收到 ANTHROPIC_API_KEY=sk-okMY_KEY,cc-switch use 非零退出,fake claude 未被启动(可通过 claude 端写入一个 sentinel 文件检测"没执行")env.ANTHROPIC_API_KEY=env:ANTHROPIC_API_KEY,父 shell 设该 var,fake claude 收到父 shell 的值(验证先快照后清理)cc-switch add x --from-template openrouter --non-interactive --env ANTHROPIC_AUTH_TOKEN=sk-test --env ANTHROPIC_MODEL=anthropic/claude-opus-4(其他 key 由模板带出)能成功落盘;cc-switch templates list 输出含全部种子模板README.md:安装、快速上手(add → use)、配置文件示例(Anthropic 官方 / OpenRouter / 其他兼容层 snippet)、env:VAR 引用用法(配合 1Password CLI / direnv 的示例)、--from-template 用法.goreleaser.yaml):darwin/linux 的 amd64+arm64 二进制;SHA256 校验;可选 Homebrew tapvet + test + build(PR & main)v0.1.0,触发发布流水线(验收通过后) — 留给用户examples/config.example.yaml,含两三个 provider 示例(占位 key + 至少一条 env:VAR 引用示例)env:VAR 链式解析、不做字面 env: 转义