Merge remote-tracking branch 'origin/rebrand/r5-prompts-strings' into work/v0.8.41-codewhale-ready
This commit is contained in:
@@ -143,7 +143,7 @@ impl AcpServer {
|
||||
.and_then(Value::as_str)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| self.default_cwd.clone());
|
||||
let session_id = format!("deepseek-{}", uuid::Uuid::new_v4());
|
||||
let session_id = format!("codewhale-{}", uuid::Uuid::new_v4());
|
||||
self.sessions.insert(session_id.clone(), AcpSession { cwd });
|
||||
Ok(json!({ "sessionId": session_id }))
|
||||
}
|
||||
@@ -284,8 +284,8 @@ fn initialize_result(client_protocol_version: Option<u64>) -> Value {
|
||||
"sessionCapabilities": {}
|
||||
},
|
||||
"agentInfo": {
|
||||
"name": "deepseek",
|
||||
"title": "DeepSeek TUI",
|
||||
"name": "codewhale",
|
||||
"title": "codewhale",
|
||||
"version": env!("CARGO_PKG_VERSION")
|
||||
},
|
||||
"authMethods": []
|
||||
@@ -423,7 +423,7 @@ mod tests {
|
||||
let result = initialize_result(Some(1));
|
||||
|
||||
assert_eq!(result["protocolVersion"], 1);
|
||||
assert_eq!(result["agentInfo"]["name"], "deepseek");
|
||||
assert_eq!(result["agentInfo"]["name"], "codewhale");
|
||||
assert_eq!(result["agentCapabilities"]["loadSession"], false);
|
||||
assert_eq!(
|
||||
result["agentCapabilities"]["promptCapabilities"]["embeddedContext"],
|
||||
|
||||
@@ -976,7 +976,7 @@ pub struct AutoRouteSelection {
|
||||
}
|
||||
|
||||
pub const AUTO_MODEL_ROUTER_SYSTEM_PROMPT: &str = "\
|
||||
You are the DeepSeek TUI auto-routing classifier. Return only compact JSON: \
|
||||
You are the codewhale auto-routing classifier. Return only compact JSON: \
|
||||
{\"model\":\"deepseek-v4-flash|deepseek-v4-pro\",\"thinking\":\"off|high|max\"}. \
|
||||
Use deepseek-v4-flash for trivial, conversational, status, or single-step work. \
|
||||
Use deepseek-v4-pro for coding, debugging, release work, multi-step tasks, high-risk decisions, \
|
||||
@@ -1706,7 +1706,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-default-mode-test-{}-{}",
|
||||
"codewhale-tui-default-mode-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1731,7 +1731,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-cost-currency-test-{}-{}",
|
||||
"codewhale-tui-cost-currency-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1757,7 +1757,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-theme-command-test-{}-{}",
|
||||
"codewhale-tui-theme-command-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1780,7 +1780,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-theme-save-test-{}-{}",
|
||||
"codewhale-tui-theme-save-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1884,7 +1884,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-logout-test-{}-{}",
|
||||
"codewhale-tui-logout-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1933,7 +1933,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-statusline-persist-{}-{}",
|
||||
"codewhale-statusline-persist-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1964,7 +1964,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-statusline-preserve-{}-{}",
|
||||
"codewhale-statusline-preserve-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
|
||||
@@ -782,7 +782,7 @@ mod tests {
|
||||
let result = home_dashboard(&mut app);
|
||||
assert!(result.message.is_some());
|
||||
let msg = result.message.unwrap();
|
||||
assert!(msg.contains("DeepSeek TUI Home Dashboard"));
|
||||
assert!(msg.contains("codewhale Home Dashboard"));
|
||||
assert!(msg.contains("Model:"));
|
||||
assert!(msg.contains("Mode:"));
|
||||
assert!(msg.contains("Workspace:"));
|
||||
@@ -831,7 +831,7 @@ mod tests {
|
||||
!msg.lines()
|
||||
.any(|line| line.trim_start().starts_with("/set "))
|
||||
);
|
||||
assert!(!msg.contains("/deepseek"));
|
||||
assert!(!msg.contains("/codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -101,7 +101,7 @@ fn render_session_html(history_json: &str, model: &str, mode: &str) -> String {
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DeepSeek TUI Session Export</title>
|
||||
<title>codewhale Session Export</title>
|
||||
<style>
|
||||
body {{
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
@@ -119,14 +119,14 @@ fn render_session_html(history_json: &str, model: &str, mode: &str) -> String {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DeepSeek TUI Session</h1>
|
||||
<h1>codewhale Session</h1>
|
||||
<div class="meta">
|
||||
<strong>Model:</strong> {escaped_model} · <strong>Mode:</strong> {escaped_mode}<br>
|
||||
<strong>Exported:</strong> {timestamp}
|
||||
</div>
|
||||
<pre>{escaped_body}</pre>
|
||||
<div class="footer">
|
||||
Generated by DeepSeek TUI · https://github.com/Hmbown/DeepSeek-TUI
|
||||
Generated by codewhale · https://github.com/Hmbown/DeepSeek-TUI
|
||||
</div>
|
||||
</body>
|
||||
</html>"#,
|
||||
@@ -145,7 +145,7 @@ fn html_escape(s: &str) -> String {
|
||||
/// Write HTML to a secure temp file and keep it alive for upload.
|
||||
fn write_temp_html(html: &str) -> Result<tempfile::NamedTempFile, String> {
|
||||
let mut tmp = tempfile::Builder::new()
|
||||
.prefix("deepseek-share-")
|
||||
.prefix("codewhale-share-")
|
||||
.suffix(".html")
|
||||
.tempfile()
|
||||
.map_err(|e| format!("{e}"))?;
|
||||
@@ -164,7 +164,7 @@ async fn upload_gist(path: &Path) -> Result<String, String> {
|
||||
"--filename",
|
||||
"session-export.html",
|
||||
"--desc",
|
||||
"DeepSeek TUI Session Export",
|
||||
"codewhale Session Export",
|
||||
])
|
||||
.output()
|
||||
.await
|
||||
@@ -194,7 +194,7 @@ mod tests {
|
||||
assert!(html.contains("deepseek-v4-pro"));
|
||||
assert!(html.contains("agent"));
|
||||
assert!(html.contains("[{}]"));
|
||||
assert!(html.contains("DeepSeek TUI"));
|
||||
assert!(html.contains("codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,7 +18,7 @@ fn format_status(app: &App) -> String {
|
||||
let mut out = String::new();
|
||||
let (context_used, context_max, context_percent) = context_usage(app);
|
||||
|
||||
let _ = writeln!(out, "DeepSeek TUI Status");
|
||||
let _ = writeln!(out, "codewhale Status");
|
||||
let _ = writeln!(out, "===================");
|
||||
let _ = writeln!(out);
|
||||
push_row(&mut out, "Version:", env!("CARGO_PKG_VERSION"));
|
||||
@@ -227,7 +227,7 @@ mod tests {
|
||||
|
||||
let result = status(&mut app);
|
||||
let msg = result.message.expect("status message");
|
||||
assert!(msg.contains("DeepSeek TUI Status"));
|
||||
assert!(msg.contains("codewhale Status"));
|
||||
assert!(msg.contains("Provider:"));
|
||||
assert!(msg.contains("Model:"));
|
||||
assert!(msg.contains("Directory:"));
|
||||
|
||||
+73
-73
@@ -1,4 +1,4 @@
|
||||
//! Configuration loading and defaults for DeepSeek TUI.
|
||||
//! Configuration loading and defaults for codewhale.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
@@ -969,7 +969,7 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub hooks: Option<HooksConfig>,
|
||||
|
||||
/// Provider-specific credentials and defaults shared with the `deepseek` facade.
|
||||
/// Provider-specific credentials and defaults shared with the `codewhale` facade.
|
||||
#[serde(default)]
|
||||
pub providers: Option<ProvidersConfig>,
|
||||
|
||||
@@ -1024,7 +1024,7 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub subagents: Option<SubagentsConfig>,
|
||||
|
||||
/// Runtime API server tuning (`deepseek serve --http`). Currently only
|
||||
/// Runtime API server tuning (`codewhale serve --http`). Currently only
|
||||
/// hosts the CORS allow-list extension (whalescale#255 / #561). When the
|
||||
/// table is absent, the daemon ships with localhost:3000 / localhost:1420
|
||||
/// / tauri://localhost as the only allowed dev origins.
|
||||
@@ -1656,7 +1656,7 @@ impl Config {
|
||||
}
|
||||
|
||||
// 1. Config file (provider-scoped slot). This intentionally wins
|
||||
// over ambient env so `deepseek auth set` fixes stale shell exports.
|
||||
// over ambient env so `codewhale auth set` fixes stale shell exports.
|
||||
if let Some(configured) = self
|
||||
.provider_config_for(provider)
|
||||
.and_then(|provider| provider.api_key.clone())
|
||||
@@ -1683,7 +1683,7 @@ impl Config {
|
||||
\n\
|
||||
1. Get a key: https://platform.deepseek.com/api_keys\n\
|
||||
2. Save it (works in every folder, no OS prompts):\n\
|
||||
deepseek auth set --provider deepseek\n\
|
||||
codewhale auth set --provider deepseek\n\
|
||||
\n\
|
||||
Alternatives:\n\
|
||||
• export DEEPSEEK_API_KEY=<your-key> (current shell only;\n\
|
||||
@@ -1692,33 +1692,33 @@ impl Config {
|
||||
• api_key = \"<your-key>\" in ~/.deepseek/config.toml"
|
||||
),
|
||||
ApiProvider::NvidiaNim => anyhow::bail!(
|
||||
"NVIDIA NIM API key not found. Run 'deepseek auth set --provider nvidia-nim', \
|
||||
"NVIDIA NIM API key not found. Run 'codewhale auth set --provider nvidia-nim', \
|
||||
set NVIDIA_API_KEY/NVIDIA_NIM_API_KEY, or save api_key in ~/.deepseek/config.toml \
|
||||
with provider = \"nvidia-nim\"."
|
||||
),
|
||||
ApiProvider::Openai => anyhow::bail!(
|
||||
"OpenAI-compatible API key not found. Run 'deepseek auth set --provider openai', \
|
||||
"OpenAI-compatible API key not found. Run 'codewhale auth set --provider openai', \
|
||||
set OPENAI_API_KEY, or add [providers.openai] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
ApiProvider::Atlascloud => anyhow::bail!(
|
||||
"AtlasCloud API key not found. Run 'deepseek auth set --provider atlascloud', \
|
||||
"AtlasCloud API key not found. Run 'codewhale auth set --provider atlascloud', \
|
||||
set ATLASCLOUD_API_KEY, or add [providers.atlascloud] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
ApiProvider::WanjieArk => anyhow::bail!(
|
||||
"Wanjie Ark API key not found. Run 'deepseek auth set --provider wanjie-ark', \
|
||||
"Wanjie Ark API key not found. Run 'codewhale auth set --provider wanjie-ark', \
|
||||
set WANJIE_ARK_API_KEY/WANJIE_API_KEY/WANJIE_MAAS_API_KEY, or add \
|
||||
[providers.wanjie_ark] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
ApiProvider::Openrouter => anyhow::bail!(
|
||||
"OpenRouter API key not found. Run 'deepseek auth set --provider openrouter', \
|
||||
"OpenRouter API key not found. Run 'codewhale auth set --provider openrouter', \
|
||||
set OPENROUTER_API_KEY, or add [providers.openrouter] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
ApiProvider::Novita => anyhow::bail!(
|
||||
"Novita API key not found. Run 'deepseek auth set --provider novita', \
|
||||
"Novita API key not found. Run 'codewhale auth set --provider novita', \
|
||||
set NOVITA_API_KEY, or add [providers.novita] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
ApiProvider::Fireworks => anyhow::bail!(
|
||||
"Fireworks AI API key not found. Run 'deepseek auth set --provider fireworks', \
|
||||
"Fireworks AI API key not found. Run 'codewhale auth set --provider fireworks', \
|
||||
set FIREWORKS_API_KEY, or add [providers.fireworks] api_key in ~/.deepseek/config.toml."
|
||||
),
|
||||
// Self-hosted deployments commonly run without auth on localhost.
|
||||
@@ -2120,7 +2120,7 @@ fn resolve_load_config_path(path: Option<PathBuf>) -> Option<PathBuf> {
|
||||
|
||||
/// Create an inspectable config file on first interactive launch.
|
||||
///
|
||||
/// The file intentionally omits `api_key`; onboarding or `deepseek auth set`
|
||||
/// The file intentionally omits `api_key`; onboarding or `codewhale auth set`
|
||||
/// writes that field after the user supplies a key.
|
||||
pub fn ensure_config_file_exists(path: Option<PathBuf>) -> Result<Option<PathBuf>> {
|
||||
let config_path = path
|
||||
@@ -2133,9 +2133,9 @@ pub fn ensure_config_file_exists(path: Option<PathBuf>) -> Result<Option<PathBuf
|
||||
|
||||
ensure_parent_dir(&config_path)?;
|
||||
let content = format!(
|
||||
r#"# DeepSeek TUI Configuration
|
||||
r#"# codewhale Configuration
|
||||
# Get your API key from https://platform.deepseek.com
|
||||
# Save it with: deepseek auth set --provider deepseek
|
||||
# Save it with: codewhale auth set --provider deepseek
|
||||
|
||||
# Base URL (default: https://api.deepseek.com/beta)
|
||||
# Set https://api.deepseek.com to opt out of beta features.
|
||||
@@ -3128,7 +3128,7 @@ pub fn ensure_parent_dir(path: &Path) -> Result<()> {
|
||||
perms.set_mode(mode & !0o077);
|
||||
if let Err(err) = fs::set_permissions(parent, perms) {
|
||||
tracing::warn!(
|
||||
target: "deepseek::config",
|
||||
target: "codewhale::config",
|
||||
path = %parent.display(),
|
||||
error = %err,
|
||||
"could not tighten parent dir permissions; \
|
||||
@@ -3166,7 +3166,7 @@ fn write_config_file_secure(path: &Path, content: &str) -> Result<()> {
|
||||
// system's native ACL model is doing the access control.
|
||||
if let Err(err) = file.set_permissions(fs::Permissions::from_mode(0o600)) {
|
||||
tracing::warn!(
|
||||
target: "deepseek::config",
|
||||
target: "codewhale::config",
|
||||
path = %path.display(),
|
||||
error = %err,
|
||||
"could not enforce 0o600 on config file; filesystem may \
|
||||
@@ -3186,7 +3186,7 @@ fn write_config_file_secure(path: &Path, content: &str) -> Result<()> {
|
||||
/// the caller can show a confirmation message without leaking the key.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SavedCredential {
|
||||
/// Stored in **both** the OS keyring and the deepseek config file.
|
||||
/// Stored in **both** the OS keyring and the codewhale config file.
|
||||
/// This is the default outcome on platforms with a working keyring
|
||||
/// backend: writing both layers defeats the
|
||||
/// `keyring → env → config-file` resolution-order shadow that
|
||||
@@ -3201,7 +3201,7 @@ pub enum SavedCredential {
|
||||
/// Absolute path to the config file that was also updated.
|
||||
path: PathBuf,
|
||||
},
|
||||
/// Stored in the deepseek config file only. Fallback when no
|
||||
/// Stored in the codewhale config file only. Fallback when no
|
||||
/// keyring backend is reachable, or under `cfg(test)` so unit
|
||||
/// tests don't pollute the host keyring.
|
||||
ConfigFile(PathBuf),
|
||||
@@ -3324,7 +3324,7 @@ fn save_api_key_to_config_file(api_key: &str) -> Result<PathBuf> {
|
||||
} else {
|
||||
// Create new minimal config
|
||||
format!(
|
||||
r#"# DeepSeek TUI Configuration
|
||||
r#"# codewhale Configuration
|
||||
# Get your API key from https://platform.deepseek.com
|
||||
# Or set DEEPSEEK_API_KEY environment variable
|
||||
|
||||
@@ -4063,7 +4063,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-test-{}-{}",
|
||||
"codewhale-tui-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4099,7 +4099,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-first-run-config-{}-{}",
|
||||
"codewhale-tui-first-run-config-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4125,7 +4125,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-workspace-trust-{}-{}",
|
||||
"codewhale-tui-workspace-trust-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4161,7 +4161,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-existing-project-trust-{}-{}",
|
||||
"codewhale-tui-existing-project-trust-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4364,7 +4364,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-clear-{}-{}",
|
||||
"codewhale-tui-clear-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4397,7 +4397,7 @@ api_key = "old-openrouter-key"
|
||||
);
|
||||
assert!(
|
||||
!after.contains("old-provider-key"),
|
||||
"provider-scoped deepseek key must be stripped: {after}"
|
||||
"provider-scoped codewhale key must be stripped: {after}"
|
||||
);
|
||||
assert!(
|
||||
!after.contains("old-openrouter-key"),
|
||||
@@ -4420,7 +4420,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-override-{}-{}",
|
||||
"codewhale-tui-override-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4446,7 +4446,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-config-over-env-{}-{}",
|
||||
"codewhale-tui-config-over-env-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4471,7 +4471,7 @@ api_key = "old-openrouter-key"
|
||||
fn active_provider_detects_env_only_api_key() -> Result<()> {
|
||||
let _lock = lock_test_env();
|
||||
let temp_root =
|
||||
env::temp_dir().join(format!("deepseek-tui-env-only-key-{}", std::process::id()));
|
||||
env::temp_dir().join(format!("codewhale-tui-env-only-key-{}", std::process::id()));
|
||||
fs::create_dir_all(&temp_root)?;
|
||||
let _guard = EnvGuard::new(&temp_root);
|
||||
|
||||
@@ -4501,7 +4501,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-sentinel-{}-{}",
|
||||
"codewhale-tui-sentinel-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4529,7 +4529,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-tilde-test-{}-{}",
|
||||
"codewhale-tui-tilde-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4558,7 +4558,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-load-tilde-test-{}-{}",
|
||||
"codewhale-tui-load-tilde-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4587,7 +4587,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-load-fallback-test-{}-{}",
|
||||
"codewhale-tui-load-fallback-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4646,7 +4646,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-api-key-test-{}-{}",
|
||||
"codewhale-tui-api-key-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4693,7 +4693,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-empty-key-{}-{}",
|
||||
"codewhale-tui-empty-key-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4726,7 +4726,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-env-key-not-config-{}-{}",
|
||||
"codewhale-tui-env-key-not-config-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -4839,7 +4839,7 @@ api_key = "old-openrouter-key"
|
||||
#[test]
|
||||
fn normalize_model_name_rejects_invalid_or_non_deepseek_ids() {
|
||||
assert!(normalize_model_name("gpt-4o").is_none());
|
||||
assert!(normalize_model_name("deepseek v4").is_none());
|
||||
assert!(normalize_model_name("codewhale v4").is_none());
|
||||
assert!(normalize_model_name("").is_none());
|
||||
}
|
||||
|
||||
@@ -4980,7 +4980,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-model-env-test-{}-{}",
|
||||
"codewhale-tui-model-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5009,7 +5009,7 @@ api_key = "old-openrouter-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-http-headers-root-{}-{}",
|
||||
"codewhale-tui-http-headers-root-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5073,7 +5073,7 @@ http_headers = { "X-Model-Provider-Id" = "tongyi" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-http-headers-env-{}-{}",
|
||||
"codewhale-tui-http-headers-env-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5127,7 +5127,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-model-alias-test-{}-{}",
|
||||
"codewhale-tui-nim-model-alias-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5171,7 +5171,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-env-test-{}-{}",
|
||||
"codewhale-tui-nim-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5200,7 +5200,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-base-url-alias-test-{}-{}",
|
||||
"codewhale-tui-nim-base-url-alias-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5227,7 +5227,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-forwarded-base-url-test-{}-{}",
|
||||
"codewhale-tui-nim-forwarded-base-url-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5285,7 +5285,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-atlascloud-env-test-{}-{}",
|
||||
"codewhale-tui-atlascloud-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5329,7 +5329,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-wanjie-env-test-{}-{}",
|
||||
"codewhale-tui-wanjie-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5359,7 +5359,7 @@ http_headers = { "X-Model-Provider-Id" = "from-file" }
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-wanjie-table-{}-{}",
|
||||
"codewhale-tui-wanjie-table-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5398,7 +5398,7 @@ model = "account-model-id"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-openai-table-{}-{}",
|
||||
"codewhale-tui-openai-table-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5429,7 +5429,7 @@ model = "glm-5"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Regression for issue #1714: `deepseek --provider openai --model
|
||||
// Regression for issue #1714: `codewhale --provider openai --model
|
||||
// MiniMax-M2.7` forwards the choice via DEEPSEEK_MODEL (never
|
||||
// OPENAI_MODEL) and uses the DEFAULT base_url. The explicit custom model
|
||||
// must pass through verbatim instead of silently becoming a
|
||||
@@ -5442,7 +5442,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-1714-passthrough-{}-{}",
|
||||
"codewhale-tui-1714-passthrough-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5495,7 +5495,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-openai-env-test-{}-{}",
|
||||
"codewhale-tui-openai-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5529,7 +5529,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-openai-forwarded-base-url-test-{}-{}",
|
||||
"codewhale-tui-openai-forwarded-base-url-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5563,7 +5563,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-or-defaults-{}-{}",
|
||||
"codewhale-tui-or-defaults-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5589,7 +5589,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-novita-defaults-{}-{}",
|
||||
"codewhale-tui-novita-defaults-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5615,7 +5615,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-fireworks-defaults-{}-{}",
|
||||
"codewhale-tui-fireworks-defaults-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5641,7 +5641,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-sglang-defaults-{}-{}",
|
||||
"codewhale-tui-sglang-defaults-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5669,7 +5669,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-ollama-defaults-{}-{}",
|
||||
"codewhale-tui-ollama-defaults-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5697,7 +5697,7 @@ model = "glm-5"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-ollama-model-test-{}-{}",
|
||||
"codewhale-tui-ollama-model-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5731,7 +5731,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-self-hosted-base-url-test-{}-{}",
|
||||
"codewhale-tui-self-hosted-base-url-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5766,7 +5766,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-ollama-env-test-{}-{}",
|
||||
"codewhale-tui-ollama-env-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5795,7 +5795,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-or-env-key-{}-{}",
|
||||
"codewhale-tui-or-env-key-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5822,7 +5822,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-novita-env-key-{}-{}",
|
||||
"codewhale-tui-novita-env-key-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5849,7 +5849,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-or-base-url-{}-{}",
|
||||
"codewhale-tui-or-base-url-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5876,7 +5876,7 @@ model = "qwen2.5-coder:7b"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-or-table-{}-{}",
|
||||
"codewhale-tui-or-table-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5910,7 +5910,7 @@ base_url = "https://or-table.example/v1"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-or-custom-model-{}-{}",
|
||||
"codewhale-tui-or-custom-model-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5946,7 +5946,7 @@ model = "DeepSeek-V4-Pro"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-novita-table-{}-{}",
|
||||
"codewhale-tui-novita-table-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -5979,7 +5979,7 @@ api_key = "novita-table-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-has-key-{}-{}",
|
||||
"codewhale-tui-has-key-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6036,7 +6036,7 @@ api_key = "novita-table-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-has-key-cn-{}-{}",
|
||||
"codewhale-tui-has-key-cn-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6073,7 +6073,7 @@ api_key = "novita-table-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-save-key-or-{}-{}",
|
||||
"codewhale-tui-save-key-or-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6161,7 +6161,7 @@ api_key = "novita-table-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-save-key-cn-{}-{}",
|
||||
"codewhale-tui-save-key-cn-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6188,7 +6188,7 @@ api_key = "novita-table-key"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-provider-table-test-{}-{}",
|
||||
"codewhale-tui-nim-provider-table-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6227,7 +6227,7 @@ model = "deepseek-v4-pro"
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let temp_root = env::temp_dir().join(format!(
|
||||
"deepseek-tui-nim-root-key-precedence-test-{}-{}",
|
||||
"codewhale-tui-nim-root-key-precedence-test-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -6238,7 +6238,7 @@ model = "deepseek-v4-pro"
|
||||
ensure_parent_dir(&config_path)?;
|
||||
fs::write(
|
||||
&config_path,
|
||||
r#"api_key = "deepseek-root-key"
|
||||
r#"api_key = "codewhale-root-key"
|
||||
provider = "nvidia-nim"
|
||||
|
||||
[providers.nvidia_nim]
|
||||
|
||||
@@ -283,7 +283,7 @@ pub enum StatusItemValue {
|
||||
pub fn parse_mode(arg: Option<&str>) -> Result<ConfigUiMode, String> {
|
||||
let raw = arg.unwrap_or("").trim();
|
||||
// Bare `/config` opens the legacy native modal — it matches the rest
|
||||
// of the deepseek-tui navy chrome out of the box. Power users can
|
||||
// of the codewhale-tui navy chrome out of the box. Power users can
|
||||
// opt into the schemaui-driven editor with `/config tui`, or the
|
||||
// browser surface with `/config web` (web feature only).
|
||||
if raw.is_empty() || raw.eq_ignore_ascii_case("native") {
|
||||
@@ -348,7 +348,7 @@ pub fn build_document(app: &App, config: &Config) -> Result<ConfigUiDocument> {
|
||||
|
||||
pub fn build_schema() -> Value {
|
||||
let mut schema = serde_json::to_value(schema_for!(ConfigUiDocument)).expect("config ui schema");
|
||||
schema["title"] = Value::String("DeepSeek TUI Config".to_string());
|
||||
schema["title"] = Value::String("codewhale Config".to_string());
|
||||
schema["description"] =
|
||||
Value::String("Edit runtime and persisted TUI configuration.".to_string());
|
||||
schema
|
||||
@@ -359,7 +359,7 @@ pub fn run_tui_editor(app: &App, config: &Config) -> Result<ConfigUiDocument> {
|
||||
let document = build_document(app, config)?;
|
||||
let value = SchemaUI::new(serde_json::to_value(document.clone())?)
|
||||
.with_schema(build_schema())
|
||||
.with_title("DeepSeek TUI Config")
|
||||
.with_title("codewhale Config")
|
||||
.with_description("Edit persisted settings and live runtime knobs.")
|
||||
.run(FrontendOptions::Tui(
|
||||
UiOptions::default()
|
||||
@@ -377,7 +377,7 @@ pub async fn start_web_editor(app: &App, config: &Config) -> Result<WebConfigSes
|
||||
let initial = serde_json::to_value(build_document(app, config)?)?;
|
||||
let session = WebSessionBuilder::new(build_schema())
|
||||
.with_initial_data(initial)
|
||||
.with_title("DeepSeek TUI Config")
|
||||
.with_title("codewhale Config")
|
||||
.with_description("Save updates the browser draft. Exit commits changes back to the TUI.")
|
||||
.build()?;
|
||||
let bound = bind_session(session, ServeOptions::default()).await?;
|
||||
@@ -1082,7 +1082,7 @@ mod tests {
|
||||
.expect("clock")
|
||||
.as_nanos();
|
||||
let temp_root = std::env::temp_dir().join(format!(
|
||||
"deepseek-config-ui-cost-currency-{}-{}",
|
||||
"codewhale-config-ui-cost-currency-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1126,7 +1126,7 @@ cost_currency = "cny"
|
||||
.expect("clock")
|
||||
.as_nanos();
|
||||
let temp_root = std::env::temp_dir().join(format!(
|
||||
"deepseek-config-ui-background-color-{}-{}",
|
||||
"codewhale-config-ui-background-color-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
@@ -1208,7 +1208,7 @@ background_color = "#1A1B26"
|
||||
.expect("clock")
|
||||
.as_nanos();
|
||||
let temp_root = std::env::temp_dir().join(format!(
|
||||
"deepseek-config-ui-session-only-{}-{}",
|
||||
"codewhale-config-ui-session-only-{}-{}",
|
||||
std::process::id(),
|
||||
nanos
|
||||
));
|
||||
|
||||
@@ -298,7 +298,7 @@ pub struct Engine {
|
||||
/// can fan completion events back into the engine.
|
||||
tx_subagent_completion: mpsc::UnboundedSender<SubAgentCompletion>,
|
||||
/// Receiver paired with `tx_subagent_completion`. Drained at the
|
||||
/// turn-loop's empty-tool_uses branch to surface `<deepseek:subagent.done>`
|
||||
/// turn-loop's empty-tool_uses branch to surface `<codewhale:subagent.done>`
|
||||
/// sentinels into the parent's transcript before deciding to end the turn.
|
||||
pub(super) rx_subagent_completion: mpsc::UnboundedReceiver<SubAgentCompletion>,
|
||||
cancel_token: CancellationToken,
|
||||
@@ -378,8 +378,8 @@ impl Engine {
|
||||
|
||||
Some(format!(
|
||||
"The rejected key came from {env_var}; no saved config key is present.\n\
|
||||
Run `deepseek auth status` to inspect credential sources, then \
|
||||
`deepseek auth set --provider {provider}` to save a valid key in ~/.deepseek/config.toml, \
|
||||
Run `codewhale auth status` to inspect credential sources, then \
|
||||
`codewhale auth set --provider {provider}` to save a valid key in ~/.deepseek/config.toml, \
|
||||
or remove the stale export and open a fresh shell.",
|
||||
provider = provider.as_str()
|
||||
))
|
||||
|
||||
@@ -88,7 +88,7 @@ pub(super) fn should_transparently_retry_stream(
|
||||
|
||||
pub(crate) const TOOL_CALL_START_MARKERS: [&str; 5] = [
|
||||
"[TOOL_CALL]",
|
||||
"<deepseek:tool_call",
|
||||
"<codewhale:tool_call",
|
||||
"<tool_call",
|
||||
"<invoke ",
|
||||
"<function_calls>",
|
||||
@@ -96,7 +96,7 @@ pub(crate) const TOOL_CALL_START_MARKERS: [&str; 5] = [
|
||||
|
||||
pub(crate) const TOOL_CALL_END_MARKERS: [&str; 5] = [
|
||||
"[/TOOL_CALL]",
|
||||
"</deepseek:tool_call>",
|
||||
"</codewhale:tool_call>",
|
||||
"</tool_call>",
|
||||
"</invoke>",
|
||||
"</function_calls>",
|
||||
|
||||
@@ -94,8 +94,8 @@ fn env_only_auth_error_gets_recovery_hint() {
|
||||
|
||||
assert!(message.contains("DEEPSEEK_API_KEY"));
|
||||
assert!(message.contains("no saved config key is present"));
|
||||
assert!(message.contains("deepseek auth status"));
|
||||
assert!(message.contains("deepseek auth set --provider deepseek"));
|
||||
assert!(message.contains("codewhale auth status"));
|
||||
assert!(message.contains("codewhale auth set --provider deepseek"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1874,7 +1874,7 @@ fn filter_tool_call_delta_strips_bracket_marker() {
|
||||
fn filter_tool_call_delta_strips_deepseek_xml_marker() {
|
||||
let mut in_block = false;
|
||||
let visible = filter_tool_call_delta(
|
||||
"before <deepseek:tool_call name=\"x\">payload</deepseek:tool_call> after",
|
||||
"before <codewhale:tool_call name=\"x\">payload</codewhale:tool_call> after",
|
||||
&mut in_block,
|
||||
);
|
||||
assert!(!in_block);
|
||||
|
||||
@@ -924,7 +924,7 @@ impl Engine {
|
||||
// streaming with no tool calls — but if it has direct children
|
||||
// still running (or completions queued from children that
|
||||
// finished while we were inferring), surface their
|
||||
// `<deepseek:subagent.done>` sentinels into the transcript and
|
||||
// `<codewhale:subagent.done>` sentinels into the transcript and
|
||||
// resume instead of ending the turn. This fulfils the contract
|
||||
// already documented in `prompts/base.md`: the parent is
|
||||
// promised it'll see the sentinel when a child finishes.
|
||||
@@ -2010,13 +2010,13 @@ fn subagent_completion_runtime_message(payload: &str) -> Message {
|
||||
role: "system".to_string(),
|
||||
content: vec![ContentBlock::Text {
|
||||
text: format!(
|
||||
"<deepseek:runtime_event kind=\"subagent_completion\" visibility=\"internal\">\n\
|
||||
"<codewhale:runtime_event kind=\"subagent_completion\" visibility=\"internal\">\n\
|
||||
This is an internal runtime event, not user input. Use the sub-agent completion \
|
||||
data below to continue coordinating the current task. Do not tell the user they \
|
||||
pasted sentinels, do not explain the sentinel protocol, and do not quote the raw \
|
||||
XML unless the user explicitly asks to debug sub-agent internals.\n\n\
|
||||
{payload}\n\
|
||||
</deepseek:runtime_event>"
|
||||
</codewhale:runtime_event>"
|
||||
),
|
||||
cache_control: None,
|
||||
}],
|
||||
@@ -2109,7 +2109,7 @@ mod tests {
|
||||
#[test]
|
||||
fn subagent_completion_handoff_is_internal_system_message() {
|
||||
let message = subagent_completion_runtime_message(
|
||||
"Build passed\n<deepseek:subagent.done>{\"agent_id\":\"agent_a\"}</deepseek:subagent.done>",
|
||||
"Build passed\n<codewhale:subagent.done>{\"agent_id\":\"agent_a\"}</codewhale:subagent.done>",
|
||||
);
|
||||
|
||||
assert_eq!(message.role, "system");
|
||||
@@ -2119,7 +2119,7 @@ mod tests {
|
||||
};
|
||||
assert!(text.contains("internal runtime event, not user input"));
|
||||
assert!(text.contains("Do not tell the user they pasted sentinels"));
|
||||
assert!(text.contains("<deepseek:subagent.done>"));
|
||||
assert!(text.contains("<codewhale:subagent.done>"));
|
||||
assert!(text.contains("Build passed"));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
//!
|
||||
//! Or XML-style format:
|
||||
//! ```text
|
||||
//! <deepseek:tool_call>
|
||||
//! <codewhale:tool_call>
|
||||
//! <invoke name="tool_name">
|
||||
//! <parameter name="arg">value</parameter>
|
||||
//! </invoke>
|
||||
//! </deepseek:tool_call>
|
||||
//! </codewhale:tool_call>
|
||||
//! ```
|
||||
//!
|
||||
//! This module parses these text patterns into structured tool calls.
|
||||
@@ -60,8 +60,8 @@ fn get_tool_call_regex() -> &'static Regex {
|
||||
|
||||
fn get_xml_tool_call_regex() -> &'static Regex {
|
||||
XML_TOOL_CALL_REGEX.get_or_init(|| {
|
||||
// Match <deepseek:tool_call>...</deepseek:tool_call> or similar XML patterns
|
||||
Regex::new(r"(?s)<(?:deepseek:)?tool_call[^>]*>\s*(.*?)\s*</(?:deepseek:)?tool_call>")
|
||||
// Match <codewhale:tool_call>...</codewhale:tool_call> or similar XML patterns
|
||||
Regex::new(r"(?s)<(?:codewhale:)?tool_call[^>]*>\s*(.*?)\s*</(?:codewhale:)?tool_call>")
|
||||
.expect("XML tool_call regex pattern is valid")
|
||||
})
|
||||
}
|
||||
@@ -108,7 +108,7 @@ pub fn parse_tool_calls(text: &str) -> ParseResult {
|
||||
clean_text = clean_text.replace(full_match, "");
|
||||
}
|
||||
|
||||
// Parse XML-style <deepseek:tool_call> or <tool_call> format
|
||||
// Parse XML-style <codewhale:tool_call> or <tool_call> format
|
||||
let xml_regex = get_xml_tool_call_regex();
|
||||
for cap in xml_regex.captures_iter(text) {
|
||||
let (Some(full_match), Some(inner)) = (cap.get(0), cap.get(1)) else {
|
||||
@@ -443,7 +443,7 @@ fn extract_json_object(text: &str) -> Option<Value> {
|
||||
/// Check if text contains tool call markers (either format).
|
||||
pub fn has_tool_call_markers(text: &str) -> bool {
|
||||
text.contains("[TOOL_CALL]")
|
||||
|| text.contains("<deepseek:tool_call")
|
||||
|| text.contains("<codewhale:tool_call")
|
||||
|| text.contains("<tool_call")
|
||||
|| text.contains("<invoke ")
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ mod tests {
|
||||
fn probe_executable_returns_false_for_unknown_binary() {
|
||||
// Pick a name we're confident isn't on any developer's PATH.
|
||||
// If this ever starts failing locally, rename it.
|
||||
assert!(!probe_executable("deepseek-tui-imaginary-binary-xyz123"));
|
||||
assert!(!probe_executable("codewhale-tui-imaginary-binary-xyz123"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -952,7 +952,7 @@ fn english(id: MessageId) -> &'static str {
|
||||
MessageId::CmdNoteDescription => "Add, list, edit, or remove workspace notes",
|
||||
MessageId::CmdThemeDescription => "Switch theme or open the theme picker",
|
||||
MessageId::CmdProviderDescription => {
|
||||
"Switch or view the active LLM backend (deepseek | nvidia-nim | ollama)"
|
||||
"Switch or view the active LLM backend (codewhale | nvidia-nim | ollama)"
|
||||
}
|
||||
MessageId::CmdQueueDescription => "View or edit queued messages",
|
||||
MessageId::CmdRecallDescription => "Search prior cycle archives (BM25 over message text)",
|
||||
@@ -1131,7 +1131,7 @@ fn english(id: MessageId) -> &'static str {
|
||||
MessageId::LinksTip => "Tip: API keys are available in the dashboard console.",
|
||||
MessageId::SubagentsFetching => "Fetching sub-agent status...",
|
||||
MessageId::HelpUnknownCommand => "Unknown command: {topic}",
|
||||
MessageId::HomeDashboardTitle => "DeepSeek TUI Home Dashboard",
|
||||
MessageId::HomeDashboardTitle => "codewhale Home Dashboard",
|
||||
MessageId::HomeModel => "Model:",
|
||||
MessageId::HomeMode => "Mode:",
|
||||
MessageId::HomeWorkspace => "Workspace:",
|
||||
@@ -1336,7 +1336,7 @@ fn japanese(id: MessageId) -> Option<&'static str> {
|
||||
"テーマを切り替え(ダーク/ライト/グレースケール/システム)"
|
||||
}
|
||||
MessageId::CmdProviderDescription => {
|
||||
"現在の LLM バックエンドを切り替え・確認(deepseek | nvidia-nim | ollama)"
|
||||
"現在の LLM バックエンドを切り替え・確認(codewhale | nvidia-nim | ollama)"
|
||||
}
|
||||
MessageId::CmdQueueDescription => "キューされたメッセージを確認・編集",
|
||||
MessageId::CmdRecallDescription => {
|
||||
@@ -1516,7 +1516,7 @@ fn japanese(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::LinksTip => "ヒント: API キーはダッシュボードコンソールで取得できます。",
|
||||
MessageId::SubagentsFetching => "サブエージェントの状態を取得中...",
|
||||
MessageId::HelpUnknownCommand => "不明なコマンド: {topic}",
|
||||
MessageId::HomeDashboardTitle => "DeepSeek TUI ホームダッシュボード",
|
||||
MessageId::HomeDashboardTitle => "codewhale ホームダッシュボード",
|
||||
MessageId::HomeModel => "モデル:",
|
||||
MessageId::HomeMode => "モード:",
|
||||
MessageId::HomeWorkspace => "ワークスペース:",
|
||||
@@ -1679,7 +1679,7 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::CmdNoteDescription => "添加、列出、编辑或删除工作区笔记",
|
||||
MessageId::CmdThemeDescription => "切换主题:深色、浅色、灰度或系统",
|
||||
MessageId::CmdProviderDescription => {
|
||||
"切换或查看当前 LLM 后端(deepseek | nvidia-nim | ollama)"
|
||||
"切换或查看当前 LLM 后端(codewhale | nvidia-nim | ollama)"
|
||||
}
|
||||
MessageId::CmdQueueDescription => "查看或编辑已排队的消息",
|
||||
MessageId::CmdRecallDescription => "搜索此前的循环归档(基于消息文本的 BM25 检索)",
|
||||
@@ -1833,7 +1833,7 @@ fn chinese_simplified(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::LinksTip => "提示:API 密钥可在控制台中获取。",
|
||||
MessageId::SubagentsFetching => "正在获取子代理状态...",
|
||||
MessageId::HelpUnknownCommand => "未知命令:{topic}",
|
||||
MessageId::HomeDashboardTitle => "DeepSeek TUI 主面板",
|
||||
MessageId::HomeDashboardTitle => "codewhale 主面板",
|
||||
MessageId::HomeModel => "模型:",
|
||||
MessageId::HomeMode => "模式:",
|
||||
MessageId::HomeWorkspace => "工作区:",
|
||||
@@ -2006,7 +2006,7 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::CmdNoteDescription => "Adicionar, listar, editar ou remover notas do workspace",
|
||||
MessageId::CmdThemeDescription => "Alternar tema: escuro, claro, tons de cinza ou sistema",
|
||||
MessageId::CmdProviderDescription => {
|
||||
"Trocar ou exibir o backend LLM ativo (deepseek | nvidia-nim | ollama)"
|
||||
"Trocar ou exibir o backend LLM ativo (codewhale | nvidia-nim | ollama)"
|
||||
}
|
||||
MessageId::CmdQueueDescription => "Ver ou editar mensagens enfileiradas",
|
||||
MessageId::CmdRecallDescription => {
|
||||
@@ -2198,7 +2198,7 @@ fn portuguese_brazil(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::LinksTip => "Dica: chaves de API estão disponíveis no console do painel.",
|
||||
MessageId::SubagentsFetching => "Buscando status dos sub-agentes...",
|
||||
MessageId::HelpUnknownCommand => "Comando desconhecido: {topic}",
|
||||
MessageId::HomeDashboardTitle => "Painel Inicial do DeepSeek TUI",
|
||||
MessageId::HomeDashboardTitle => "Painel Inicial do codewhale",
|
||||
MessageId::HomeModel => "Modelo:",
|
||||
MessageId::HomeMode => "Modo:",
|
||||
MessageId::HomeWorkspace => "Workspace:",
|
||||
@@ -2393,7 +2393,7 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::CmdNoteDescription => "Agregar nota al archivo persistente (.deepseek/notes.md)",
|
||||
MessageId::CmdThemeDescription => "Alternar entre tema claro y oscuro",
|
||||
MessageId::CmdProviderDescription => {
|
||||
"Cambiar o mostrar el backend LLM activo (deepseek | nvidia-nim | ollama)"
|
||||
"Cambiar o mostrar el backend LLM activo (codewhale | nvidia-nim | ollama)"
|
||||
}
|
||||
MessageId::CmdQueueDescription => "Ver o editar mensajes en cola",
|
||||
MessageId::CmdRecallDescription => {
|
||||
@@ -2591,7 +2591,7 @@ fn spanish_latin_america(id: MessageId) -> Option<&'static str> {
|
||||
MessageId::LinksTip => "Tip: las claves de API están disponibles en la consola del panel.",
|
||||
MessageId::SubagentsFetching => "Obteniendo estado de los sub-agentes...",
|
||||
MessageId::HelpUnknownCommand => "Comando desconocido: {topic}",
|
||||
MessageId::HomeDashboardTitle => "Panel Inicial de DeepSeek TUI",
|
||||
MessageId::HomeDashboardTitle => "Panel Inicial de codewhale",
|
||||
MessageId::HomeModel => "Modelo:",
|
||||
MessageId::HomeMode => "Modo:",
|
||||
MessageId::HomeWorkspace => "Workspace:",
|
||||
|
||||
@@ -67,7 +67,7 @@ mod tests {
|
||||
#[test]
|
||||
fn log_value_parser_accepts_common_rust_log_directives() {
|
||||
assert!(log_value_enables_verbose("debug"));
|
||||
assert!(log_value_enables_verbose("deepseek_cli=debug"));
|
||||
assert!(log_value_enables_verbose("codewhale_cli=debug"));
|
||||
assert!(log_value_enables_verbose(
|
||||
"warn,codewhale_tui::client=trace"
|
||||
));
|
||||
|
||||
+93
-93
@@ -101,12 +101,12 @@ fn configure_windows_console_utf8() {}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "deepseek-tui",
|
||||
bin_name = "deepseek-tui",
|
||||
name = "codewhale-tui",
|
||||
bin_name = "codewhale-tui",
|
||||
author,
|
||||
version = env!("DEEPSEEK_BUILD_VERSION"),
|
||||
about = "DeepSeek TUI/CLI for DeepSeek models",
|
||||
long_about = "Terminal-native TUI and CLI for DeepSeek models.\n\nRun 'deepseek' to start.\n\nNot affiliated with DeepSeek Inc."
|
||||
about = "codewhale/CLI for DeepSeek models",
|
||||
long_about = "Terminal-native TUI and CLI for DeepSeek models.\n\nRun 'codewhale' to start.\n\nNot affiliated with DeepSeek Inc."
|
||||
)]
|
||||
struct Cli {
|
||||
/// Subcommand to run
|
||||
@@ -372,7 +372,7 @@ fn resolve_exec_resume_session_id(args: &ExecArgs, workspace: &Path) -> Result<O
|
||||
latest_session_id_for_workspace(workspace)?.map_or_else(
|
||||
|| {
|
||||
bail!(
|
||||
"No saved sessions found for workspace {}. Use `deepseek sessions` to list sessions, or pass `deepseek exec --resume <SESSION_ID> ...`.",
|
||||
"No saved sessions found for workspace {}. Use `codewhale sessions` to list sessions, or pass `codewhale exec --resume <SESSION_ID> ...`.",
|
||||
workspace.display()
|
||||
)
|
||||
},
|
||||
@@ -590,15 +590,15 @@ enum McpCommand {
|
||||
Validate,
|
||||
/// Register this DeepSeek binary as a local MCP stdio server.
|
||||
///
|
||||
/// This adds a config entry that runs `deepseek serve --mcp` (stdio protocol).
|
||||
/// For the HTTP/SSE runtime API, use `deepseek serve --http` directly instead.
|
||||
/// This adds a config entry that runs `codewhale serve --mcp` (stdio protocol).
|
||||
/// For the HTTP/SSE runtime API, use `codewhale serve --http` directly instead.
|
||||
#[command(
|
||||
name = "add-self",
|
||||
long_about = "Register this DeepSeek binary as a local MCP stdio server.\n\nAdds a config entry to ~/.deepseek/mcp.json that launches `deepseek serve --mcp`\nvia the stdio transport. Other DeepSeek sessions (or any MCP client) can then\ndiscover and call tools exposed by this server.\n\nUse `deepseek serve --http` instead if you need the HTTP/SSE runtime API."
|
||||
long_about = "Register this DeepSeek binary as a local MCP stdio server.\n\nAdds a config entry to ~/.deepseek/mcp.json that launches `codewhale serve --mcp`\nvia the stdio transport. Other DeepSeek sessions (or any MCP client) can then\ndiscover and call tools exposed by this server.\n\nUse `codewhale serve --http` instead if you need the HTTP/SSE runtime API."
|
||||
)]
|
||||
AddSelf {
|
||||
/// Server name in mcp.json (default: "deepseek")
|
||||
#[arg(long, default_value = "deepseek")]
|
||||
/// Server name in mcp.json (default: "codewhale")
|
||||
#[arg(long, default_value = "codewhale")]
|
||||
name: String,
|
||||
/// Workspace directory for the MCP server
|
||||
#[arg(long)]
|
||||
@@ -894,7 +894,7 @@ async fn main() -> Result<()> {
|
||||
return run_one_shot(&config, &model, &prompt).await;
|
||||
}
|
||||
|
||||
// Handle session resume. Plain `deepseek` starts fresh: interrupted
|
||||
// Handle session resume. Plain `codewhale` starts fresh: interrupted
|
||||
// snapshots are preserved for explicit resume, but never auto-attached.
|
||||
let resume_session_id = if cli.continue_session {
|
||||
let workspace = resolve_workspace(&cli);
|
||||
@@ -1087,7 +1087,7 @@ fn init_skills_dir(skills_dir: &Path, force: bool) -> Result<(PathBuf, WriteStat
|
||||
fn tools_readme_template() -> &'static str {
|
||||
"# Local tools\n\n\
|
||||
Drop self-describing scripts here so they can be discovered by\n\
|
||||
`deepseek-tui setup --status` and surfaced in `deepseek-tui doctor`.\n\n\
|
||||
`codewhale-tui setup --status` and surfaced in `codewhale-tui doctor`.\n\n\
|
||||
Each script should start with a frontmatter-style header so the\n\
|
||||
description is visible without executing the file:\n\n\
|
||||
```\n\
|
||||
@@ -1105,7 +1105,7 @@ fn tools_example_script() -> &'static str {
|
||||
# name: example\n\
|
||||
# description: Print a confirmation that local tool discovery works\n\
|
||||
# usage: example [name]\n\
|
||||
printf 'deepseek-tui local tool ok: %s\\n' \"${1:-world}\"\n"
|
||||
printf 'codewhale-tui local tool ok: %s\\n' \"${1:-world}\"\n"
|
||||
}
|
||||
|
||||
fn init_tools_dir(tools_dir: &Path, force: bool) -> Result<(PathBuf, WriteStatus, WriteStatus)> {
|
||||
@@ -1166,7 +1166,7 @@ fn init_plugins_dir(
|
||||
Ok((readme_path, example_path, readme_status, example_status))
|
||||
}
|
||||
|
||||
/// Resolve the user-supplied CORS origins for `deepseek serve --http`.
|
||||
/// Resolve the user-supplied CORS origins for `codewhale serve --http`.
|
||||
///
|
||||
/// Sources, in priority order (later sources extend earlier ones):
|
||||
/// 1. `--cors-origin URL` flags (repeatable)
|
||||
@@ -1291,7 +1291,9 @@ fn run_setup(config: &Config, workspace: &Path, args: SetupArgs) -> Result<()> {
|
||||
println!(" · MCP config already exists at {}", mcp_path.display());
|
||||
}
|
||||
}
|
||||
println!(" Next: edit the file, then run `deepseek mcp list` or `deepseek mcp tools`.");
|
||||
println!(
|
||||
" Next: edit the file, then run `codewhale mcp list` or `codewhale mcp tools`."
|
||||
);
|
||||
}
|
||||
|
||||
if run_skills {
|
||||
@@ -1463,45 +1465,45 @@ fn run_setup_status(config: &Config, workspace: &Path) -> Result<()> {
|
||||
let (env_var, login_hint) = match config.api_provider() {
|
||||
crate::config::ApiProvider::NvidiaNim => (
|
||||
"NVIDIA_API_KEY",
|
||||
"deepseek auth set --provider nvidia-nim --api-key \"...\"",
|
||||
"codewhale auth set --provider nvidia-nim --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Openai => (
|
||||
"OPENAI_API_KEY",
|
||||
"deepseek auth set --provider openai --api-key \"...\"",
|
||||
"codewhale auth set --provider openai --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Atlascloud => (
|
||||
"ATLASCLOUD_API_KEY",
|
||||
"deepseek auth set --provider atlascloud --api-key \"...\"",
|
||||
"codewhale auth set --provider atlascloud --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::WanjieArk => (
|
||||
"WANJIE_ARK_API_KEY",
|
||||
"deepseek auth set --provider wanjie-ark --api-key \"...\"",
|
||||
"codewhale auth set --provider wanjie-ark --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Openrouter => (
|
||||
"OPENROUTER_API_KEY",
|
||||
"deepseek auth set --provider openrouter --api-key \"...\"",
|
||||
"codewhale auth set --provider openrouter --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Novita => (
|
||||
"NOVITA_API_KEY",
|
||||
"deepseek auth set --provider novita --api-key \"...\"",
|
||||
"codewhale auth set --provider novita --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Fireworks => (
|
||||
"FIREWORKS_API_KEY",
|
||||
"deepseek auth set --provider fireworks --api-key \"...\"",
|
||||
"codewhale auth set --provider fireworks --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Sglang => (
|
||||
"SGLANG_API_KEY",
|
||||
"deepseek auth set --provider sglang --api-key \"...\"",
|
||||
"codewhale auth set --provider sglang --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Vllm => (
|
||||
"VLLM_API_KEY",
|
||||
"deepseek auth set --provider vllm --api-key \"...\"",
|
||||
"codewhale auth set --provider vllm --api-key \"...\"",
|
||||
),
|
||||
crate::config::ApiProvider::Ollama => {
|
||||
("OLLAMA_API_KEY", "deepseek auth set --provider ollama")
|
||||
("OLLAMA_API_KEY", "codewhale auth set --provider ollama")
|
||||
}
|
||||
crate::config::ApiProvider::Deepseek | crate::config::ApiProvider::DeepseekCN => {
|
||||
("DEEPSEEK_API_KEY", "deepseek auth set --provider deepseek")
|
||||
("DEEPSEEK_API_KEY", "codewhale auth set --provider deepseek")
|
||||
}
|
||||
};
|
||||
println!(
|
||||
@@ -1596,7 +1598,7 @@ fn run_setup_status(config: &Config, workspace: &Path) -> Result<()> {
|
||||
println!(" {} {}", "·".dimmed(), dotenv_status_line(workspace));
|
||||
|
||||
println!();
|
||||
println!("Run `deepseek doctor --json` for a machine-readable check.");
|
||||
println!("Run `codewhale doctor --json` for a machine-readable check.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1664,16 +1666,14 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
"DeepSeek TUI Doctor"
|
||||
.truecolor(blue_r, blue_g, blue_b)
|
||||
.bold()
|
||||
"codewhale Doctor".truecolor(blue_r, blue_g, blue_b).bold()
|
||||
);
|
||||
println!("{}", "==================".truecolor(sky_r, sky_g, sky_b));
|
||||
println!();
|
||||
|
||||
// Version info
|
||||
println!("{}", "Version Information:".bold());
|
||||
println!(" deepseek-tui: {}", env!("DEEPSEEK_BUILD_VERSION"));
|
||||
println!(" codewhale-tui: {}", env!("DEEPSEEK_BUILD_VERSION"));
|
||||
println!(" rust: {}", rustc_version());
|
||||
println!();
|
||||
|
||||
@@ -1836,7 +1836,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
"✗".truecolor(red_r, red_g, red_b)
|
||||
);
|
||||
println!(
|
||||
" Run 'deepseek auth set --provider <name>' to save a key to ~/.deepseek/config.toml."
|
||||
" Run 'codewhale auth set --provider <name>' to save a key to ~/.deepseek/config.toml."
|
||||
);
|
||||
false
|
||||
};
|
||||
@@ -1888,21 +1888,21 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
);
|
||||
if error_msg.contains("401") || error_msg.contains("Unauthorized") {
|
||||
println!(
|
||||
" Invalid API key. Check `deepseek auth status`, DEEPSEEK_API_KEY, or config.toml"
|
||||
" Invalid API key. Check `codewhale auth status`, DEEPSEEK_API_KEY, or config.toml"
|
||||
);
|
||||
if matches!(api_key_source, ApiKeySource::Keyring) {
|
||||
println!(
|
||||
" The rejected key came from the OS keyring via the dispatcher."
|
||||
);
|
||||
println!(
|
||||
" Run `deepseek auth status` to inspect config/keyring/env sources."
|
||||
" Run `codewhale auth status` to inspect config/keyring/env sources."
|
||||
);
|
||||
} else if matches!(api_key_source, ApiKeySource::Env) {
|
||||
println!(
|
||||
" The rejected key came from DEEPSEEK_API_KEY; no saved config key is present."
|
||||
);
|
||||
println!(
|
||||
" Run `deepseek auth set --provider deepseek` to save a config key that overrides stale env."
|
||||
" Run `codewhale auth set --provider deepseek` to save a config key that overrides stale env."
|
||||
);
|
||||
}
|
||||
} else if error_msg.contains("403") || error_msg.contains("Forbidden") {
|
||||
@@ -2004,7 +2004,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
"·".dimmed(),
|
||||
crate::utils::display_path(&mcp_config_path)
|
||||
);
|
||||
println!(" Run `deepseek mcp init` or `deepseek setup --mcp`.");
|
||||
println!(" Run `codewhale mcp init` or `codewhale setup --mcp`.");
|
||||
}
|
||||
|
||||
// Skills configuration
|
||||
@@ -2134,7 +2134,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
.is_some_and(|dir| dir.exists())
|
||||
&& !global_skills_dir.exists()
|
||||
{
|
||||
println!(" Run `deepseek setup --skills` (or add --local for ./skills).");
|
||||
println!(" Run `codewhale setup --skills` (or add --local for ./skills).");
|
||||
}
|
||||
|
||||
// Tools directory
|
||||
@@ -2155,7 +2155,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
"·".dimmed(),
|
||||
crate::utils::display_path(&tools_dir)
|
||||
);
|
||||
println!(" Run `deepseek setup --tools` to scaffold a starter dir.");
|
||||
println!(" Run `codewhale setup --tools` to scaffold a starter dir.");
|
||||
}
|
||||
|
||||
// Plugins directory
|
||||
@@ -2176,7 +2176,7 @@ async fn run_doctor(config: &Config, workspace: &Path, config_path_override: Opt
|
||||
"·".dimmed(),
|
||||
crate::utils::display_path(&plugins_dir)
|
||||
);
|
||||
println!(" Run `deepseek setup --plugins` to scaffold a starter dir.");
|
||||
println!(" Run `codewhale setup --plugins` to scaffold a starter dir.");
|
||||
}
|
||||
|
||||
// Storage surfaces (#422 / #440 / #500)
|
||||
@@ -2707,7 +2707,7 @@ fn run_doctor_json(
|
||||
},
|
||||
"api_connectivity": {
|
||||
"checked": false,
|
||||
"note": "Skipped in --json mode; run `deepseek doctor` for a live check.",
|
||||
"note": "Skipped in --json mode; run `codewhale doctor` for a live check.",
|
||||
},
|
||||
"capability": provider_capability_report(config),
|
||||
});
|
||||
@@ -2844,7 +2844,7 @@ fn doctor_timeout_recovery_lines(config: &Config) -> Vec<String> {
|
||||
&& !target.base_url.contains("api.deepseeki.com") =>
|
||||
{
|
||||
lines.push(
|
||||
"If this is a custom DeepSeek-compatible endpoint, set its HTTPS base URL in ~/.deepseek/config.toml and rerun `deepseek doctor`."
|
||||
"If this is a custom DeepSeek-compatible endpoint, set its HTTPS base URL in ~/.deepseek/config.toml and rerun `codewhale doctor`."
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
@@ -2863,7 +2863,7 @@ fn doctor_timeout_recovery_lines(config: &Config) -> Vec<String> {
|
||||
}
|
||||
|
||||
lines.push(
|
||||
"Run `deepseek doctor --json` and include `base_url`, `default_text_model`, and `api_connectivity` when filing an issue."
|
||||
"Run `codewhale doctor --json` and include `base_url`, `default_text_model`, and `api_connectivity` when filing an issue."
|
||||
.to_string(),
|
||||
);
|
||||
lines
|
||||
@@ -2987,7 +2987,7 @@ fn list_sessions(limit: usize, search: Option<String>) -> Result<()> {
|
||||
println!("{}", "No sessions found.".truecolor(sky_r, sky_g, sky_b));
|
||||
println!(
|
||||
"Start a new session with: {}",
|
||||
"deepseek".truecolor(blue_r, blue_g, blue_b)
|
||||
"codewhale".truecolor(blue_r, blue_g, blue_b)
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -3020,12 +3020,12 @@ fn list_sessions(limit: usize, search: Option<String>) -> Result<()> {
|
||||
println!();
|
||||
println!(
|
||||
"Resume with: {} {}",
|
||||
"deepseek --resume".truecolor(blue_r, blue_g, blue_b),
|
||||
"codewhale --resume".truecolor(blue_r, blue_g, blue_b),
|
||||
"<session-id>".dimmed()
|
||||
);
|
||||
println!(
|
||||
"Continue latest in this workspace: {}",
|
||||
"deepseek --continue".truecolor(blue_r, blue_g, blue_b)
|
||||
"codewhale --continue".truecolor(blue_r, blue_g, blue_b)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -3062,7 +3062,7 @@ fn init_project() -> Result<()> {
|
||||
);
|
||||
println!();
|
||||
println!("Edit this file to customize how the AI agent works with your project.");
|
||||
println!("The instructions will be loaded automatically when you run deepseek.");
|
||||
println!("The instructions will be loaded automatically when you run codewhale.");
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
@@ -3126,7 +3126,7 @@ fn resolve_session_id(session_id: Option<String>, last: bool, workspace: &Path)
|
||||
if last {
|
||||
return latest_session_id_for_workspace(workspace)?.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"No saved sessions found for workspace {}. Use `deepseek sessions` to list all sessions, or `deepseek resume <SESSION_ID>` to resume one explicitly.",
|
||||
"No saved sessions found for workspace {}. Use `codewhale sessions` to list all sessions, or `codewhale resume <SESSION_ID>` to resume one explicitly.",
|
||||
workspace.display()
|
||||
)
|
||||
});
|
||||
@@ -3289,7 +3289,7 @@ Provide findings ordered by severity with file references, then open questions,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `deepseek pr <N>` (#451) — fetch a GitHub PR via `gh`, format
|
||||
/// `codewhale pr <N>` (#451) — fetch a GitHub PR via `gh`, format
|
||||
/// title + body + diff as the composer's first message, and launch
|
||||
/// the interactive TUI. Falls back gracefully if `gh` is missing.
|
||||
async fn run_pr(
|
||||
@@ -3303,7 +3303,7 @@ async fn run_pr(
|
||||
bail!(
|
||||
"`gh` CLI not found on PATH. Install GitHub CLI \
|
||||
(https://cli.github.com) and authenticate (`gh auth login`) \
|
||||
so `deepseek pr <N>` can fetch PR metadata and the diff."
|
||||
so `codewhale pr <N>` can fetch PR metadata and the diff."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3583,7 +3583,7 @@ async fn run_mcp_command(config: &Config, command: McpCommand) -> Result<()> {
|
||||
);
|
||||
}
|
||||
}
|
||||
println!("Edit the file, then run `deepseek mcp list` or `deepseek mcp tools`.");
|
||||
println!("Edit the file, then run `codewhale mcp list` or `codewhale mcp tools`.");
|
||||
Ok(())
|
||||
}
|
||||
McpCommand::List => {
|
||||
@@ -3763,7 +3763,7 @@ async fn run_mcp_command(config: &Config, command: McpCommand) -> Result<()> {
|
||||
let mut cfg = load_mcp_config(&config_path)?;
|
||||
if cfg.servers.contains_key(&name) {
|
||||
bail!(
|
||||
"MCP server '{name}' already exists in {}. Use `deepseek mcp remove {name}` first, or choose a different --name.",
|
||||
"MCP server '{name}' already exists in {}. Use `codewhale mcp remove {name}` first, or choose a different --name.",
|
||||
config_path.display()
|
||||
);
|
||||
}
|
||||
@@ -3796,8 +3796,8 @@ async fn run_mcp_command(config: &Config, command: McpCommand) -> Result<()> {
|
||||
workspace.map_or(String::new(), |ws| format!(" --workspace {ws}"))
|
||||
);
|
||||
println!();
|
||||
println!("Tip: Use `deepseek mcp validate` to test the connection.");
|
||||
println!(" Use `deepseek serve --http` for the HTTP/SSE runtime API instead.");
|
||||
println!("Tip: Use `codewhale mcp validate` to test the connection.");
|
||||
println!(" Use `codewhale serve --http` for the HTTP/SSE runtime API instead.");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -4119,7 +4119,7 @@ fn checkpoint_age_label(age: std::time::Duration) -> String {
|
||||
/// **The checkpoint's workspace must also match the resolved launch workspace
|
||||
/// after canonicalisation.** If the workspace doesn't match, the checkpoint is
|
||||
/// persisted as a regular session (so the user can find it via
|
||||
/// `deepseek sessions` / `deepseek resume <id>`) and cleared, but not loaded.
|
||||
/// `codewhale sessions` / `codewhale resume <id>`) and cleared, but not loaded.
|
||||
fn recover_interrupted_checkpoint_for_resume(launch_workspace: &Path) -> Option<String> {
|
||||
let manager = session_manager::SessionManager::default_location().ok()?;
|
||||
let (session, age) = load_recent_checkpoint(&manager)?;
|
||||
@@ -4133,7 +4133,7 @@ fn recover_interrupted_checkpoint_for_resume(launch_workspace: &Path) -> Option<
|
||||
session_manager::workspace_scope_matches(&session_workspace, launch_workspace);
|
||||
|
||||
if !workspace_matches {
|
||||
// Persist the checkpoint so the user can find it via `deepseek
|
||||
// Persist the checkpoint so the user can find it via `codewhale
|
||||
// sessions`, then clear it so the next launch in this folder doesn't
|
||||
// re-trip the nag. Print a one-line notice pointing at the explicit
|
||||
// resume command — but DO NOT auto-load the session here.
|
||||
@@ -4141,7 +4141,7 @@ fn recover_interrupted_checkpoint_for_resume(launch_workspace: &Path) -> Option<
|
||||
let _ = manager.clear_checkpoint();
|
||||
eprintln!(
|
||||
"Note: an interrupted session from another workspace ({}) is \
|
||||
available. Run `deepseek sessions` to list saved sessions. Starting \
|
||||
available. Run `codewhale sessions` to list saved sessions. Starting \
|
||||
fresh in {}.",
|
||||
session_workspace.display(),
|
||||
launch_workspace.display(),
|
||||
@@ -4166,7 +4166,7 @@ fn recover_interrupted_checkpoint_for_resume(launch_workspace: &Path) -> Option<
|
||||
}
|
||||
|
||||
/// Preserve an interrupted checkpoint on a normal fresh launch without
|
||||
/// attaching it to the new TUI instance. This keeps "open another deepseek in
|
||||
/// attaching it to the new TUI instance. This keeps "open another codewhale in
|
||||
/// the same folder" from re-entering the previous in-flight session while still
|
||||
/// leaving an explicit resume path.
|
||||
fn preserve_interrupted_checkpoint_for_explicit_resume(launch_workspace: &Path) {
|
||||
@@ -4185,12 +4185,12 @@ fn preserve_interrupted_checkpoint_for_explicit_resume(launch_workspace: &Path)
|
||||
if session_manager::workspace_scope_matches(&session_workspace, launch_workspace) {
|
||||
eprintln!(
|
||||
"Found an in-flight session snapshot ({age_str}). Starting a new \
|
||||
session. Run `deepseek --continue` to resume it."
|
||||
session. Run `codewhale --continue` to resume it."
|
||||
);
|
||||
} else {
|
||||
eprintln!(
|
||||
"Note: an interrupted session from another workspace ({}) is \
|
||||
available. Run `deepseek sessions` to list saved sessions. Starting \
|
||||
available. Run `codewhale sessions` to list saved sessions. Starting \
|
||||
fresh in {}.",
|
||||
session_workspace.display(),
|
||||
launch_workspace.display(),
|
||||
@@ -5205,7 +5205,7 @@ mod doctor_endpoint_tests {
|
||||
assert!(text.contains("api.deepseek.com"));
|
||||
assert!(text.contains("custom DeepSeek-compatible endpoint"));
|
||||
assert!(!text.contains("provider = \"deepseek-cn\""));
|
||||
assert!(text.contains("deepseek doctor --json"));
|
||||
assert!(text.contains("codewhale doctor --json"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5234,19 +5234,19 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn prompt_flag_accepts_split_prompt_words_for_windows_cmd_shims() {
|
||||
let cli = parse_cli(&["deepseek", "-p", "hello", "world"]);
|
||||
let cli = parse_cli(&["codewhale", "-p", "hello", "world"]);
|
||||
|
||||
assert_eq!(cli.prompt, vec!["hello", "world"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn companion_binary_reports_its_own_name() {
|
||||
assert_eq!(Cli::command().get_name(), "deepseek-tui");
|
||||
assert_eq!(Cli::command().get_name(), "codewhale-tui");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_accepts_split_prompt_words_for_windows_cmd_shims() {
|
||||
let cli = parse_cli(&["deepseek", "exec", "hello", "world"]);
|
||||
let cli = parse_cli(&["codewhale", "exec", "hello", "world"]);
|
||||
let Some(Commands::Exec(args)) = cli.command else {
|
||||
panic!("expected exec command");
|
||||
};
|
||||
@@ -5256,7 +5256,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn exec_keeps_flags_before_split_prompt_words() {
|
||||
let cli = parse_cli(&["deepseek", "exec", "--json", "hello", "world"]);
|
||||
let cli = parse_cli(&["codewhale", "exec", "--json", "hello", "world"]);
|
||||
let Some(Commands::Exec(args)) = cli.command else {
|
||||
panic!("expected exec command");
|
||||
};
|
||||
@@ -5268,7 +5268,7 @@ mod terminal_mode_tests {
|
||||
#[test]
|
||||
fn exec_accepts_resume_session_flags_for_harnesses() {
|
||||
let cli = parse_cli(&[
|
||||
"deepseek",
|
||||
"codewhale",
|
||||
"exec",
|
||||
"--resume",
|
||||
"abc123",
|
||||
@@ -5287,7 +5287,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn exec_accepts_session_id_alias() {
|
||||
let cli = parse_cli(&["deepseek", "exec", "--session-id", "abc123", "follow up"]);
|
||||
let cli = parse_cli(&["codewhale", "exec", "--session-id", "abc123", "follow up"]);
|
||||
let Some(Commands::Exec(args)) = cli.command else {
|
||||
panic!("expected exec command");
|
||||
};
|
||||
@@ -5298,7 +5298,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn exec_accepts_continue_for_latest_workspace_session() {
|
||||
let cli = parse_cli(&["deepseek", "exec", "--continue", "follow up"]);
|
||||
let cli = parse_cli(&["codewhale", "exec", "--continue", "follow up"]);
|
||||
let Some(Commands::Exec(args)) = cli.command else {
|
||||
panic!("expected exec command");
|
||||
};
|
||||
@@ -5309,7 +5309,7 @@ mod terminal_mode_tests {
|
||||
#[test]
|
||||
fn exec_json_conflicts_with_stream_json_output() {
|
||||
let err = Cli::try_parse_from([
|
||||
"deepseek",
|
||||
"codewhale",
|
||||
"exec",
|
||||
"--json",
|
||||
"--output-format",
|
||||
@@ -5337,7 +5337,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn alternate_screen_defaults_on_in_auto_mode() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_alt_screen(&cli, &config));
|
||||
@@ -5345,7 +5345,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn no_alt_screen_flag_is_accepted_but_keeps_alternate_screen() {
|
||||
let cli = parse_cli(&["deepseek", "--no-alt-screen"]);
|
||||
let cli = parse_cli(&["codewhale", "--no-alt-screen"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_alt_screen(&cli, &config));
|
||||
@@ -5353,7 +5353,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn config_never_is_accepted_but_keeps_alternate_screen() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config {
|
||||
tui: Some(crate::config::TuiConfig {
|
||||
alternate_screen: Some("never".to_string()),
|
||||
@@ -5373,7 +5373,7 @@ mod terminal_mode_tests {
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn mouse_capture_defaults_on_when_alternate_screen_is_active() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
@@ -5387,7 +5387,7 @@ mod terminal_mode_tests {
|
||||
// Legacy conhost (no `WT_SESSION` and no `ConEmuPID`) keeps the
|
||||
// v0.8.x default-off behavior: mouse-mode reporting on legacy console
|
||||
// can leak SGR escapes into the composer.
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
@@ -5403,7 +5403,7 @@ mod terminal_mode_tests {
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn mouse_capture_defaults_on_in_windows_terminal() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
@@ -5421,7 +5421,7 @@ mod terminal_mode_tests {
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn mouse_capture_defaults_on_in_conemu() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
@@ -5436,7 +5436,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn no_mouse_capture_flag_disables_mouse_capture() {
|
||||
let cli = parse_cli(&["deepseek", "--no-mouse-capture"]);
|
||||
let cli = parse_cli(&["codewhale", "--no-mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
@@ -5446,7 +5446,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn config_can_disable_default_mouse_capture() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config {
|
||||
tui: Some(crate::config::TuiConfig {
|
||||
alternate_screen: None,
|
||||
@@ -5467,7 +5467,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_flag_enables_mouse_capture() {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let cli = parse_cli(&["codewhale", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
@@ -5477,7 +5477,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn config_can_enable_mouse_capture() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config {
|
||||
tui: Some(crate::config::TuiConfig {
|
||||
alternate_screen: None,
|
||||
@@ -5498,7 +5498,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_is_off_without_alternate_screen() {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let cli = parse_cli(&["codewhale", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
@@ -5515,7 +5515,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_defaults_off_in_jetbrains_jediterm() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(!should_use_mouse_capture_with(
|
||||
@@ -5530,7 +5530,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn jetbrains_default_off_is_case_insensitive() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config::default();
|
||||
|
||||
// JetBrains has occasionally varied the casing across releases;
|
||||
@@ -5547,7 +5547,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn mouse_capture_flag_overrides_jetbrains_default() {
|
||||
let cli = parse_cli(&["deepseek", "--mouse-capture"]);
|
||||
let cli = parse_cli(&["codewhale", "--mouse-capture"]);
|
||||
let config = Config::default();
|
||||
|
||||
assert!(should_use_mouse_capture_with(
|
||||
@@ -5562,7 +5562,7 @@ mod terminal_mode_tests {
|
||||
|
||||
#[test]
|
||||
fn config_mouse_capture_true_overrides_jetbrains_default() {
|
||||
let cli = parse_cli(&["deepseek"]);
|
||||
let cli = parse_cli(&["codewhale"]);
|
||||
let config = Config {
|
||||
tui: Some(crate::config::TuiConfig {
|
||||
alternate_screen: None,
|
||||
@@ -5782,24 +5782,24 @@ max_subagents = -3
|
||||
fn project_overlay_skips_missing_config_file() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let mut config = Config {
|
||||
provider: Some("deepseek".to_string()),
|
||||
provider: Some("codewhale".to_string()),
|
||||
..Config::default()
|
||||
};
|
||||
merge_project_config(&mut config, tmp.path());
|
||||
// Untouched.
|
||||
assert_eq!(config.provider.as_deref(), Some("deepseek"));
|
||||
assert_eq!(config.provider.as_deref(), Some("codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_overlay_skips_malformed_toml() {
|
||||
let tmp = workspace_with_project_config("this is not valid TOML !!");
|
||||
let mut config = Config {
|
||||
provider: Some("deepseek".to_string()),
|
||||
provider: Some("codewhale".to_string()),
|
||||
..Config::default()
|
||||
};
|
||||
merge_project_config(&mut config, tmp.path());
|
||||
// Untouched on parse error — better to fall back to global than crash.
|
||||
assert_eq!(config.provider.as_deref(), Some("deepseek"));
|
||||
assert_eq!(config.provider.as_deref(), Some("codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5811,13 +5811,13 @@ model = ""
|
||||
"#,
|
||||
);
|
||||
let mut config = Config {
|
||||
provider: Some("deepseek".to_string()),
|
||||
provider: Some("codewhale".to_string()),
|
||||
default_text_model: Some("deepseek-v4-pro".to_string()),
|
||||
..Config::default()
|
||||
};
|
||||
merge_project_config(&mut config, tmp.path());
|
||||
// Empty strings are ignored — they're rarely a deliberate override.
|
||||
assert_eq!(config.provider.as_deref(), Some("deepseek"));
|
||||
assert_eq!(config.provider.as_deref(), Some("codewhale"));
|
||||
assert_eq!(
|
||||
config.default_text_model.as_deref(),
|
||||
Some("deepseek-v4-pro")
|
||||
@@ -5958,7 +5958,7 @@ mod doctor_mcp_tests {
|
||||
|
||||
#[test]
|
||||
fn test_self_hosted_absolute_is_ok() {
|
||||
let server = make_server(Some("/usr/local/bin/deepseek"), &["serve", "--mcp"], None);
|
||||
let server = make_server(Some("/usr/local/bin/codewhale"), &["serve", "--mcp"], None);
|
||||
match doctor_check_mcp_server(&server) {
|
||||
McpServerDoctorStatus::Ok(detail) | McpServerDoctorStatus::Error(detail) => {
|
||||
// On systems where the path doesn't exist, this will be Error.
|
||||
@@ -5976,7 +5976,7 @@ mod doctor_mcp_tests {
|
||||
|
||||
#[test]
|
||||
fn test_self_hosted_relative_is_warning() {
|
||||
let server = make_server(Some("deepseek"), &["serve", "--mcp"], None);
|
||||
let server = make_server(Some("codewhale"), &["serve", "--mcp"], None);
|
||||
match doctor_check_mcp_server(&server) {
|
||||
McpServerDoctorStatus::Warning(detail) => {
|
||||
assert!(detail.contains("relative"));
|
||||
@@ -6461,7 +6461,7 @@ mod pr_prompt_tests {
|
||||
// A deliberately-implausible name to confirm the negative
|
||||
// branch — `--version` on this would exec(3) → ENOENT.
|
||||
assert!(
|
||||
!is_command_available("this-command-cannot-exist-deepseek-tui-test-ENOENT-marker"),
|
||||
!is_command_available("this-command-cannot-exist-codewhale-tui-test-ENOENT-marker"),
|
||||
"missing command should return false, not panic"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1328,7 +1328,7 @@ impl McpConnection {
|
||||
"params": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"clientInfo": {
|
||||
"name": "deepseek-tui",
|
||||
"name": "codewhale-tui",
|
||||
"version": env!("CARGO_PKG_VERSION")
|
||||
},
|
||||
"capabilities": {
|
||||
@@ -3343,7 +3343,7 @@ mod tests {
|
||||
r#"{
|
||||
"mcpServers": {
|
||||
"broken": {
|
||||
"command": "deepseek-tui-test-this-binary-does-not-exist-9f8e7d6c5b4a",
|
||||
"command": "codewhale-tui-test-this-binary-does-not-exist-9f8e7d6c5b4a",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ pub(crate) fn locale_reinforcement_closer(locale_tag: &str) -> Option<&'static s
|
||||
}
|
||||
|
||||
const LOCALE_PREAMBLE_ZH_HANS: &str = "## 语言要求\n\n\
|
||||
你正在 DeepSeek TUI 中运行。无论任务上下文(代码、错误日志、文件名)\
|
||||
你正在 codewhale 中运行。无论任务上下文(代码、错误日志、文件名)\
|
||||
是英文,无论系统提示的其余部分是英文,你都必须用简体中文进行 \
|
||||
`reasoning_content`(内部思考)和最终回复。代码、文件路径、工具名称\
|
||||
(例如 `read_file`、`exec_shell`)、环境变量、命令行参数和 URL \
|
||||
@@ -286,7 +286,7 @@ const LOCALE_PREAMBLE_ZH_HANS: &str = "## 语言要求\n\n\
|
||||
如果用户明确要求(例如 \"think in English\"),则覆盖此规则。";
|
||||
|
||||
const LOCALE_PREAMBLE_JA: &str = "## 言語要件\n\n\
|
||||
DeepSeek TUI を実行しています。タスクコンテキスト(コード、エラーログ、\
|
||||
codewhale を実行しています。タスクコンテキスト(コード、エラーログ、\
|
||||
ファイル名)が英語であっても、システムプロンプトの他の部分が英語で\
|
||||
あっても、`reasoning_content`(内部思考)と最終的な返信は日本語で\
|
||||
行ってください。コード、ファイルパス、ツール名(例:`read_file`、\
|
||||
@@ -297,7 +297,7 @@ DeepSeek TUI を実行しています。タスクコンテキスト(コード
|
||||
\"think in English\")はこのルールを上書きします。";
|
||||
|
||||
const LOCALE_PREAMBLE_PT_BR: &str = "## Requisito de Idioma\n\n\
|
||||
Você está rodando dentro do DeepSeek TUI. Escreva tanto \
|
||||
Você está rodando dentro do codewhale. Escreva tanto \
|
||||
`reasoning_content` (seu pensamento interno) quanto a resposta final \
|
||||
em português do Brasil, mesmo quando o contexto da tarefa (código, \
|
||||
logs de erro, nomes de arquivos) estiver em inglês e mesmo quando o \
|
||||
@@ -888,7 +888,7 @@ mod tests {
|
||||
SystemPrompt::Blocks(_) => panic!("expected text system prompt"),
|
||||
};
|
||||
let preamble_marker = "## 语言要求";
|
||||
let base_marker = "You are DeepSeek TUI";
|
||||
let base_marker = "You are codewhale";
|
||||
let preamble_pos = text
|
||||
.find(preamble_marker)
|
||||
.expect("zh-Hans preamble should be present");
|
||||
@@ -1262,7 +1262,7 @@ mod tests {
|
||||
fn compose_prompt_includes_all_layers() {
|
||||
let prompt = compose_prompt(AppMode::Agent, Personality::Calm);
|
||||
// Base layer
|
||||
assert!(prompt.contains("You are DeepSeek TUI"));
|
||||
assert!(prompt.contains("You are codewhale"));
|
||||
// Personality layer
|
||||
assert!(prompt.contains("Personality: Calm"));
|
||||
// Mode layer
|
||||
@@ -1321,7 +1321,7 @@ mod tests {
|
||||
#[test]
|
||||
fn compose_prompt_deterministic_order() {
|
||||
let prompt = compose_prompt(AppMode::Yolo, Personality::Calm);
|
||||
let base_pos = prompt.find("You are DeepSeek TUI").unwrap();
|
||||
let base_pos = prompt.find("You are codewhale").unwrap();
|
||||
let personality_pos = prompt.find("Personality: Calm").unwrap();
|
||||
let mode_pos = prompt.find("Mode: YOLO").unwrap();
|
||||
let approval_pos = prompt.find("Approval Policy: Auto").unwrap();
|
||||
@@ -1581,7 +1581,7 @@ mod tests {
|
||||
fn subagent_done_sentinel_section_present() {
|
||||
let prompt = compose_prompt(AppMode::Agent, Personality::Calm);
|
||||
assert!(prompt.contains("Internal Sub-agent Completion Events"));
|
||||
assert!(prompt.contains("<deepseek:subagent.done>"));
|
||||
assert!(prompt.contains("<codewhale:subagent.done>"));
|
||||
assert!(prompt.contains("not user input"));
|
||||
assert!(prompt.contains("Integration protocol"));
|
||||
assert!(prompt.contains("Do not tell the user they pasted sentinels"));
|
||||
|
||||
@@ -11,6 +11,6 @@ flow.
|
||||
## Sub-agent completion sentinel
|
||||
|
||||
When you open a sub-agent via `agent_open`, the child runs independently.
|
||||
You will receive a `<deepseek:subagent.done>` element in the transcript when it finishes.
|
||||
You will receive a `<codewhale:subagent.done>` element in the transcript when it finishes.
|
||||
Read its `summary` field and integrate the work — do not re-do what the child already did.
|
||||
You can also call `agent_eval` with the agent name or id to pull the current structured projection or transcript handle.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
You are DeepSeek TUI. You're already running inside it. Do not launch a nested interactive `deepseek` or `deepseek-tui` session unless the user explicitly asks. Using `deepseek` CLI subcommands such as `deepseek --version`, `deepseek -p`, `deepseek doctor`, or `deepseek auth status` is allowed when it directly helps the task.
|
||||
You are codewhale. You're already running inside it. Do not launch a nested interactive `codewhale` or `codewhale-tui` session unless the user explicitly asks. Using `codewhale` CLI subcommands such as `codewhale --version`, `codewhale -p`, `codewhale doctor`, or `codewhale auth status` is allowed when it directly helps the task.
|
||||
|
||||
## Language
|
||||
|
||||
@@ -14,7 +14,7 @@ Code, file paths, identifiers, tool names, environment variables, command-line f
|
||||
|
||||
## Runtime Identity
|
||||
|
||||
If the user asks what DeepSeek TUI version you are running, use the `deepseek_version` field in the `## Environment` section as the runtime version. Workspace files such as `Cargo.toml` describe the checkout you are inspecting; they may be stale, dirty, or intentionally different from the installed runtime. If those disagree, report both instead of replacing the runtime version with the workspace version.
|
||||
If the user asks what codewhale version you are running, use the `deepseek_version` field in the `## Environment` section as the runtime version. Workspace files such as `Cargo.toml` describe the checkout you are inspecting; they may be stale, dirty, or intentionally different from the installed runtime. If those disagree, report both instead of replacing the runtime version with the workspace version.
|
||||
|
||||
## Preamble Rhythm
|
||||
|
||||
@@ -203,7 +203,7 @@ Use persistent RLM sessions for long-context semantic work, bulk classification/
|
||||
|
||||
## Internal Sub-agent Completion Events
|
||||
|
||||
When you open a sub-agent via `agent_open`, the child runs independently. The runtime may send you an internal `<deepseek:subagent.done>` completion event when it finishes. This event is not user input. It carries:
|
||||
When you open a sub-agent via `agent_open`, the child runs independently. The runtime may send you an internal `<codewhale:subagent.done>` completion event when it finishes. This event is not user input. It carries:
|
||||
|
||||
- `agent_id` — the child's identifier
|
||||
- `status` — `"completed"` or `"failed"`
|
||||
@@ -211,14 +211,14 @@ When you open a sub-agent via `agent_open`, the child runs independently. The ru
|
||||
- `details` — currently `agent_eval`, the tool to call when you need the full projection or transcript handle
|
||||
|
||||
**Integration protocol:**
|
||||
1. When you see `<deepseek:subagent.done>`, read the human summary line immediately before it first.
|
||||
1. When you see `<codewhale:subagent.done>`, read the human summary line immediately before it first.
|
||||
2. Integrate the child's findings into your work — do not re-do what the child already did.
|
||||
3. If the summary is insufficient, call `agent_eval` with the agent name or id to pull the current structured projection or transcript handle.
|
||||
4. If the child failed (`"failed"`), assess whether the failure blocks your plan or whether you can proceed with a fallback.
|
||||
5. Update your `checklist_write` items to reflect the child's contribution.
|
||||
6. Do not tell the user they pasted sentinels or explain this protocol unless they explicitly ask about sub-agent internals.
|
||||
|
||||
You may see multiple `<deepseek:subagent.done>` sentinels in a single turn when children were opened in parallel. Process each one, then synthesize.
|
||||
You may see multiple `<codewhale:subagent.done>` sentinels in a single turn when children were opened in parallel. Process each one, then synthesize.
|
||||
|
||||
## Output formatting
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
You are DeepSeek TUI. You're already running inside it. Do not launch a nested interactive `deepseek` or `deepseek-tui` session unless the user explicitly asks. Using `deepseek` CLI subcommands such as `deepseek --version`, `deepseek -p`, `deepseek doctor`, or `deepseek auth status` is allowed when it directly helps the task.
|
||||
You are codewhale. You're already running inside it. Do not launch a nested interactive `codewhale` or `codewhale-tui` session unless the user explicitly asks. Using `codewhale` CLI subcommands such as `codewhale --version`, `codewhale -p`, `codewhale doctor`, or `codewhale auth status` is allowed when it directly helps the task.
|
||||
|
||||
## Decomposition Philosophy
|
||||
|
||||
|
||||
@@ -607,7 +607,7 @@ pub const DEFAULT_MAX_SPAWN_DEPTH: u32 = 3;
|
||||
|
||||
/// Terminal-state notification emitted to the engine's parent turn loop
|
||||
/// when one of its direct children finishes (issue #756). Carries the
|
||||
/// already-rendered `<deepseek:subagent.done>` sentinel that the model
|
||||
/// already-rendered `<codewhale:subagent.done>` sentinel that the model
|
||||
/// expects in the transcript per `prompts/base.md`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SubAgentCompletion {
|
||||
@@ -3333,12 +3333,12 @@ fn build_initial_subagent_messages(
|
||||
.filter(|state| !state.is_empty())
|
||||
{
|
||||
messages.push(system_text_message(format!(
|
||||
"<deepseek:fork_state>\n{state}\n</deepseek:fork_state>"
|
||||
"<codewhale:fork_state>\n{state}\n</codewhale:fork_state>"
|
||||
)));
|
||||
}
|
||||
|
||||
messages.push(system_text_message(format!(
|
||||
"<deepseek:subagent_context>\n{}\n</deepseek:subagent_context>",
|
||||
"<codewhale:subagent_context>\n{}\n</codewhale:subagent_context>",
|
||||
build_subagent_system_prompt(agent_type, assignment)
|
||||
)));
|
||||
}
|
||||
@@ -3406,7 +3406,7 @@ async fn run_subagent_task(task: SubAgentTask) {
|
||||
// sidebar / cell) AND a structured sentinel the model can recognize
|
||||
// on its next turn. Format: human summary on the first line,
|
||||
// sentinel on the second. The sentinel uses an opaque tag
|
||||
// (`deepseek:subagent.done`) to avoid collision with normal user
|
||||
// (`codewhale:subagent.done`) to avoid collision with normal user
|
||||
// text.
|
||||
let (summary, sentinel) = match &result {
|
||||
Ok(res) => (
|
||||
@@ -3473,7 +3473,7 @@ pub(crate) fn emit_parent_completion(
|
||||
true
|
||||
}
|
||||
|
||||
/// Build a `<deepseek:subagent.done>` JSON sentinel for a successful child.
|
||||
/// Build a `<codewhale:subagent.done>` JSON sentinel for a successful child.
|
||||
/// Intended to surface in the parent's transcript so the model recognizes
|
||||
/// child completion and can decide whether to read the full result via
|
||||
/// `agent_eval`.
|
||||
@@ -3490,10 +3490,10 @@ fn subagent_done_sentinel(agent_id: &str, res: &SubAgentResult) -> String {
|
||||
"summary_location": "previous_line",
|
||||
"details": "agent_eval",
|
||||
});
|
||||
format!("<deepseek:subagent.done>{payload}</deepseek:subagent.done>")
|
||||
format!("<codewhale:subagent.done>{payload}</codewhale:subagent.done>")
|
||||
}
|
||||
|
||||
/// Build a `<deepseek:subagent.done>` sentinel for a failed child.
|
||||
/// Build a `<codewhale:subagent.done>` sentinel for a failed child.
|
||||
fn subagent_failed_sentinel(agent_id: &str, _err: &str) -> String {
|
||||
let payload = json!({
|
||||
"agent_id": agent_id,
|
||||
@@ -3501,7 +3501,7 @@ fn subagent_failed_sentinel(agent_id: &str, _err: &str) -> String {
|
||||
"error_location": "previous_line",
|
||||
"details": "agent_eval",
|
||||
});
|
||||
format!("<deepseek:subagent.done>{payload}</deepseek:subagent.done>")
|
||||
format!("<codewhale:subagent.done>{payload}</codewhale:subagent.done>")
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -4437,7 +4437,7 @@ async fn subagent_flash_router(
|
||||
}
|
||||
|
||||
const SUBAGENT_ROUTER_SYSTEM_PROMPT: &str = "\
|
||||
You are the DeepSeek TUI sub-agent routing manager. Return only compact JSON: \
|
||||
You are the codewhale sub-agent routing manager. Return only compact JSON: \
|
||||
{\"model\":\"deepseek-v4-flash|deepseek-v4-pro\",\"thinking\":\"off|high|max\"}. \
|
||||
Treat each child assignment like a customer request entering a team queue: decide the least \
|
||||
sufficient worker and thinking budget for that assignment. Do not treat being a sub-agent as \
|
||||
|
||||
@@ -472,9 +472,9 @@ fn forked_subagent_messages_preserve_parent_prefix_then_append_task() {
|
||||
assert_eq!(messages.first(), Some(&parent_message));
|
||||
assert_eq!(messages.len(), 4);
|
||||
assert_eq!(messages[1].role, "system");
|
||||
assert!(message_text(&messages[1]).contains("<deepseek:fork_state>"));
|
||||
assert!(message_text(&messages[1]).contains("<codewhale:fork_state>"));
|
||||
assert_eq!(messages[2].role, "system");
|
||||
assert!(message_text(&messages[2]).contains("<deepseek:subagent_context>"));
|
||||
assert!(message_text(&messages[2]).contains("<codewhale:subagent_context>"));
|
||||
assert_eq!(messages[3].role, "user");
|
||||
assert!(message_text(&messages[3]).contains("inspect parser"));
|
||||
}
|
||||
@@ -1209,13 +1209,13 @@ fn build_subagent_system_prompt_skips_role_when_blank() {
|
||||
fn subagent_done_sentinel_format_is_well_formed() {
|
||||
let res = make_snapshot(SubAgentStatus::Completed);
|
||||
let sentinel = subagent_done_sentinel("agent_xyz", &res);
|
||||
assert!(sentinel.starts_with("<deepseek:subagent.done>"));
|
||||
assert!(sentinel.ends_with("</deepseek:subagent.done>"));
|
||||
assert!(sentinel.starts_with("<codewhale:subagent.done>"));
|
||||
assert!(sentinel.ends_with("</codewhale:subagent.done>"));
|
||||
|
||||
// The inner JSON parses and carries the expected fields.
|
||||
let inner = sentinel
|
||||
.trim_start_matches("<deepseek:subagent.done>")
|
||||
.trim_end_matches("</deepseek:subagent.done>");
|
||||
.trim_start_matches("<codewhale:subagent.done>")
|
||||
.trim_end_matches("</codewhale:subagent.done>");
|
||||
let parsed: serde_json::Value = serde_json::from_str(inner).expect("inner JSON parses");
|
||||
assert_eq!(parsed["agent_id"], "agent_xyz");
|
||||
assert_eq!(parsed["status"], "completed");
|
||||
@@ -1231,8 +1231,8 @@ fn subagent_done_sentinel_format_is_well_formed() {
|
||||
fn subagent_failed_sentinel_format_is_well_formed() {
|
||||
let sentinel = subagent_failed_sentinel("agent_zzz", "boom");
|
||||
let inner = sentinel
|
||||
.trim_start_matches("<deepseek:subagent.done>")
|
||||
.trim_end_matches("</deepseek:subagent.done>");
|
||||
.trim_start_matches("<codewhale:subagent.done>")
|
||||
.trim_end_matches("</codewhale:subagent.done>");
|
||||
let parsed: serde_json::Value = serde_json::from_str(inner).expect("inner JSON parses");
|
||||
assert_eq!(parsed["agent_id"], "agent_zzz");
|
||||
assert_eq!(parsed["status"], "failed");
|
||||
@@ -1708,7 +1708,7 @@ fn persisted_non_empty_allowed_tools_loads_as_narrow() {
|
||||
fn stub_runtime() -> SubAgentRuntime {
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
let workspace = std::env::temp_dir().join("deepseek-test-stub");
|
||||
let workspace = std::env::temp_dir().join("codewhale-test-stub");
|
||||
let context = ToolContext::new(workspace.clone());
|
||||
SubAgentRuntime {
|
||||
client: stub_client(),
|
||||
@@ -2080,7 +2080,7 @@ fn child_runtime_preserves_step_api_timeout() {
|
||||
#[test]
|
||||
fn subagent_completion_payload_carries_existing_sentinel_format() {
|
||||
// The payload format is the same one already documented in
|
||||
// prompts/base.md: human summary on line 1, `<deepseek:subagent.done>`
|
||||
// prompts/base.md: human summary on line 1, `<codewhale:subagent.done>`
|
||||
// sentinel on line 2. This test pins the format so future refactors
|
||||
// don't silently break the model's parsing contract.
|
||||
let mut snap = make_snapshot(SubAgentStatus::Completed);
|
||||
@@ -2094,14 +2094,14 @@ fn subagent_completion_payload_carries_existing_sentinel_format() {
|
||||
let first = lines.next().expect("first line is summary");
|
||||
let second = lines.next().expect("second line is sentinel");
|
||||
assert!(
|
||||
!first.starts_with("<deepseek:subagent.done>"),
|
||||
!first.starts_with("<codewhale:subagent.done>"),
|
||||
"summary should not be the sentinel itself"
|
||||
);
|
||||
assert!(
|
||||
second.starts_with("<deepseek:subagent.done>"),
|
||||
second.starts_with("<codewhale:subagent.done>"),
|
||||
"second line is the sentinel"
|
||||
);
|
||||
assert!(second.ends_with("</deepseek:subagent.done>"));
|
||||
assert!(second.ends_with("</codewhale:subagent.done>"));
|
||||
assert!(
|
||||
second.contains("\"agent_id\":\"agent_test\""),
|
||||
"sentinel JSON includes agent_id"
|
||||
|
||||
@@ -143,7 +143,7 @@ fn build_escape(method: Method, in_tmux: bool, msg: &str) -> Vec<u8> {
|
||||
}
|
||||
Method::Ghostty => {
|
||||
// Ghostty notification: OSC 777 ; notify ; title ; message BEL
|
||||
let seq = format!("\x1b]777;notify;DeepSeek TUI;{msg}\x07");
|
||||
let seq = format!("\x1b]777;notify;codewhale;{msg}\x07");
|
||||
wrap_for_multiplexer(&seq, in_tmux).into_bytes()
|
||||
}
|
||||
// Auto and Off should not reach build_escape.
|
||||
@@ -332,18 +332,18 @@ pub fn completed_turn_message(
|
||||
) -> String {
|
||||
let mut msg = text_summary(current_streaming_text)
|
||||
.or_else(|| latest_assistant_text(&app.api_messages))
|
||||
.unwrap_or_else(|| "deepseek: turn complete".to_string());
|
||||
.unwrap_or_else(|| "codewhale: turn complete".to_string());
|
||||
|
||||
if include_summary {
|
||||
let human = humanize_duration(turn_elapsed);
|
||||
let summary = match turn_cost {
|
||||
Some(c) => {
|
||||
let cost = crate::pricing::format_cost_estimate(c, app.cost_currency);
|
||||
format!("deepseek: turn complete ({human}, {cost})")
|
||||
format!("codewhale: turn complete ({human}, {cost})")
|
||||
}
|
||||
None => format!("deepseek: turn complete ({human})"),
|
||||
None => format!("codewhale: turn complete ({human})"),
|
||||
};
|
||||
if msg == "deepseek: turn complete" {
|
||||
if msg == "codewhale: turn complete" {
|
||||
msg = summary;
|
||||
} else {
|
||||
msg.push('\n');
|
||||
@@ -366,16 +366,16 @@ pub fn subagent_completion_message(
|
||||
let result_line = result
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.find(|line| !line.is_empty() && !line.starts_with("<deepseek:subagent.done>"));
|
||||
.find(|line| !line.is_empty() && !line.starts_with("<codewhale:subagent.done>"));
|
||||
let mut msg = result_line
|
||||
.and_then(text_summary)
|
||||
.map(|summary| format!("sub-agent {id}: {summary}"))
|
||||
.unwrap_or_else(|| format!("deepseek: sub-agent {id} complete"));
|
||||
.unwrap_or_else(|| format!("codewhale: sub-agent {id} complete"));
|
||||
|
||||
if include_summary {
|
||||
let human = humanize_duration(elapsed);
|
||||
msg.push('\n');
|
||||
msg.push_str(&format!("deepseek: sub-agent complete ({human})"));
|
||||
msg.push_str(&format!("codewhale: sub-agent complete ({human})"));
|
||||
}
|
||||
|
||||
msg
|
||||
@@ -471,8 +471,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn osc9_body_format() {
|
||||
let out = capture(Method::Osc9, false, "deepseek: done", 0, 1);
|
||||
assert_eq!(out, b"\x1b]9;deepseek: done\x07");
|
||||
let out = capture(Method::Osc9, false, "codewhale: done", 0, 1);
|
||||
assert_eq!(out, b"\x1b]9;codewhale: done\x07");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -501,7 +501,7 @@ mod tests {
|
||||
let out = capture(Method::Ghostty, false, "done", 0, 1);
|
||||
let s = String::from_utf8(out).unwrap();
|
||||
assert!(
|
||||
s.contains("777;notify;DeepSeek TUI;done"),
|
||||
s.contains("777;notify;codewhale;done"),
|
||||
"should have ghostty seq"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::palette;
|
||||
pub fn lines() -> Vec<Line<'static>> {
|
||||
vec![
|
||||
Line::from(Span::styled(
|
||||
"DeepSeek TUI",
|
||||
"codewhale",
|
||||
Style::default()
|
||||
.fg(palette::DEEPSEEK_BLUE)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
|
||||
@@ -2051,7 +2051,7 @@ mod tests {
|
||||
.push(HistoryCell::Tool(ToolCell::Generic(GenericToolCell {
|
||||
name: "read_file".to_string(),
|
||||
status: ToolStatus::Success,
|
||||
input_summary: Some("deepseek-tui/CHANGELOG.md".to_string()),
|
||||
input_summary: Some("codewhale-tui/CHANGELOG.md".to_string()),
|
||||
output: Some("done".to_string()),
|
||||
prompts: None,
|
||||
spillover_path: None,
|
||||
@@ -2328,7 +2328,7 @@ mod tests {
|
||||
fn tasks_panel_failed_shell_rows_point_to_activity_details() {
|
||||
let mut app = create_test_app();
|
||||
app.history.push(HistoryCell::Tool(ToolCell::Exec(ExecCell {
|
||||
command: "cargo test -p deepseek-tui".to_string(),
|
||||
command: "cargo test -p codewhale-tui".to_string(),
|
||||
status: ToolStatus::Failed,
|
||||
output: Some("test failed".to_string()),
|
||||
started_at: None,
|
||||
|
||||
@@ -576,7 +576,7 @@ fn should_show_resume_hint(session_id: Option<&str>) -> bool {
|
||||
}
|
||||
|
||||
fn resume_hint_text() -> &'static str {
|
||||
"To continue this session, execute deepseek run --continue"
|
||||
"To continue this session, execute codewhale run --continue"
|
||||
}
|
||||
|
||||
fn terminal_probe_timeout(config: &Config) -> Duration {
|
||||
|
||||
@@ -116,7 +116,7 @@ impl Drop for SettingsHomeGuard {
|
||||
fn resume_hint_uses_canonical_resume_command() {
|
||||
assert_eq!(
|
||||
resume_hint_text(),
|
||||
"To continue this session, execute deepseek run --continue"
|
||||
"To continue this session, execute codewhale run --continue"
|
||||
);
|
||||
assert!(should_show_resume_hint(Some(
|
||||
"019dd9d6-4f44-7c83-9863-59674a12b827"
|
||||
@@ -1952,7 +1952,7 @@ fn init_git_repo() -> TempDir {
|
||||
let commit = Command::new("git")
|
||||
.args([
|
||||
"-c",
|
||||
"user.name=DeepSeek TUI Tests",
|
||||
"user.name=codewhale Tests",
|
||||
"-c",
|
||||
"user.email=tests@example.com",
|
||||
"commit",
|
||||
@@ -2864,7 +2864,7 @@ fn visible_slash_menu_entries_excludes_removed_commands() {
|
||||
assert!(entries.iter().any(|entry| entry.name == "/config"));
|
||||
assert!(entries.iter().any(|entry| entry.name == "/links"));
|
||||
assert!(!entries.iter().any(|entry| entry.name == "/set"));
|
||||
assert!(!entries.iter().any(|entry| entry.name == "/deepseek"));
|
||||
assert!(!entries.iter().any(|entry| entry.name == "/codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5970,7 +5970,7 @@ fn completed_turn_notification_falls_back_to_default_when_empty() {
|
||||
Duration::from_secs(5),
|
||||
None,
|
||||
);
|
||||
assert_eq!(msg, "deepseek: turn complete");
|
||||
assert_eq!(msg, "codewhale: turn complete");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5993,13 +5993,13 @@ fn completed_turn_notification_truncates_long_text() {
|
||||
fn subagent_completion_notification_uses_summary_line_not_sentinel() {
|
||||
let msg = crate::tui::notifications::subagent_completion_message(
|
||||
"agent_live",
|
||||
"Finished the docs audit.\n<deepseek:subagent.done>{}</deepseek:subagent.done>",
|
||||
"Finished the docs audit.\n<codewhale:subagent.done>{}</codewhale:subagent.done>",
|
||||
false,
|
||||
Duration::from_secs(42),
|
||||
);
|
||||
|
||||
assert_eq!(msg, "sub-agent agent_live: Finished the docs audit.");
|
||||
assert!(!msg.contains("deepseek:subagent.done"));
|
||||
assert!(!msg.contains("codewhale:subagent.done"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -6011,8 +6011,8 @@ fn subagent_completion_notification_can_include_elapsed_summary() {
|
||||
Duration::from_secs(65),
|
||||
);
|
||||
|
||||
assert!(msg.contains("deepseek: sub-agent agent_live complete"));
|
||||
assert!(msg.contains("deepseek: sub-agent complete (1m 5s)"));
|
||||
assert!(msg.contains("codewhale: sub-agent agent_live complete"));
|
||||
assert!(msg.contains("codewhale: sub-agent complete (1m 5s)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -619,7 +619,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
),
|
||||
@@ -627,7 +627,7 @@ mod tests {
|
||||
);
|
||||
|
||||
assert!(rendered.contains("Agent"));
|
||||
assert!(rendered.contains("deepseek-tui"));
|
||||
assert!(rendered.contains("codewhale-tui"));
|
||||
assert!(rendered.contains("deepseek-v4-pro"));
|
||||
assert!(!rendered.contains("Plan"));
|
||||
assert!(!rendered.contains("Yolo"));
|
||||
@@ -637,12 +637,12 @@ mod tests {
|
||||
fn header_renders_version_chip_when_width_allows() {
|
||||
// At a generous width the header must surface the runtime version
|
||||
// — users repeatedly ask for it in the live UI (vs only via
|
||||
// `deepseek --version` / `/status`).
|
||||
// `codewhale --version` / `/status`).
|
||||
let rendered = render_header(
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
),
|
||||
@@ -663,7 +663,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Yolo,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
true,
|
||||
palette::DEEPSEEK_INK,
|
||||
)
|
||||
@@ -775,7 +775,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-ai/deepseek-v4-flash",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
)
|
||||
@@ -794,7 +794,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
),
|
||||
@@ -859,7 +859,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
)
|
||||
@@ -890,7 +890,7 @@ mod tests {
|
||||
HeaderData::new(
|
||||
AppMode::Agent,
|
||||
"deepseek-v4-pro",
|
||||
"deepseek-tui",
|
||||
"codewhale-tui",
|
||||
false,
|
||||
palette::DEEPSEEK_INK,
|
||||
)
|
||||
|
||||
@@ -333,7 +333,7 @@ impl Renderable for ChatWidget {
|
||||
|
||||
let area = _area;
|
||||
|
||||
// Repaint the full chat area with the deepseek-ink background each
|
||||
// Repaint the full chat area with the codewhale-ink background each
|
||||
// frame. Ratatui's `Paragraph` only writes cells that contain text,
|
||||
// so cells the current frame's paragraph doesn't touch would
|
||||
// otherwise hold the *previous* frame's contents (the `:24Z`
|
||||
@@ -1926,7 +1926,7 @@ fn build_empty_state_lines(app: &App, area: Rect) -> Vec<Line<'static>> {
|
||||
|
||||
let body = vec![
|
||||
Line::from(Span::styled(
|
||||
format!("{inset}>_ DeepSeek TUI (v{})", env!("CARGO_PKG_VERSION")),
|
||||
format!("{inset}>_ codewhale (v{})", env!("CARGO_PKG_VERSION")),
|
||||
Style::default().fg(palette::DEEPSEEK_BLUE).bold(),
|
||||
)),
|
||||
Line::from(""),
|
||||
@@ -2557,7 +2557,7 @@ mod tests {
|
||||
fn slash_completion_hints_exclude_set_and_deepseek_commands() {
|
||||
let hints = slash_completion_hints("/", 128, &[], Locale::En, None, ApiProvider::Deepseek);
|
||||
assert!(!hints.iter().any(|hint| hint.name == "/set"));
|
||||
assert!(!hints.iter().any(|hint| hint.name == "/deepseek"));
|
||||
assert!(!hints.iter().any(|hint| hint.name == "/codewhale"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2809,7 +2809,7 @@ mod tests {
|
||||
let mut app = create_test_app();
|
||||
app.composer_density = ComposerDensity::Comfortable;
|
||||
app.session_title =
|
||||
Some("hello could you please take a look at deepseek-tui and all changes".to_string());
|
||||
Some("hello could you please take a look at codewhale-tui and all changes".to_string());
|
||||
let slash_menu_entries = Vec::<SlashMenuEntry>::new();
|
||||
let mention_menu_entries = Vec::<String>::new();
|
||||
let widget = ComposerWidget::new(&app, 5, &slash_menu_entries, &mention_menu_entries);
|
||||
@@ -2825,7 +2825,7 @@ mod tests {
|
||||
let rendered = buffer_text(&buf, area);
|
||||
|
||||
assert!(rendered.contains("Composer"));
|
||||
assert!(!rendered.contains("deepseek-tui"));
|
||||
assert!(!rendered.contains("codewhale-tui"));
|
||||
assert!(!rendered.contains("hello could you"));
|
||||
}
|
||||
|
||||
@@ -2951,7 +2951,7 @@ mod tests {
|
||||
#[test]
|
||||
fn empty_state_shows_startup_context() {
|
||||
let mut app = create_test_app();
|
||||
app.workspace = PathBuf::from("/tmp/deepseek-test-workspace");
|
||||
app.workspace = PathBuf::from("/tmp/codewhale-test-workspace");
|
||||
app.model = "deepseek-v4-pro".to_string();
|
||||
|
||||
let lines = build_empty_state_lines(&app, Rect::new(0, 0, 100, 20));
|
||||
@@ -2966,9 +2966,9 @@ mod tests {
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
assert!(rendered.contains(&format!(">_ DeepSeek TUI (v{})", env!("CARGO_PKG_VERSION"))));
|
||||
assert!(rendered.contains(&format!(">_ codewhale (v{})", env!("CARGO_PKG_VERSION"))));
|
||||
assert!(rendered.contains("model: deepseek-v4-pro /model to switch"));
|
||||
assert!(rendered.contains("directory: /tmp/deepseek-test-workspace"));
|
||||
assert!(rendered.contains("directory: /tmp/codewhale-test-workspace"));
|
||||
}
|
||||
|
||||
/// Probe: confirm `cell.lines_with_motion` returns no Line whose total
|
||||
@@ -3449,7 +3449,7 @@ mod tests {
|
||||
/// pays the wrap cost; subsequent calls at different offsets should hit
|
||||
/// the per-cell cache and be ~constant time regardless of offset.
|
||||
///
|
||||
/// Run with: `cargo test -p deepseek-tui --release bench_transcript_scroll
|
||||
/// Run with: `cargo test -p codewhale-tui --release bench_transcript_scroll
|
||||
/// -- --ignored --nocapture`
|
||||
// Perf bench prints timing rows to stdout — runs in `cargo test`,
|
||||
// never inside the TUI alt-screen.
|
||||
|
||||
@@ -48,7 +48,7 @@ fn any_engine_source_contains(needle: &str) -> bool {
|
||||
|
||||
const EXPECTED_START_MARKERS: &[&str] = &[
|
||||
"[TOOL_CALL]",
|
||||
"<deepseek:tool_call",
|
||||
"<codewhale:tool_call",
|
||||
"<tool_call",
|
||||
"<invoke ",
|
||||
"<function_calls>",
|
||||
@@ -56,7 +56,7 @@ const EXPECTED_START_MARKERS: &[&str] = &[
|
||||
|
||||
const EXPECTED_END_MARKERS: &[&str] = &[
|
||||
"[/TOOL_CALL]",
|
||||
"</deepseek:tool_call>",
|
||||
"</codewhale:tool_call>",
|
||||
"</tool_call>",
|
||||
"</invoke>",
|
||||
"</function_calls>",
|
||||
|
||||
@@ -227,7 +227,7 @@ impl Harness {
|
||||
return PathBuf::from(path);
|
||||
}
|
||||
// Legacy fallback for callers still referencing the old bin name.
|
||||
if name == "deepseek-tui"
|
||||
if name == "codewhale-tui"
|
||||
&& let Some(path) = option_env!("CARGO_BIN_EXE_deepseek-tui")
|
||||
{
|
||||
return PathBuf::from(path);
|
||||
|
||||
Reference in New Issue
Block a user