30d7650bae
Adds first-class keyring management on the dispatcher CLI and wires the TUI to read its DeepSeek key through the same Secrets façade. Subcommands: * `auth set --provider <name>` writes to the OS keyring; prompts on stdin without echo, never prints the key, never touches `config.toml`. Supports `--api-key` and `--api-key-stdin`. * `auth get --provider <name>` reports `set` / `not set` plus the resolving layer (keyring / env / config-file). Never prints the value. * `auth clear --provider <name>` deletes from keyring and from any legacy plaintext slot in `config.toml` for parity. * `auth list` table of all known providers and whether each layer holds a key. Non-revealing. * `auth migrate [--dry-run]` reads `api_key` (root + per-provider blocks) from `config.toml`, writes them to the keyring, then strips the entries from disk. Idempotent. * `auth status` expanded to also report the active keyring backend and per-provider keyring state. `doctor` now prints `keyring backend: ...` plus per-provider `keyring=yes/no, env=yes/no` lines and points users at `deepseek auth set` when no key resolves. `Config::deepseek_api_key()` in the TUI is rewritten to consult `Secrets::auto_detect()` first (keyring -> env), then fall back to the existing TOML slots with a deprecation warning. Error messages now lead with `deepseek auth set --provider <name>`. 5 new unit tests cover argument parsing for the new subcommands and end-to-end auth set/clear/migrate behaviour against an `InMemoryKeyringStore`, verifying that no plaintext key ever lands in `config.toml`. Verified manually on macOS: $ deepseek auth set --provider deepseek --api-key-stdin $ security find-generic-password -s deepseek -a deepseek # entry present $ deepseek auth migrate # api_key lines stripped from ~/.deepseek/config.toml Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>