release: v0.6.1 — pricing update, remove light theme + theme setting

- V4 cache-hit input prices cut to 1/10th per DeepSeek pricing update:
  Pro promo 0.03625→0.003625, Pro base 0.145→0.0145, Flash 0.028→0.0028
- Remove the 'light' theme variant (Variant::Light, Theme::light(), test)
- Remove the theme setting entirely — hardcode UI_THEME to whale/dark,
  drop the theme field from Settings, ConfigView, and config command
- Bump workspace version 0.6.0 → 0.6.1 (Cargo.toml, npm pkg, CHANGELOG)
- De-cringe the README: drop emojis, marketing fluff, unverified claims
This commit is contained in:
Hunter Bown
2026-04-26 11:56:41 -05:00
parent c5a584d5c3
commit e1ac84ae44
12 changed files with 56 additions and 126 deletions
+10 -1
View File
@@ -25,6 +25,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Test hygiene
- **5 regression tests pin auto-scroll churn contract.** `mark_history_updated` does not scroll; tool-cell handlers only `mark_history_updated`; `add_message` and `flush_active_cell` gate on `user_scrolled_during_stream`; the per-stream lock clears at TurnComplete and when the user returns to the live tail. (P2.4)
## [0.6.1] - 2026-04-26
### Changed
- **V4 cache-hit input prices cut to 1/10th per DeepSeek's pricing update.** Pro promo 0.03625→0.003625, Pro base 0.145→0.0145, Flash 0.028→0.0028 per 1M tokens. Cache-miss and output rates unchanged.
- **Removed the "light" theme option.** It was never tested, looked bad, and the dark/whale palettes are the supported targets. Theme validation now accepts only `default`, `dark`, and `whale`.
- **System prompts redesigned with decomposition-first philosophy.** All five prompt tiers teach the model to `todo_write` before acting, `update_plan` for strategy, and sub-agents for parallel work. Inspired by the mismanaged-geniuses hypothesis (Zhang et al., 2026).
## [0.6.0] - 2026-04-25
### Added
@@ -558,7 +565,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Hooks system and config profiles
- Example skills and launch assets
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.4.9...HEAD
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.6.1...HEAD
[0.6.1]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.4.9...v0.6.0
[0.4.9]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.4.8...v0.4.9
[0.4.8]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.3.33...v0.4.8
[0.3.33]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.3.32...v0.3.33
Generated
+13 -13
View File
@@ -806,7 +806,7 @@ dependencies = [
[[package]]
name = "deepseek-agent"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"deepseek-config",
"serde",
@@ -814,7 +814,7 @@ dependencies = [
[[package]]
name = "deepseek-app-server"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"axum",
@@ -837,7 +837,7 @@ dependencies = [
[[package]]
name = "deepseek-config"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"dirs",
@@ -848,7 +848,7 @@ dependencies = [
[[package]]
name = "deepseek-core"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"chrono",
@@ -867,7 +867,7 @@ dependencies = [
[[package]]
name = "deepseek-execpolicy"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"deepseek-protocol",
@@ -876,7 +876,7 @@ dependencies = [
[[package]]
name = "deepseek-hooks"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"async-trait",
@@ -890,7 +890,7 @@ dependencies = [
[[package]]
name = "deepseek-mcp"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"deepseek-protocol",
@@ -900,7 +900,7 @@ dependencies = [
[[package]]
name = "deepseek-protocol"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"serde",
"serde_json",
@@ -908,7 +908,7 @@ dependencies = [
[[package]]
name = "deepseek-state"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"chrono",
@@ -920,7 +920,7 @@ dependencies = [
[[package]]
name = "deepseek-tools"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"async-trait",
@@ -933,7 +933,7 @@ dependencies = [
[[package]]
name = "deepseek-tui"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"arboard",
@@ -987,7 +987,7 @@ dependencies = [
[[package]]
name = "deepseek-tui-cli"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"anyhow",
"chrono",
@@ -1005,7 +1005,7 @@ dependencies = [
[[package]]
name = "deepseek-tui-core"
version = "0.6.0"
version = "0.6.1"
[[package]]
name = "deranged"
+1 -1
View File
@@ -18,7 +18,7 @@ default-members = ["crates/cli", "crates/app-server", "crates/tui"]
resolver = "2"
[workspace.package]
version = "0.6.0"
version = "0.6.1"
edition = "2024"
license = "MIT"
repository = "https://github.com/Hmbown/DeepSeek-TUI"
+13 -15
View File
@@ -21,17 +21,17 @@ DeepSeek TUI is a coding agent that runs entirely in your terminal. It gives Dee
### Key Features
- 🌊 **Native RLM** *(new in v0.6)*`rlm_query` tool fans out 116 cheap `deepseek-v4-flash` children in parallel against the existing DeepSeek client. The model uses it for batched analysis, decomposition, or cheap parallel reasoning — one structured tool call, no external runtime
- 🧠 **Thinking-mode streaming**watch DeepSeek's chain-of-thought as it reasons about your code
- 🔧 **Full tool suite** — file ops, shell execution, git, web search/browse, apply-patch, sub-agents, MCP servers, and more
- 🪟 **1M-token context** feed entire codebases; automatic intelligent compaction when context fills up
- 🎛️ **Three interaction modes** — Plan (read-only explore), Agent (interactive with approval), YOLO (auto-approved). All three guided by decomposition-first system prompts that teach the model to `todo_write`, `update_plan`, and spawn sub-agents before acting
- **Reasoning-effort tiers** — cycle through `off → high → max` with Shift+Tab
- 🔄 **Session save/resume** — checkpoint and resume long sessions, fork conversations
- 🌐 **HTTP/SSE runtime API**`deepseek serve --http` for headless agent workflows
- 📦 **MCP protocol** — connect to Model Context Protocol servers for extended tooling
- 💰 **Live cost tracking** — per-turn and session-level token usage and cost estimates
- 🎨 **Dark & light themes** with a DeepSeek-blue branded palette
- **Native RLM** (`rlm_query` tool) — fans out 116 cheap `deepseek-v4-flash` children in parallel against the existing DeepSeek client for batched analysis, decomposition, or parallel reasoning
- **Thinking-mode streaming**shows DeepSeek's chain-of-thought as it reasons about your code
- **Full tool suite** — file ops, shell execution, git, web search/browse, apply-patch, sub-agents, MCP servers
- **1M-token context** — automatic intelligent compaction when context fills up
- **Three interaction modes** — Plan (read-only explore), Agent (interactive with approval), YOLO (auto-approved). Decomposition-first system prompts teach the model to `todo_write`, `update_plan`, and spawn sub-agents before acting
- **Reasoning-effort tiers** — cycle through `off → high → max` with Shift+Tab
- **Session save/resume** — checkpoint and resume long sessions
- **HTTP/SSE runtime API**`deepseek serve --http` for headless agent workflows
- **MCP protocol** — connect to Model Context Protocol servers for extended tooling
- **Live cost tracking** — per-turn and session-level token usage and cost estimates
- **Dark theme** — DeepSeek-blue palette
---
@@ -119,8 +119,8 @@ DeepSeek TUI targets **DeepSeek V4** models with 1M-token context windows by def
| Model | Context | Input (cache hit) | Input (cache miss) | Output |
|---|---|---|---|---|
| `deepseek-v4-pro` | 1M | $0.03625 / 1M* | $0.435 / 1M* | $0.87 / 1M* |
| `deepseek-v4-flash` | 1M | $0.028 / 1M | $0.14 / 1M | $0.28 / 1M |
| `deepseek-v4-pro` | 1M | $0.003625 / 1M* | $0.435 / 1M* | $0.87 / 1M* |
| `deepseek-v4-flash` | 1M | $0.0028 / 1M | $0.14 / 1M | $0.28 / 1M |
Legacy aliases `deepseek-chat` and `deepseek-reasoner` silently map to `deepseek-v4-flash`.
@@ -167,8 +167,6 @@ deepseek serve --http # HTTP/SSE API server
| **Agent** 🤖 | Default interactive mode — multi-step tool use with approval gates; model outlines work via `todo_write` before requesting writes |
| **YOLO** ⚡ | Auto-approve all tools in a trusted workspace; model still creates `todo_write`/`update_plan` to keep work visible and trackable |
All three modes are guided by decomposition-first system prompts: the model is taught to break work into verifiable tasks, track them in the sidebar, and fan out sub-agents for parallel work — "managing the geniuses" rather than just running single-shot prompts.
---
## Configuration
-5
View File
@@ -4,7 +4,6 @@ use std::path::{Path, PathBuf};
use super::CommandResult;
use crate::config::{COMMON_DEEPSEEK_MODELS, clear_api_key, normalize_model_name};
use crate::palette;
use crate::settings::Settings;
use crate::tui::app::{App, AppAction, AppMode, OnboardingState, SidebarFocus};
use crate::tui::approval::ApprovalMode;
@@ -130,10 +129,6 @@ pub fn set_config_value(app: &mut App, key: &str, value: &str, persist: bool) ->
action = Some(AppAction::UpdateCompaction(app.compaction_config()));
}
}
"theme" => {
app.ui_theme = palette::ui_theme(&settings.theme);
app.mark_history_updated();
}
"sidebar_width" | "sidebar" => {
app.sidebar_width_percent = settings.sidebar_width_percent;
app.mark_history_updated();
+3 -32
View File
@@ -1,9 +1,9 @@
//! Whale/DeepSeek terminal theme tokens.
//!
//! A small, deliberately flat module that names the color, border, and
//! padding choices the TUI is already making. The dark variant matches the
//! values previously hard-coded against [`crate::palette`]; the light variant
//! is reserved for the future skin swap that issue #14 tracks. Visible output
//! padding choices the TUI is already making. All values match the dark
//! palette previously hard-coded against [`crate::palette`]; a single
//! source-of-truth change here can swap the skin later. Visible output
//! is not changed by introducing this module.
//!
//! The only consumers today are the plan and tool cell renderers in
@@ -21,9 +21,6 @@ use crate::tui::history::ToolStatus;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Variant {
Dark,
/// Reserved for the future skin swap (issue #14). Not wired up yet.
#[allow(dead_code)]
Light,
}
/// Centralized visual tokens for sidebar, plan, and tool rendering.
@@ -83,18 +80,6 @@ impl Theme {
}
}
/// The light variant. Same RGB values as `dark()` today so a future skin
/// swap is a single-source-of-truth change in this file. Out of scope:
/// actually picking a distinct light palette.
#[must_use]
#[allow(dead_code)]
pub const fn light() -> Self {
Self {
variant: Variant::Light,
..Self::dark()
}
}
/// Pick the right tool accent for a given [`ToolStatus`].
#[must_use]
pub const fn tool_status_color(self, status: ToolStatus) -> Color {
@@ -167,20 +152,6 @@ mod tests {
assert_eq!(theme.tool_failed_accent, palette::ACCENT_TOOL_ISSUE);
}
#[test]
fn light_theme_keeps_dark_values_until_skin_swap() {
// The light variant exists so the future skin swap is a single-file
// change. It intentionally mirrors `dark()` today so introducing the
// module does not move pixels.
let dark = Theme::dark();
let light = Theme::light();
assert_eq!(light.variant, Variant::Light);
assert_eq!(light.section_border_color, dark.section_border_color);
assert_eq!(light.tool_running_accent, dark.tool_running_accent);
assert_eq!(light.tool_failed_accent, dark.tool_failed_accent);
assert_eq!(light.plan_in_progress_color, dark.plan_in_progress_color);
}
#[test]
fn tool_status_color_maps_each_status() {
let theme = Theme::dark();
+7 -30
View File
@@ -64,8 +64,6 @@ pub const ACCENT_PRIMARY: Color = DEEPSEEK_BLUE; // #3578E5
#[allow(dead_code)]
pub const ACCENT_SECONDARY: Color = TEXT_ACCENT; // #6AAEF2
#[allow(dead_code)]
pub const BACKGROUND_LIGHT: Color = Color::Rgb(30, 47, 71); // #1E2F47
#[allow(dead_code)]
pub const BACKGROUND_DARK: Color = Color::Rgb(13, 26, 48); // #0D1A30
#[allow(dead_code)]
pub const STATUS_NEUTRAL: Color = Color::Rgb(160, 160, 160); // #A0A0A0
@@ -103,6 +101,7 @@ pub const MODE_YOLO: Color = Color::Rgb(255, 100, 100); // Warning red
pub const MODE_PLAN: Color = Color::Rgb(255, 170, 60); // Orange
pub const SELECTION_BG: Color = Color::Rgb(26, 44, 74);
#[allow(dead_code)]
pub const COMPOSER_BG: Color = DEEPSEEK_SLATE;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -113,31 +112,9 @@ pub struct UiTheme {
pub header_bg: Color,
}
pub fn ui_theme(name: &str) -> UiTheme {
match name.to_ascii_lowercase().as_str() {
"dark" => UiTheme {
name: "dark",
composer_bg: DEEPSEEK_INK,
selection_bg: SELECTION_BG,
header_bg: DEEPSEEK_INK,
},
"light" => UiTheme {
name: "light",
composer_bg: Color::Rgb(26, 38, 58),
selection_bg: SELECTION_BG,
header_bg: DEEPSEEK_SLATE,
},
"whale" => UiTheme {
name: "whale",
composer_bg: DEEPSEEK_SLATE,
selection_bg: SELECTION_BG,
header_bg: DEEPSEEK_INK,
},
_ => UiTheme {
name: "default",
composer_bg: COMPOSER_BG,
selection_bg: SELECTION_BG,
header_bg: DEEPSEEK_INK,
},
}
}
pub const UI_THEME: UiTheme = UiTheme {
name: "whale",
composer_bg: DEEPSEEK_SLATE,
selection_bg: SELECTION_BG,
header_bg: DEEPSEEK_INK,
};
+6 -6
View File
@@ -39,13 +39,13 @@ fn pricing_for_model_at(model: &str, now: DateTime<Utc>) -> Option<ModelPricing>
// DeepSeek lists these as a limited-time 75% discount through
// 2026-05-05 15:59 UTC.
return Some(ModelPricing {
input_cache_hit_per_million: 0.03625,
input_cache_hit_per_million: 0.003625,
input_cache_miss_per_million: 0.435,
output_per_million: 0.87,
});
}
Some(ModelPricing {
input_cache_hit_per_million: 0.145,
input_cache_hit_per_million: 0.0145,
input_cache_miss_per_million: 1.74,
output_per_million: 3.48,
})
@@ -53,7 +53,7 @@ fn pricing_for_model_at(model: &str, now: DateTime<Utc>) -> Option<ModelPricing>
// deepseek-v4-flash and legacy aliases (deepseek-chat, deepseek-reasoner,
// deepseek-v3*) all price as v4-flash.
Some(ModelPricing {
input_cache_hit_per_million: 0.028,
input_cache_hit_per_million: 0.0028,
input_cache_miss_per_million: 0.14,
output_per_million: 0.28,
})
@@ -136,7 +136,7 @@ mod tests {
.unwrap();
let pricing = pricing_for_model_at("deepseek-v4-pro", before_expiry).unwrap();
assert_eq!(pricing.input_cache_hit_per_million, 0.03625);
assert_eq!(pricing.input_cache_hit_per_million, 0.003625);
assert_eq!(pricing.input_cache_miss_per_million, 0.435);
assert_eq!(pricing.output_per_million, 0.87);
}
@@ -146,7 +146,7 @@ mod tests {
let after_expiry = Utc.with_ymd_and_hms(2026, 5, 5, 16, 0, 0).single().unwrap();
let pricing = pricing_for_model_at("deepseek-v4-pro", after_expiry).unwrap();
assert_eq!(pricing.input_cache_hit_per_million, 0.145);
assert_eq!(pricing.input_cache_hit_per_million, 0.0145);
assert_eq!(pricing.input_cache_miss_per_million, 1.74);
assert_eq!(pricing.output_per_million, 3.48);
}
@@ -156,7 +156,7 @@ mod tests {
let now = Utc.with_ymd_and_hms(2026, 4, 25, 0, 0, 0).single().unwrap();
let pricing = pricing_for_model_at("deepseek-v4-flash", now).unwrap();
assert_eq!(pricing.input_cache_hit_per_million, 0.028);
assert_eq!(pricing.input_cache_hit_per_million, 0.0028);
assert_eq!(pricing.input_cache_miss_per_million, 0.14);
assert_eq!(pricing.output_per_million, 0.28);
}
-13
View File
@@ -13,8 +13,6 @@ use crate::config::{expand_path, normalize_model_name};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct Settings {
/// Color theme: "default", "dark", "light"
pub theme: String,
/// Auto-compact conversations when they get long
pub auto_compact: bool,
/// Reduce status noise and collapse details more aggressively
@@ -46,7 +44,6 @@ pub struct Settings {
impl Default for Settings {
fn default() -> Self {
Self {
theme: "whale".to_string(),
auto_compact: true,
calm_mode: false,
low_motion: false,
@@ -130,14 +127,6 @@ impl Settings {
/// Set a single setting by key
pub fn set(&mut self, key: &str, value: &str) -> Result<()> {
match key {
"theme" => {
if !["default", "dark", "light", "whale"].contains(&value) {
anyhow::bail!(
"Failed to update setting: invalid theme '{value}'. Expected: default, dark, light, whale."
);
}
self.theme = value.to_string();
}
"auto_compact" | "compact" => {
self.auto_compact = parse_bool(value)?;
}
@@ -252,7 +241,6 @@ impl Settings {
let mut lines = Vec::new();
lines.push("Settings:".to_string());
lines.push("─────────────────────────────".to_string());
lines.push(format!(" theme: {}", self.theme));
lines.push(format!(" auto_compact: {}", self.auto_compact));
lines.push(format!(" calm_mode: {}", self.calm_mode));
lines.push(format!(" low_motion: {}", self.low_motion));
@@ -284,7 +272,6 @@ impl Settings {
#[allow(dead_code)]
pub fn available_settings() -> Vec<(&'static str, &'static str)> {
vec![
("theme", "Color theme: default, dark, light"),
("auto_compact", "Auto-compact conversations: on/off"),
("calm_mode", "Calmer UI defaults: on/off"),
("low_motion", "Reduce animation and redraw churn: on/off"),
+1 -1
View File
@@ -661,7 +661,7 @@ impl App {
let sidebar_width_percent = settings.sidebar_width_percent;
let sidebar_focus = SidebarFocus::from_setting(&settings.sidebar_focus);
let max_input_history = settings.max_input_history;
let ui_theme = palette::ui_theme(&settings.theme);
let ui_theme = palette::UI_THEME;
let model = settings.default_model.clone().unwrap_or(model);
let compact_threshold =
compaction_threshold_for_model_and_effort(&model, config.reasoning_effort());
-7
View File
@@ -363,12 +363,6 @@ impl ConfigView {
editable: true,
scope: ConfigScope::Saved,
},
ConfigRow {
key: "theme".to_string(),
value: settings.theme.clone(),
editable: true,
scope: ConfigScope::Saved,
},
ConfigRow {
key: "sidebar_width".to_string(),
value: settings.sidebar_width_percent.to_string(),
@@ -597,7 +591,6 @@ fn config_hint_for_key(key: &str) -> &'static str {
| "composer_border" => "on/off, true/false, yes/no, 1/0",
"composer_density" | "transcript_spacing" => "compact | comfortable | spacious",
"default_mode" => "agent | plan | yolo",
"theme" => "default | dark | light | whale",
"sidebar_width" => "10..=50",
"sidebar_focus" => "auto | plan | todos | tasks | agents",
"max_history" => "integer (0 allowed)",
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "deepseek-tui",
"version": "0.6.0",
"deepseekBinaryVersion": "0.6.0",
"version": "0.6.1",
"deepseekBinaryVersion": "0.6.1",
"description": "Install and run deepseek and deepseek-tui binaries from GitHub release artifacts.",
"author": "Hmbown",
"license": "MIT",