diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a6490b0..e3f3033c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: - name: Lockfile drift guard run: git diff --exit-code -- Cargo.lock - name: Run Offline Eval Harness - run: cargo run -p deepseek-tui --all-features -- eval + run: cargo run -p codewhale-tui --all-features -- eval npm-wrapper-smoke: name: npm wrapper smoke @@ -120,7 +120,7 @@ jobs: with: cache-bin: false - name: Build wrapper binaries - run: cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui + run: cargo build --release --locked -p codewhale-cli -p codewhale-tui - name: Smoke wrapper install and delegated entrypoints run: node scripts/release/npm-wrapper-smoke.js diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b43d10d6..53fcd34a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -24,46 +24,48 @@ jobs: fail-fast: false matrix: include: + # --- codewhale (cli dispatcher, canonical) --- - os: ubuntu-latest target: x86_64-unknown-linux-gnu - binary: deepseek - artifact_name: deepseek-linux-x64 + binary: codewhale + artifact_name: codewhale-linux-x64 - os: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu - binary: deepseek - artifact_name: deepseek-linux-arm64 + binary: codewhale + artifact_name: codewhale-linux-arm64 - os: macos-latest target: x86_64-apple-darwin - binary: deepseek - artifact_name: deepseek-macos-x64 + binary: codewhale + artifact_name: codewhale-macos-x64 - os: macos-latest target: aarch64-apple-darwin - binary: deepseek - artifact_name: deepseek-macos-arm64 + binary: codewhale + artifact_name: codewhale-macos-arm64 - os: windows-latest target: x86_64-pc-windows-msvc - binary: deepseek.exe - artifact_name: deepseek-windows-x64.exe + binary: codewhale.exe + artifact_name: codewhale-windows-x64.exe + # --- codewhale-tui (TUI runtime, canonical) --- - os: ubuntu-latest target: x86_64-unknown-linux-gnu - binary: deepseek-tui - artifact_name: deepseek-tui-linux-x64 + binary: codewhale-tui + artifact_name: codewhale-tui-linux-x64 - os: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu - binary: deepseek-tui - artifact_name: deepseek-tui-linux-arm64 + binary: codewhale-tui + artifact_name: codewhale-tui-linux-arm64 - os: macos-latest target: x86_64-apple-darwin - binary: deepseek-tui - artifact_name: deepseek-tui-macos-x64 + binary: codewhale-tui + artifact_name: codewhale-tui-macos-x64 - os: macos-latest target: aarch64-apple-darwin - binary: deepseek-tui - artifact_name: deepseek-tui-macos-arm64 + binary: codewhale-tui + artifact_name: codewhale-tui-macos-arm64 - os: windows-latest target: x86_64-pc-windows-msvc - binary: deepseek-tui.exe - artifact_name: deepseek-tui-windows-x64.exe + binary: codewhale-tui.exe + artifact_name: codewhale-tui-windows-x64.exe runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43810df4..6b637bb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,11 +47,11 @@ jobs: - name: Workspace tests run: cargo test --workspace --all-features --locked - name: TUI snapshot parity - run: cargo test -p deepseek-tui-core --test snapshot --locked + run: cargo test -p codewhale-tui-core --test snapshot --locked - name: Protocol schema parity - run: cargo test -p deepseek-protocol --test parity_protocol --locked + run: cargo test -p codewhale-protocol --test parity_protocol --locked - name: State persistence parity - run: cargo test -p deepseek-state --test parity_state --locked + run: cargo test -p codewhale-state --test parity_state --locked - name: Lockfile drift guard run: git diff --exit-code -- Cargo.lock @@ -100,7 +100,49 @@ jobs: strategy: matrix: include: - # --- deepseek (cli) --- + # --- codewhale (cli dispatcher, canonical) --- + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + binary: codewhale + artifact_name: codewhale-linux-x64 + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + binary: codewhale + artifact_name: codewhale-linux-arm64 + - os: macos-latest + target: x86_64-apple-darwin + binary: codewhale + artifact_name: codewhale-macos-x64 + - os: macos-latest + target: aarch64-apple-darwin + binary: codewhale + artifact_name: codewhale-macos-arm64 + - os: windows-latest + target: x86_64-pc-windows-msvc + binary: codewhale.exe + artifact_name: codewhale-windows-x64.exe + # --- codewhale-tui (TUI runtime, canonical) --- + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + binary: codewhale-tui + artifact_name: codewhale-tui-linux-x64 + - os: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + binary: codewhale-tui + artifact_name: codewhale-tui-linux-arm64 + - os: macos-latest + target: x86_64-apple-darwin + binary: codewhale-tui + artifact_name: codewhale-tui-macos-x64 + - os: macos-latest + target: aarch64-apple-darwin + binary: codewhale-tui + artifact_name: codewhale-tui-macos-arm64 + - os: windows-latest + target: x86_64-pc-windows-msvc + binary: codewhale-tui.exe + artifact_name: codewhale-tui-windows-x64.exe + # --- deepseek (legacy dispatcher shim; removed in v0.9.0) --- - os: ubuntu-latest target: x86_64-unknown-linux-gnu binary: deepseek @@ -121,7 +163,7 @@ jobs: target: x86_64-pc-windows-msvc binary: deepseek.exe artifact_name: deepseek-windows-x64.exe - # --- deepseek-tui (TUI) --- + # --- deepseek-tui (legacy TUI shim; removed in v0.9.0) --- - os: ubuntu-latest target: x86_64-unknown-linux-gnu binary: deepseek-tui @@ -253,20 +295,27 @@ jobs: - uses: actions/download-artifact@v4 with: path: artifacts - pattern: deepseek* + # Match both the canonical `codewhale*` artifacts and the legacy + # `deepseek*` shim artifacts that ship for the transition release. + pattern: '*' - name: List artifacts run: find artifacts -type f - name: Generate checksum manifest shell: bash run: | mkdir -p artifacts/checksums - manifest="artifacts/checksums/deepseek-artifacts-sha256.txt" + # Canonical manifest used by codewhale's `codewhale update` flow. + manifest="artifacts/checksums/codewhale-artifacts-sha256.txt" : > "${manifest}" while IFS= read -r -d '' file; do hash="$(sha256sum "${file}" | awk '{print $1}')" base="$(basename "${file}")" printf '%s %s\n' "${hash}" "${base}" >> "${manifest}" done < <(find artifacts -type f ! -path 'artifacts/checksums/*' -print0 | sort -z) + # Legacy alias manifest so v0.8.40 `deepseek update` clients can + # still find a manifest by their hardcoded name. Same content; will + # be removed once the legacy shim binaries are retired in v0.9.0. + cp "${manifest}" "artifacts/checksums/deepseek-artifacts-sha256.txt" cat "${manifest}" - uses: softprops/action-gh-release@v1 with: @@ -274,12 +323,19 @@ jobs: files: artifacts/*/* prerelease: false body: | + > This release renames the project to **codewhale**. The legacy + > `deepseek` and `deepseek-tui` binaries continue to ship as + > deprecation shims for one release cycle; they print a one-line + > warning and forward to `codewhale` / `codewhale-tui`. They will + > be removed in v0.9.0. See `docs/REBRAND.md` for the full + > migration story. + ## Install ### Recommended — npm (one command, both binaries) ```bash - npm install -g deepseek-tui + npm install -g codewhale ``` The wrapper downloads both binaries from this Release and places them in the same directory. @@ -293,15 +349,15 @@ jobs: ghcr.io/hmbown/deepseek-tui:${{ needs.resolve.outputs.tag }} ``` - The image ships the `deepseek` dispatcher and `deepseek-tui` runtime. The `latest` tag is also updated on release. + The image ships the `codewhale` dispatcher and `codewhale-tui` runtime (plus the legacy `deepseek` / `deepseek-tui` shims during the transition). The `latest` tag is also updated on release. ### Cargo (Linux / macOS) ```bash - cargo install deepseek-tui-cli deepseek-tui --locked + cargo install codewhale-cli codewhale-tui --locked ``` - Both crates are required — `deepseek-tui-cli` produces the `deepseek` dispatcher and `deepseek-tui` produces the interactive runtime that the dispatcher delegates to. Installing only one binary will fail at runtime with a `MISSING_COMPANION_BINARY` error. + Both crates are required — `codewhale-cli` produces the `codewhale` dispatcher and `codewhale-tui` produces the interactive runtime that the dispatcher delegates to. Installing only one binary will fail at runtime with a `MISSING_COMPANION_BINARY` error. ### Manual download @@ -309,26 +365,30 @@ jobs: | Platform | Dispatcher | TUI runtime | |---|---|---| - | Linux x64 | `deepseek-linux-x64` | `deepseek-tui-linux-x64` | - | Linux ARM64 | `deepseek-linux-arm64` | `deepseek-tui-linux-arm64` | - | macOS x64 | `deepseek-macos-x64` | `deepseek-tui-macos-x64` | - | macOS ARM | `deepseek-macos-arm64` | `deepseek-tui-macos-arm64` | - | Windows x64 | `deepseek-windows-x64.exe` | `deepseek-tui-windows-x64.exe` | + | Linux x64 | `codewhale-linux-x64` | `codewhale-tui-linux-x64` | + | Linux ARM64 | `codewhale-linux-arm64` | `codewhale-tui-linux-arm64` | + | macOS x64 | `codewhale-macos-x64` | `codewhale-tui-macos-x64` | + | macOS ARM | `codewhale-macos-arm64` | `codewhale-tui-macos-arm64` | + | Windows x64 | `codewhale-windows-x64.exe` | `codewhale-tui-windows-x64.exe` | - Then `chmod +x` both (Unix) and run `./deepseek`. + Then `chmod +x` both (Unix) and run `./codewhale`. + + Legacy `deepseek-*` and `deepseek-tui-*` assets are also attached for one release cycle so that existing `deepseek update` invocations on v0.8.40 keep working; they install the deprecation shims, which forward to the canonical binaries. ### Verify (recommended) - Download `deepseek-artifacts-sha256.txt` from this Release and verify: + Download `codewhale-artifacts-sha256.txt` from this Release and verify: ```bash # Linux - sha256sum -c deepseek-artifacts-sha256.txt + sha256sum -c codewhale-artifacts-sha256.txt # macOS - shasum -a 256 -c deepseek-artifacts-sha256.txt + shasum -a 256 -c codewhale-artifacts-sha256.txt ``` + The legacy `deepseek-artifacts-sha256.txt` is also attached for backward compatibility and contains the same hashes. + ## Changelog See [CHANGELOG.md](https://github.com/Hmbown/DeepSeek-TUI/blob/main/CHANGELOG.md) for the full notes for this release. diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 65ba1aef..613b6d88 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -7,9 +7,15 @@ repository.workspace = true description = "Codex-style CLI facade for DeepSeek workspace architecture" [[bin]] -name = "deepseek" +name = "codewhale" path = "src/main.rs" +# Legacy alias — forwards to `codewhale` and prints a deprecation notice. +# Will be removed in v0.9.0. +[[bin]] +name = "deepseek" +path = "src/bin/deepseek_legacy_shim.rs" + [dependencies] anyhow.workspace = true clap.workspace = true diff --git a/crates/cli/src/bin/deepseek_legacy_shim.rs b/crates/cli/src/bin/deepseek_legacy_shim.rs new file mode 100644 index 00000000..b6e4abdc --- /dev/null +++ b/crates/cli/src/bin/deepseek_legacy_shim.rs @@ -0,0 +1,32 @@ +//! Legacy `deepseek` alias. +//! +//! Forwards argv to the `codewhale` dispatcher and prints a one-line +//! deprecation notice to stderr on each invocation. This binary exists +//! for one release cycle to give existing installs a smooth path to the +//! new name; it will be removed in v0.9.0. See `docs/REBRAND.md` for the +//! full migration story. + +use std::env; +use std::process::Command; + +fn main() { + eprintln!( + "warning: `deepseek` is deprecated; run `codewhale` instead. \ + This alias will be removed in v0.9.0." + ); + let args: Vec = env::args_os() + .skip(1) + .map(|a| a.to_string_lossy().into_owned()) + .collect(); + let status = match Command::new("codewhale").args(&args).status() { + Ok(s) => s, + Err(e) => { + eprintln!( + "error: failed to spawn `codewhale`: {e}. Is it on PATH? \ + Install with `cargo install codewhale-cli` or via npm/Homebrew." + ); + std::process::exit(127); + } + }; + std::process::exit(status.code().unwrap_or(1)); +} diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 12a0cc24..28147236 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -56,10 +56,10 @@ impl From for ProviderKind { #[derive(Debug, Parser)] #[command( - name = "deepseek", + name = "codewhale", version = env!("DEEPSEEK_BUILD_VERSION"), - bin_name = "deepseek", - override_usage = "deepseek [OPTIONS] [PROMPT]\n deepseek [OPTIONS] [ARGS]" + bin_name = "codewhale", + override_usage = "codewhale [OPTIONS] [PROMPT]\n codewhale [OPTIONS] [ARGS]" )] struct Cli { #[arg(long)] @@ -175,26 +175,26 @@ Common forwarded flags: /// Generate shell completions. #[command(after_help = r#"Examples: Bash (current shell only): - source <(deepseek completion bash) + source <(codewhale completion bash) Bash (persistent, Linux/bash-completion): mkdir -p ~/.local/share/bash-completion/completions - deepseek completion bash > ~/.local/share/bash-completion/completions/deepseek + codewhale completion bash > ~/.local/share/bash-completion/completions/codewhale # Requires bash-completion to be installed and loaded by your shell. Zsh: mkdir -p ~/.zfunc - deepseek completion zsh > ~/.zfunc/_deepseek + codewhale completion zsh > ~/.zfunc/_codewhale # Add to ~/.zshrc if needed: # fpath=(~/.zfunc $fpath) # autoload -Uz compinit && compinit Fish: mkdir -p ~/.config/fish/completions - deepseek completion fish > ~/.config/fish/completions/deepseek.fish + codewhale completion fish > ~/.config/fish/completions/codewhale.fish PowerShell (current shell only): - deepseek completion powershell | Out-String | Invoke-Expression + codewhale completion powershell | Out-String | Invoke-Expression The command prints the completion script to stdout; redirect it to a path your shell loads automatically."#)] Completion { @@ -203,7 +203,7 @@ The command prints the completion script to stdout; redirect it to a path your s }, /// Print a usage rollup from the audit log and session store. Metrics(MetricsArgs), - /// Check for and apply updates to the `deepseek` binary. + /// Check for and apply updates to the `codewhale` binary. Update, } @@ -521,7 +521,7 @@ fn run() -> Result<()> { Some(Commands::AppServer(args)) => run_app_server_command(args), Some(Commands::Completion { shell }) => { let mut cmd = Cli::command(); - generate(shell, &mut cmd, "deepseek", &mut io::stdout()); + generate(shell, &mut cmd, "codewhale", &mut io::stdout()); Ok(()) } Some(Commands::Metrics(args)) => run_metrics_command(args), @@ -1356,7 +1356,7 @@ fn run_dispatcher_resume_picker( println!(); println!("Windows note: enter a session id or prefix from the list above."); - println!("You can also run `deepseek resume --last` to skip this prompt."); + println!("You can also run `codewhale resume --last` to skip this prompt."); print!("Session id/prefix (Enter to cancel): "); io::stdout().flush()?; @@ -1423,7 +1423,7 @@ fn build_tui_command( | ProviderKind::Ollama ) { bail!( - "The interactive TUI supports DeepSeek, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, OpenRouter, Novita, Fireworks, SGLang, vLLM, and Ollama providers. Remove --provider {} or use `deepseek model ...` for provider registry inspection.", + "The interactive TUI supports DeepSeek, NVIDIA NIM, OpenAI-compatible, AtlasCloud, Wanjie Ark, OpenRouter, Novita, Fireworks, SGLang, vLLM, and Ollama providers. Remove --provider {} or use `codewhale model ...` for provider registry inspection.", resolved_runtime.provider.as_str() ); } @@ -1502,7 +1502,7 @@ fn build_tui_command( fn exit_with_tui_status(status: std::process::ExitStatus) -> Result<()> { match status.code() { Some(code) => std::process::exit(code), - None => bail!("deepseek-tui terminated by signal"), + None => bail!("codewhale-tui terminated by signal"), } } @@ -1514,7 +1514,7 @@ fn delegate_simple_tui(args: Vec) -> Result<()> { .map_err(|err| anyhow!("{}", tui_spawn_error(&tui, &err)))?; match status.code() { Some(code) => std::process::exit(code), - None => bail!("deepseek-tui terminated by signal"), + None => bail!("codewhale-tui terminated by signal"), } } @@ -1522,23 +1522,23 @@ fn tui_spawn_error(tui: &Path, err: &io::Error) -> String { format!( "failed to spawn companion TUI binary at {}: {err}\n\ \n\ -The `deepseek` dispatcher found a `deepseek-tui` file, but the OS refused \ +The `codewhale` dispatcher found a `codewhale-tui` file, but the OS refused \ to execute it. Common fixes:\n\ - - Reinstall with `npm install -g deepseek-tui`, or run `deepseek update`.\n\ - - On Windows, run `where deepseek` and `where deepseek-tui`; both should \ + - Reinstall with `npm install -g codewhale`, or run `codewhale update`.\n\ + - On Windows, run `where codewhale` and `where codewhale-tui`; both should \ come from the same install directory.\n\ - - If you downloaded release assets manually, keep both `deepseek` and \ -`deepseek-tui` binaries together and make sure the TUI binary is executable.\n\ - - Set DEEPSEEK_TUI_BIN to the absolute path of a working `deepseek-tui` \ + - If you downloaded release assets manually, keep both `codewhale` and \ +`codewhale-tui` binaries together and make sure the TUI binary is executable.\n\ + - Set DEEPSEEK_TUI_BIN to the absolute path of a working `codewhale-tui` \ binary.", tui.display() ) } -/// Resolve the sibling `deepseek-tui` executable next to the running +/// Resolve the sibling `codewhale-tui` executable next to the running /// dispatcher. Honours platform executable suffix (`.exe` on Windows) so /// the npm-distributed Windows package — which ships -/// `bin/downloads/deepseek-tui.exe` — is found by `Path::exists` (#247). +/// `bin/downloads/codewhale-tui.exe` — is found by `Path::exists` (#247). /// /// `DEEPSEEK_TUI_BIN` is consulted first as an explicit override for /// custom installs and CI test layouts. On Windows we additionally try @@ -1562,39 +1562,39 @@ fn locate_sibling_tui_binary() -> Result { } // Build a stable error path so the user sees the platform-correct - // expected name, not "deepseek-tui" on Windows. - let expected = current.with_file_name(format!("deepseek-tui{}", std::env::consts::EXE_SUFFIX)); + // expected name, not "codewhale-tui" on Windows. + let expected = current.with_file_name(format!("codewhale-tui{}", std::env::consts::EXE_SUFFIX)); bail!( - "Companion `deepseek-tui` binary not found at {}.\n\ + "Companion `codewhale-tui` binary not found at {}.\n\ \n\ -The `deepseek` dispatcher delegates interactive sessions to a sibling \ -`deepseek-tui` binary. To fix this, install one of:\n\ - • npm: npm install -g deepseek-tui (downloads both binaries)\n\ - • cargo: cargo install deepseek-tui-cli deepseek-tui --locked\n\ - • GitHub Releases: download BOTH `deepseek-` AND \ -`deepseek-tui-` from https://github.com/Hmbown/DeepSeek-TUI/releases/latest \ +The `codewhale` dispatcher delegates interactive sessions to a sibling \ +`codewhale-tui` binary. To fix this, install one of:\n\ + • npm: npm install -g codewhale (downloads both binaries)\n\ + • cargo: cargo install codewhale-cli codewhale-tui --locked\n\ + • GitHub Releases: download BOTH `codewhale-` AND \ +`codewhale-tui-` from https://github.com/Hmbown/DeepSeek-TUI/releases/latest \ and place them in the same directory.\n\ \n\ -Or set DEEPSEEK_TUI_BIN to the absolute path of an existing `deepseek-tui` binary.", +Or set DEEPSEEK_TUI_BIN to the absolute path of an existing `codewhale-tui` binary.", expected.display() ); } /// Return the first existing sibling-binary path under any of the names -/// `deepseek-tui` might use on this platform. Pure function to keep +/// `codewhale-tui` might use on this platform. Pure function to keep /// `locate_sibling_tui_binary` testable. fn sibling_tui_candidate(dispatcher: &Path) -> Option { // Primary: platform-correct name. EXE_SUFFIX is "" on Unix and ".exe" // on Windows. let primary = - dispatcher.with_file_name(format!("deepseek-tui{}", std::env::consts::EXE_SUFFIX)); + dispatcher.with_file_name(format!("codewhale-tui{}", std::env::consts::EXE_SUFFIX)); if primary.is_file() { return Some(primary); } // Windows fallback: a user who manually renamed `.exe` away (per the // workaround in #247) still launches successfully under the new code. if cfg!(windows) { - let suffixless = dispatcher.with_file_name("deepseek-tui"); + let suffixless = dispatcher.with_file_name("codewhale-tui"); if suffixless.is_file() { return Some(suffixless); } @@ -2712,11 +2712,11 @@ mod tests { vec![ "", "bash", - "source <(deepseek completion bash)", - "~/.local/share/bash-completion/completions/deepseek", + "source <(codewhale completion bash)", + "~/.local/share/bash-completion/completions/codewhale", "fpath=(~/.zfunc $fpath)", - "deepseek completion fish > ~/.config/fish/completions/deepseek.fish", - "deepseek completion powershell | Out-String | Invoke-Expression", + "codewhale completion fish > ~/.config/fish/completions/codewhale.fish", + "codewhale completion powershell | Out-String | Invoke-Expression", ], ), ("metrics", vec!["--json", "--since"]), @@ -2735,8 +2735,8 @@ mod tests { } /// Regression for issue #247: on Windows the dispatcher must find the - /// sibling `deepseek-tui.exe`, not bail out looking for an - /// extension-less `deepseek-tui`. The candidate resolver also accepts + /// sibling `codewhale-tui.exe`, not bail out looking for an + /// extension-less `codewhale-tui`. The candidate resolver also accepts /// the suffix-less name on Windows so users who manually renamed the /// file as a workaround keep working after the upgrade. #[test] @@ -2744,7 +2744,7 @@ mod tests { let dir = tempfile::TempDir::new().expect("tempdir"); let dispatcher = dir .path() - .join("deepseek") + .join("codewhale") .with_extension(std::env::consts::EXE_EXTENSION); // Touch the dispatcher so its parent dir is the lookup root. std::fs::write(&dispatcher, b"").unwrap(); @@ -2753,7 +2753,7 @@ mod tests { assert!(sibling_tui_candidate(&dispatcher).is_none()); let target = - dispatcher.with_file_name(format!("deepseek-tui{}", std::env::consts::EXE_SUFFIX)); + dispatcher.with_file_name(format!("codewhale-tui{}", std::env::consts::EXE_SUFFIX)); std::fs::write(&target, b"").unwrap(); let found = sibling_tui_candidate(&dispatcher).expect("must locate sibling"); @@ -2763,11 +2763,11 @@ mod tests { #[test] fn dispatcher_spawn_error_names_path_and_recovery_checks() { let err = io::Error::new(io::ErrorKind::PermissionDenied, "access is denied"); - let message = tui_spawn_error(Path::new("C:/tools/deepseek-tui.exe"), &err); + let message = tui_spawn_error(Path::new("C:/tools/codewhale-tui.exe"), &err); - assert!(message.contains("C:/tools/deepseek-tui.exe")); + assert!(message.contains("C:/tools/codewhale-tui.exe")); assert!(message.contains("access is denied")); - assert!(message.contains("where deepseek")); + assert!(message.contains("where codewhale")); assert!(message.contains("DEEPSEEK_TUI_BIN")); } @@ -2779,15 +2779,15 @@ mod tests { #[test] fn sibling_tui_candidate_windows_falls_back_to_suffixless() { let dir = tempfile::TempDir::new().expect("tempdir"); - let dispatcher = dir.path().join("deepseek.exe"); + let dispatcher = dir.path().join("codewhale.exe"); std::fs::write(&dispatcher, b"").unwrap(); // Only the suffixless name exists — emulates the manual rename. - let suffixless = dispatcher.with_file_name("deepseek-tui"); + let suffixless = dispatcher.with_file_name("codewhale-tui"); std::fs::write(&suffixless, b"").unwrap(); let found = sibling_tui_candidate(&dispatcher) - .expect("Windows fallback must locate suffixless deepseek-tui"); + .expect("Windows fallback must locate suffixless codewhale-tui"); assert_eq!(found, suffixless); } diff --git a/crates/cli/src/update.rs b/crates/cli/src/update.rs index 33f2693f..7c07e503 100644 --- a/crates/cli/src/update.rs +++ b/crates/cli/src/update.rs @@ -1,4 +1,4 @@ -//! Self-update for the `deepseek` binary. +//! Self-update for the `codewhale` binary. //! //! The `update` subcommand fetches the latest release from //! `github.com/Hmbown/DeepSeek-TUI/releases/latest`, downloads the @@ -11,14 +11,14 @@ use std::path::{Path, PathBuf}; use anyhow::{Context, Result, bail}; use std::io::Write; -const CHECKSUM_MANIFEST_ASSET: &str = "deepseek-artifacts-sha256.txt"; +const CHECKSUM_MANIFEST_ASSET: &str = "codewhale-artifacts-sha256.txt"; const LATEST_RELEASE_URL: &str = "https://api.github.com/repos/Hmbown/DeepSeek-TUI/releases/latest"; const CNB_REPO_URL: &str = "https://cnb.cool/deepseek-tui.com/DeepSeek-TUI"; const RELEASE_BASE_URL_ENV: &str = "DEEPSEEK_TUI_RELEASE_BASE_URL"; const LEGACY_RELEASE_BASE_URL_ENV: &str = "DEEPSEEK_RELEASE_BASE_URL"; const UPDATE_VERSION_ENV: &str = "DEEPSEEK_TUI_VERSION"; const LEGACY_UPDATE_VERSION_ENV: &str = "DEEPSEEK_VERSION"; -const UPDATE_USER_AGENT: &str = "deepseek-tui-updater"; +const UPDATE_USER_AGENT: &str = "codewhale-updater"; /// Run the self-update workflow. pub fn run_update() -> Result<()> { @@ -134,19 +134,19 @@ pub(crate) fn binary_prefix_for_exe(current_exe: &Path) -> &'static str { let exe_name = current_exe .file_name() .and_then(|name| name.to_str()) - .unwrap_or("deepseek"); - if exe_name.contains("deepseek-tui") { - "deepseek-tui" + .unwrap_or("codewhale"); + if exe_name.contains("codewhale-tui") { + "codewhale-tui" } else { - "deepseek" + "codewhale" } } fn sibling_prefix_for(prefix: &str) -> &'static str { - if prefix == "deepseek-tui" { - "deepseek" + if prefix == "codewhale-tui" { + "codewhale" } else { - "deepseek-tui" + "codewhale-tui" } } @@ -337,7 +337,7 @@ fn release_from_mirror_base_url( browser_download_url: mirror_asset_url(base_url, CHECKSUM_MANIFEST_ASSET), }]; - for prefix in ["deepseek", "deepseek-tui"] { + for prefix in ["codewhale", "codewhale-tui"] { let name = release_asset_name_for_prefix(prefix, os, rust_arch); assets.push(Asset { browser_download_url: mirror_asset_url(base_url, &name), @@ -357,10 +357,10 @@ fn update_network_fallback_hint() -> String { "GitHub release downloads may be blocked or slow on this network.\n\ For mainland China, use one of these fallback paths:\n\ 1. Source build from the CNB mirror, installing both shipped binaries:\n\ - cargo install --git {CNB_REPO_URL} --tag vX.Y.Z deepseek-tui-cli --locked --force\n\ - cargo install --git {CNB_REPO_URL} --tag vX.Y.Z deepseek-tui --locked --force\n\ + cargo install --git {CNB_REPO_URL} --tag vX.Y.Z codewhale-cli --locked --force\n\ + cargo install --git {CNB_REPO_URL} --tag vX.Y.Z codewhale-tui --locked --force\n\ 2. Use a binary asset mirror:\n\ - {RELEASE_BASE_URL_ENV}=https://// {UPDATE_VERSION_ENV}=X.Y.Z deepseek update\n\ + {RELEASE_BASE_URL_ENV}=https://// {UPDATE_VERSION_ENV}=X.Y.Z codewhale update\n\ The mirror directory must contain {CHECKSUM_MANIFEST_ASSET} and the platform binaries." ) } @@ -428,7 +428,7 @@ fn replace_binary(target: &Path, new_bytes: &[u8]) -> Result<()> { .unwrap_or_else(|| Path::new(".")); let mut tmp = tempfile::Builder::new() - .prefix(".deepseek-update-") + .prefix(".codewhale-update-") .tempfile_in(parent) .with_context(|| format!("failed to create temp file in {}", parent.display()))?; tmp.write_all(new_bytes) @@ -532,46 +532,57 @@ mod tests { /// Verify binary prefix detection for dispatcher vs TUI binary. #[test] fn test_binary_prefix_detection() { - // TUI binary should use deepseek-tui prefix + // TUI binary should use codewhale-tui prefix assert_eq!( - binary_prefix_for_exe(Path::new("deepseek-tui")), - "deepseek-tui" + binary_prefix_for_exe(Path::new("codewhale-tui")), + "codewhale-tui" ); assert_eq!( - binary_prefix_for_exe(Path::new("deepseek-tui.exe")), - "deepseek-tui" + binary_prefix_for_exe(Path::new("codewhale-tui.exe")), + "codewhale-tui" ); assert_eq!( - binary_prefix_for_exe(Path::new("/usr/local/bin/deepseek-tui")), - "deepseek-tui" + binary_prefix_for_exe(Path::new("/usr/local/bin/codewhale-tui")), + "codewhale-tui" ); - // Dispatcher binary should use deepseek prefix - assert_eq!(binary_prefix_for_exe(Path::new("deepseek")), "deepseek"); - assert_eq!(binary_prefix_for_exe(Path::new("deepseek.exe")), "deepseek"); + // Dispatcher binary should use codewhale prefix + assert_eq!(binary_prefix_for_exe(Path::new("codewhale")), "codewhale"); assert_eq!( - binary_prefix_for_exe(Path::new("/usr/local/bin/deepseek")), - "deepseek" + binary_prefix_for_exe(Path::new("codewhale.exe")), + "codewhale" + ); + assert_eq!( + binary_prefix_for_exe(Path::new("/usr/local/bin/codewhale")), + "codewhale" ); // Fallback for unknown names - assert_eq!(binary_prefix_for_exe(Path::new("other-binary")), "deepseek"); + assert_eq!( + binary_prefix_for_exe(Path::new("other-binary")), + "codewhale" + ); } #[test] fn test_release_asset_stem_for_supported_platforms() { let cases = [ - ("deepseek", "macos", "aarch64", "deepseek-macos-arm64"), - ("deepseek", "macos", "x86_64", "deepseek-macos-x64"), - ("deepseek", "linux", "x86_64", "deepseek-linux-x64"), - ("deepseek", "windows", "x86_64", "deepseek-windows-x64"), + ("codewhale", "macos", "aarch64", "codewhale-macos-arm64"), + ("codewhale", "macos", "x86_64", "codewhale-macos-x64"), + ("codewhale", "linux", "x86_64", "codewhale-linux-x64"), + ("codewhale", "windows", "x86_64", "codewhale-windows-x64"), ( - "deepseek-tui", + "codewhale-tui", "macos", "aarch64", - "deepseek-tui-macos-arm64", + "codewhale-tui-macos-arm64", + ), + ( + "codewhale-tui", + "linux", + "x86_64", + "codewhale-tui-linux-x64", ), - ("deepseek-tui", "linux", "x86_64", "deepseek-tui-linux-x64"), ]; for (exe, os, arch, expected) in cases { @@ -584,10 +595,10 @@ mod tests { let dir = tempfile::TempDir::new().unwrap(); let dispatcher = dir .path() - .join(format!("deepseek{}", std::env::consts::EXE_SUFFIX)); + .join(format!("codewhale{}", std::env::consts::EXE_SUFFIX)); let tui = dir .path() - .join(format!("deepseek-tui{}", std::env::consts::EXE_SUFFIX)); + .join(format!("codewhale-tui{}", std::env::consts::EXE_SUFFIX)); std::fs::write(&dispatcher, b"dispatcher").unwrap(); std::fs::write(&tui, b"tui").unwrap(); @@ -598,8 +609,8 @@ mod tests { .collect::>(); assert_eq!(paths, vec![dispatcher.as_path(), tui.as_path()]); - assert!(targets[0].asset_stem.starts_with("deepseek-")); - assert!(targets[1].asset_stem.starts_with("deepseek-tui-")); + assert!(targets[0].asset_stem.starts_with("codewhale-")); + assert!(targets[1].asset_stem.starts_with("codewhale-tui-")); } #[test] @@ -607,37 +618,37 @@ mod tests { let dir = tempfile::TempDir::new().unwrap(); let dispatcher = dir .path() - .join(format!("deepseek{}", std::env::consts::EXE_SUFFIX)); + .join(format!("codewhale{}", std::env::consts::EXE_SUFFIX)); std::fs::write(&dispatcher, b"dispatcher").unwrap(); let targets = update_targets_for_exe(&dispatcher); assert_eq!(targets.len(), 1); assert_eq!(targets[0].path, dispatcher); - assert!(targets[0].asset_stem.starts_with("deepseek-")); + assert!(targets[0].asset_stem.starts_with("codewhale-")); } #[test] fn test_asset_matching_accepts_binary_assets_and_rejects_checksums() { assert!(asset_matches_platform( - "deepseek-macos-arm64", - "deepseek-macos-arm64" + "codewhale-macos-arm64", + "codewhale-macos-arm64" )); assert!(asset_matches_platform( - "deepseek-macos-arm64.tar.gz", - "deepseek-macos-arm64" + "codewhale-macos-arm64.tar.gz", + "codewhale-macos-arm64" )); assert!(asset_matches_platform( - "deepseek-tui-windows-x64.exe", - "deepseek-tui-windows-x64" + "codewhale-tui-windows-x64.exe", + "codewhale-tui-windows-x64" )); assert!(!asset_matches_platform( - "deepseek-tui-windows-x64.exe.sha256", - "deepseek-tui-windows-x64" + "codewhale-tui-windows-x64.exe.sha256", + "codewhale-tui-windows-x64" )); assert!(!asset_matches_platform( - "deepseek-macos-aarch64.tar.gz", - "deepseek-macos-arm64" + "codewhale-macos-aarch64.tar.gz", + "codewhale-macos-arm64" )); } @@ -663,18 +674,18 @@ mod tests { #[test] fn parse_checksum_manifest_accepts_sha256sum_format() { let manifest = "\ -2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 deepseek-macos-arm64 -E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-windows-x64.exe +2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 codewhale-macos-arm64 +E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *codewhale-windows-x64.exe "; let checksums = parse_checksum_manifest(manifest).expect("valid manifest"); assert_eq!( - checksums.get("deepseek-macos-arm64").map(String::as_str), + checksums.get("codewhale-macos-arm64").map(String::as_str), Some("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824") ); assert_eq!( checksums - .get("deepseek-windows-x64.exe") + .get("codewhale-windows-x64.exe") .map(String::as_str), Some("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") ); @@ -682,7 +693,7 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind #[test] fn parse_checksum_manifest_rejects_malformed_lines() { - let err = parse_checksum_manifest("not-a-hash deepseek-macos-arm64") + let err = parse_checksum_manifest("not-a-hash codewhale-macos-arm64") .expect_err("invalid manifest line should fail"); assert!( err.to_string().contains("invalid SHA256 manifest line"), @@ -694,11 +705,11 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind fn expected_sha256_from_manifest_requires_matching_asset() { let manifest = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 other-asset\n"; - let err = expected_sha256_from_manifest(manifest, "deepseek-macos-arm64") + let err = expected_sha256_from_manifest(manifest, "codewhale-macos-arm64") .expect_err("missing asset should fail"); assert!( err.to_string() - .contains("checksum manifest is missing deepseek-macos-arm64"), + .contains("checksum manifest is missing codewhale-macos-arm64"), "unexpected error: {err:#}" ); } @@ -706,7 +717,7 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind #[test] fn test_replace_binary_creates_and_replaces() { let dir = tempfile::TempDir::new().unwrap(); - let target = dir.path().join("deepseek-test"); + let target = dir.path().join("codewhale-test"); // Write initial content std::fs::write(&target, b"old binary").unwrap(); @@ -718,30 +729,30 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind #[test] fn test_replace_binary_creates_new_file() { let dir = tempfile::TempDir::new().unwrap(); - let target = dir.path().join("deepseek-new-test"); + let target = dir.path().join("codewhale-new-test"); replace_binary(&target, b"fresh binary").unwrap(); let content = std::fs::read_to_string(&target).unwrap(); assert_eq!(content, "fresh binary"); } - /// Mocked GitHub release payload covering both the dispatcher (`deepseek`) - /// and the legacy TUI (`deepseek-tui`) binaries across our published + /// Mocked GitHub release payload covering both the dispatcher (`codewhale`) + /// and the legacy TUI (`codewhale-tui`) binaries across our published /// platform/arch matrix, plus a checksum sibling that must never be picked /// as the primary binary. fn mocked_release() -> Release { let json = r#"{ "tag_name": "v0.8.8", "assets": [ - { "name": "deepseek-linux-x64", "browser_download_url": "https://example.invalid/deepseek-linux-x64" }, - { "name": "deepseek-macos-x64", "browser_download_url": "https://example.invalid/deepseek-macos-x64" }, - { "name": "deepseek-macos-arm64", "browser_download_url": "https://example.invalid/deepseek-macos-arm64" }, - { "name": "deepseek-windows-x64.exe", "browser_download_url": "https://example.invalid/deepseek-windows-x64.exe" }, - { "name": "deepseek-windows-x64.exe.sha256", "browser_download_url": "https://example.invalid/deepseek-windows-x64.exe.sha256" }, - { "name": "deepseek-tui-linux-x64", "browser_download_url": "https://example.invalid/deepseek-tui-linux-x64" }, - { "name": "deepseek-tui-macos-x64", "browser_download_url": "https://example.invalid/deepseek-tui-macos-x64" }, - { "name": "deepseek-tui-macos-arm64", "browser_download_url": "https://example.invalid/deepseek-tui-macos-arm64" }, - { "name": "deepseek-tui-windows-x64.exe","browser_download_url": "https://example.invalid/deepseek-tui-windows-x64.exe" } + { "name": "codewhale-linux-x64", "browser_download_url": "https://example.invalid/codewhale-linux-x64" }, + { "name": "codewhale-macos-x64", "browser_download_url": "https://example.invalid/codewhale-macos-x64" }, + { "name": "codewhale-macos-arm64", "browser_download_url": "https://example.invalid/codewhale-macos-arm64" }, + { "name": "codewhale-windows-x64.exe", "browser_download_url": "https://example.invalid/codewhale-windows-x64.exe" }, + { "name": "codewhale-windows-x64.exe.sha256", "browser_download_url": "https://example.invalid/codewhale-windows-x64.exe.sha256" }, + { "name": "codewhale-tui-linux-x64", "browser_download_url": "https://example.invalid/codewhale-tui-linux-x64" }, + { "name": "codewhale-tui-macos-x64", "browser_download_url": "https://example.invalid/codewhale-tui-macos-x64" }, + { "name": "codewhale-tui-macos-arm64", "browser_download_url": "https://example.invalid/codewhale-tui-macos-arm64" }, + { "name": "codewhale-tui-windows-x64.exe","browser_download_url": "https://example.invalid/codewhale-tui-windows-x64.exe" } ] }"#; serde_json::from_str(json).expect("mock release JSON") @@ -751,14 +762,14 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind fn mocked_release_selects_dispatcher_asset_for_supported_platforms() { let release = mocked_release(); let cases = [ - ("macos", "aarch64", "deepseek-macos-arm64"), - ("macos", "x86_64", "deepseek-macos-x64"), - ("linux", "x86_64", "deepseek-linux-x64"), - ("windows", "x86_64", "deepseek-windows-x64.exe"), + ("macos", "aarch64", "codewhale-macos-arm64"), + ("macos", "x86_64", "codewhale-macos-x64"), + ("linux", "x86_64", "codewhale-linux-x64"), + ("windows", "x86_64", "codewhale-windows-x64.exe"), ]; for (os, arch, expected) in cases { - let stem = release_asset_stem_for(Path::new("/usr/local/bin/deepseek"), os, arch); + let stem = release_asset_stem_for(Path::new("/usr/local/bin/codewhale"), os, arch); let asset = select_platform_asset(&release, &stem) .unwrap_or_else(|| panic!("no asset for {os}/{arch} (stem {stem})")); assert_eq!(asset.name, expected, "{os}/{arch}"); @@ -768,10 +779,13 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind #[test] fn mocked_release_selects_tui_asset_when_tui_binary_invokes_update() { let release = mocked_release(); - let stem = - release_asset_stem_for(Path::new("/usr/local/bin/deepseek-tui"), "macos", "aarch64"); + let stem = release_asset_stem_for( + Path::new("/usr/local/bin/codewhale-tui"), + "macos", + "aarch64", + ); let asset = select_platform_asset(&release, &stem).expect("TUI platform asset"); - assert_eq!(asset.name, "deepseek-tui-macos-arm64"); + assert_eq!(asset.name, "codewhale-tui-macos-arm64"); } #[test] @@ -787,19 +801,19 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind assert_eq!(release.assets[0].name, CHECKSUM_MANIFEST_ASSET); assert_eq!( release.assets[0].browser_download_url, - "https://mirror.example/releases/v0.8.36/deepseek-artifacts-sha256.txt" + "https://mirror.example/releases/v0.8.36/codewhale-artifacts-sha256.txt" ); let dispatcher = - select_platform_asset(&release, "deepseek-linux-x64").expect("dispatcher asset"); + select_platform_asset(&release, "codewhale-linux-x64").expect("dispatcher asset"); assert_eq!( dispatcher.browser_download_url, - "https://mirror.example/releases/v0.8.36/deepseek-linux-x64" + "https://mirror.example/releases/v0.8.36/codewhale-linux-x64" ); - let tui = select_platform_asset(&release, "deepseek-tui-linux-x64").expect("tui asset"); + let tui = select_platform_asset(&release, "codewhale-tui-linux-x64").expect("tui asset"); assert_eq!( tui.browser_download_url, - "https://mirror.example/releases/v0.8.36/deepseek-tui-linux-x64" + "https://mirror.example/releases/v0.8.36/codewhale-tui-linux-x64" ); } @@ -814,12 +828,12 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind assert_eq!(release.tag_name, "v0.8.36"); assert!( - select_platform_asset(&release, "deepseek-windows-x64") - .is_some_and(|asset| asset.name == "deepseek-windows-x64.exe") + select_platform_asset(&release, "codewhale-windows-x64") + .is_some_and(|asset| asset.name == "codewhale-windows-x64.exe") ); assert!( - select_platform_asset(&release, "deepseek-tui-windows-x64") - .is_some_and(|asset| asset.name == "deepseek-tui-windows-x64.exe") + select_platform_asset(&release, "codewhale-tui-windows-x64") + .is_some_and(|asset| asset.name == "codewhale-tui-windows-x64.exe") ); } @@ -830,8 +844,8 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind assert!(hint.contains(CNB_REPO_URL), "{hint}"); assert!(hint.contains(RELEASE_BASE_URL_ENV), "{hint}"); assert!(hint.contains(UPDATE_VERSION_ENV), "{hint}"); - assert!(hint.contains("deepseek-tui-cli"), "{hint}"); - assert!(hint.contains("deepseek-tui --locked"), "{hint}"); + assert!(hint.contains("codewhale-cli"), "{hint}"); + assert!(hint.contains("codewhale-tui --locked"), "{hint}"); } fn serve_http_once( @@ -868,8 +882,8 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind let body = br#"{ "tag_name": "v9.9.9", "assets": [ - { "name": "deepseek-linux-x64", "browser_download_url": "http://example.invalid/deepseek-linux-x64" }, - { "name": "deepseek-artifacts-sha256.txt", "browser_download_url": "http://example.invalid/deepseek-artifacts-sha256.txt" } + { "name": "codewhale-linux-x64", "browser_download_url": "http://example.invalid/codewhale-linux-x64" }, + { "name": "codewhale-artifacts-sha256.txt", "browser_download_url": "http://example.invalid/codewhale-artifacts-sha256.txt" } ] }"#; let (url, request_rx, handle) = serve_http_once("200 OK", "application/json", body); @@ -886,7 +900,7 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind "got {request:?}" ); assert!( - request_lower.contains("user-agent: deepseek-tui-updater"), + request_lower.contains("user-agent: codewhale-updater"), "got {request:?}" ); handle.join().expect("test server thread"); @@ -917,7 +931,7 @@ E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 *deepseek-wind let request_lower = request.to_ascii_lowercase(); assert!(request.starts_with("GET /release "), "got {request:?}"); assert!( - request_lower.contains("user-agent: deepseek-tui-updater"), + request_lower.contains("user-agent: codewhale-updater"), "got {request:?}" ); handle.join().expect("test server thread"); diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index c04abe65..50126759 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -5,7 +5,7 @@ edition.workspace = true license.workspace = true repository.workspace = true description = "Terminal UI for DeepSeek" -default-run = "deepseek-tui" +default-run = "codewhale-tui" [features] default = ["tui", "json", "toml"] @@ -15,9 +15,15 @@ json = ["schemaui/json"] toml = ["schemaui/toml"] [[bin]] -name = "deepseek-tui" +name = "codewhale-tui" path = "src/main.rs" +# Legacy alias — forwards to `codewhale-tui` and prints a deprecation +# notice. Will be removed in v0.9.0. +[[bin]] +name = "deepseek-tui" +path = "src/bin/deepseek_tui_legacy_shim.rs" + [dependencies] anyhow = "1.0.100" arboard = "3.4" diff --git a/crates/tui/src/bin/deepseek_tui_legacy_shim.rs b/crates/tui/src/bin/deepseek_tui_legacy_shim.rs new file mode 100644 index 00000000..2e36db97 --- /dev/null +++ b/crates/tui/src/bin/deepseek_tui_legacy_shim.rs @@ -0,0 +1,32 @@ +//! Legacy `deepseek-tui` alias. +//! +//! Forwards argv to the `codewhale-tui` runtime and prints a one-line +//! deprecation notice to stderr on each invocation. This binary exists +//! for one release cycle to give existing installs a smooth path to the +//! new name; it will be removed in v0.9.0. See `docs/REBRAND.md` for the +//! full migration story. + +use std::env; +use std::process::Command; + +fn main() { + eprintln!( + "warning: `deepseek-tui` is deprecated; run `codewhale-tui` (or `codewhale`) instead. \ + This alias will be removed in v0.9.0." + ); + let args: Vec = env::args_os() + .skip(1) + .map(|a| a.to_string_lossy().into_owned()) + .collect(); + let status = match Command::new("codewhale-tui").args(&args).status() { + Ok(s) => s, + Err(e) => { + eprintln!( + "error: failed to spawn `codewhale-tui`: {e}. Is it on PATH? \ + Install with `cargo install codewhale-tui` or via npm/Homebrew." + ); + std::process::exit(127); + } + }; + std::process::exit(status.code().unwrap_or(1)); +} diff --git a/scripts/release/check-versions.sh b/scripts/release/check-versions.sh index 776af6f4..4f566c6b 100755 --- a/scripts/release/check-versions.sh +++ b/scripts/release/check-versions.sh @@ -6,8 +6,10 @@ # 1. No `crates/*/Cargo.toml` carries a literal `version = "x.y.z"`; every # crate must inherit `version.workspace = true`. # 2. `npm/deepseek-tui/package.json` `version` matches the workspace -# `version` in the root `Cargo.toml`. -# 3. Internal `deepseek-*` path dependency pins match the workspace version. +# `version` in the root `Cargo.toml`. (The npm wrapper directory is +# renamed to `npm/codewhale/` in a follow-up phase; this script will +# be updated then.) +# 3. Internal `codewhale-*` path dependency pins match the workspace version. # 4. The TUI crate's packaged changelog copy matches root `CHANGELOG.md`. # 5. The current release has a dated Keep a Changelog entry and compare link. # 6. README contributor additions are mentioned in the current release entry. @@ -38,11 +40,11 @@ fi # 3) Internal path dependency pins. internal_dep_drift="$( - grep -nE 'deepseek-[a-z-]+[[:space:]]*=[[:space:]]*\{[^}]*version[[:space:]]*=[[:space:]]*"' crates/*/Cargo.toml \ + grep -nE 'codewhale-[a-z-]+[[:space:]]*=[[:space:]]*\{[^}]*version[[:space:]]*=[[:space:]]*"' crates/*/Cargo.toml \ | grep -v "version[[:space:]]*=[[:space:]]*\"${workspace_version}\"" || true )" if [[ -n "${internal_dep_drift}" ]]; then - echo "::error::Internal deepseek-* path dependency versions must match workspace version ${workspace_version}:" >&2 + echo "::error::Internal codewhale-* path dependency versions must match workspace version ${workspace_version}:" >&2 echo "${internal_dep_drift}" >&2 fail=1 fi @@ -125,7 +127,7 @@ fi # 8) Cargo.lock in sync. if ! cargo metadata --locked --format-version 1 --no-deps >/dev/null 2>&1; then - echo "::error::Cargo.lock is out of sync with the manifests. Run 'cargo update -p deepseek-tui' or 'cargo build' and commit the result." >&2 + echo "::error::Cargo.lock is out of sync with the manifests. Run 'cargo update -p codewhale-tui' or 'cargo build' and commit the result." >&2 fail=1 fi diff --git a/scripts/release/crates.sh b/scripts/release/crates.sh index 34b0e188..3918c9f1 100755 --- a/scripts/release/crates.sh +++ b/scripts/release/crates.sh @@ -1,19 +1,19 @@ #!/usr/bin/env bash -# Crates published for each DeepSeek TUI release, in dependency order. +# Crates published for each codewhale release, in dependency order. release_crates=( - deepseek-secrets - deepseek-config - deepseek-protocol - deepseek-state - deepseek-agent - deepseek-execpolicy - deepseek-hooks - deepseek-mcp - deepseek-tools - deepseek-core - deepseek-app-server - deepseek-tui-core - deepseek-tui-cli - deepseek-tui + codewhale-secrets + codewhale-config + codewhale-protocol + codewhale-state + codewhale-agent + codewhale-execpolicy + codewhale-hooks + codewhale-mcp + codewhale-tools + codewhale-core + codewhale-app-server + codewhale-tui-core + codewhale-cli + codewhale-tui )