
Codewhale v0.9.11 (Formerly DeepSeek TUI)
Operating System
Windows / macOS / LinuxDate Published
Sun Aug 23 2026Review Codewhale v0.9.11 (Formerly DeepSeek TUI)
DeepSeek TUI is an open source terminal based coding agent designed for DeepSeek AI models. Unlike a simple chatbot, it can interact with your local workspace, execute shell commands, manage git operations, and assist with software development tasks directly from the command line.
The project is written in Rust and focuses on speed, low resource usage, and keyboard driven workflows. It is especially appealing for developers who spend most of their time in the terminal.
DeepSeek TUI is gaining attention among developers who prefer terminal based workflows and want direct access to AI powered coding tools without relying on heavy IDE integrations. Built around DeepSeek models, it transforms the terminal into an interactive coding assistant capable of editing files, running commands, and managing development workflows.
Download Codewhale v0.9.11 (Formerly DeepSeek TUI) - Software Mirrors |
|---|
Codewhale v0.9.11 (Formerly DeepSeek TUI) for Windowscodewhale-windows-x64.zip | 56.06 MB codewhale-windows-x64.exe | 78.26 MB codewhale-windows-x64-portable.zip | 56.06 MB codewhale-windows-arm64.zip | 52.25 MB codewhale-windows-arm64.exe | 62.99 MB codewhale-windows-arm64-portable.zip | 52.25 MB codewhale-tui-windows-x64.exe | 78.26 MB codewhale-tui-windows-arm64.exe | 62.99 MB |
Codewhale v0.9.11 (Formerly DeepSeek TUI) for macOScodewhale-tui-macos-x64 | 58.44 MB codewhale-tui-macos-arm64 | 52.22 MB codewhale-macos-x64.tar.gz | 52.4 MB codewhale-macos-x64 | 58.44 MB codewhale-macos-arm64.tar.gz | 48.88 MB |
Codewhale v0.9.11 (Formerly DeepSeek TUI) for Linuxcodewhale-tui-linux-x64 | 62.6 MB codewhale-tui-linux-arm64 | 51.69 MB codewhale-linux-x64.tar.gz | 53.73 MB codewhale-linux-arm64.tar.gz | 49.74 MB |
Codewhale v0.9.11 (Formerly DeepSeek TUI) for Androidcodewhale-tui-android-arm64 | 53.2 MB codewhale-android-arm64.tar.gz | 49.95 MB |
Others Download related to Codewhale v0.9.11 (Formerly DeepSeek TUI) |
Codewhale v0.9.11 (Formerly DeepSeek TUI) Source CodeCodewhale v0.9.11 (Formerly DeepSeek TUI) Source code (zip) Codewhale v0.9.11 (Formerly DeepSeek TUI) Source code (tar.gz) |
Codewhale v0.9.11 (Formerly DeepSeek TUI) Release Notes:Codewhale is the public product from Shannon Labs. The
command, npm package, and release-asset names remain lowercase technical identifiers. The legacy npm package
receives no further releases. Users coming from v0.8.x legacy
InstallRecommended — npm (one command, both entrypoints)
The wrapper downloads the matched codewhale and codew command assets
from this Release. Both contain the same compiled runtime.
Docker / GHCR
The image exposes the same runtime as both codewhale and codew. The
latest tag is also updated on release.
Cargo (Linux / macOS)
The Cargo package installs codewhale. Cargo cannot create a second command
alias from one binary target; users who want the shorter spelling can add a
codew symlink to that installed executable. The npm, Homebrew, archive,
shell-installer, and container channels install both command names directly.
Manual download — platform archives (recommended)Each archive below contains the same runtime under thecodewhale and
codew command names, plus an install script:
Windows:
codewhale.exe, codew.exe, and codewhale.bat under
%LOCALAPPDATA%\Programs\CodeWhale\bin, adds that directory to the
current-user PATH, and creates a Start Menu shortcut that prefers
Windows Terminal (wt.exe) when it is installed.
codewhale-windows-arm64.zip
codewhale.bat from any directory. The NSIS installer is currently unsigned and may trigger Windows SmartScreen until a signing certificate is wired into the release pipeline.
Each platform also has bare, unarchived codewhale- and
codew- assets. The seven codewhale-tui- filenames
attached to v0.9.5 are byte-identical compatibility copies used only to let
already-installed v0.9.4 clients discover and cross this single-binary
transition; current installers do not expose a third runtime. The legacy npm
package deepseek-tui is deprecated and is not republished. For migration
from v0.8.x legacy binary names, see docs/REBRAND.md.
Verify (recommended)Download the checksum manifests from this Release and verify:
What's in v0.9.11Codewhale v0.9.11 tightens the long-running agent loop, makes workflow failures visible instead of successful-looking, adds an experimental vision-capable DeepSeek route, and prepares reproducible Codewhale-versus-Pi evaluation without publishing a result before a real run. The complete item-level change record is retained below the categorized release highlights.Added
flash-vision alias, bundled offline metadata,
registry and picker entries, and image-input capability on the chat route.
Context and output limits inherit from V4 Flash until DeepSeek publishes
distinct values; pricing remains unknown rather than guessed.
Changed
max_steps is unbounded; a positive user/config value remains an explicit
cap and is still clamped to the runtime safety ceiling. Wall-clock, provider,
heartbeat, cancellation, and admission safeguards are unchanged.
provider · model · thinking level is the persistent identity row below
the composer in every phase, and a separate activity row above the
composer carries the live phase, notices, and cost/metrics. Sending a
prompt no longer relocates the route identity above the composer, and
neither row ever duplicates it.
crates/core engine tree is gone, while the active TUI loop and its extracted
tool-call stages retain existing policy, hook, cancellation, and budget
behavior. Thanks to Sun Zhenyuan
(@bistack) for PR #5523.
Fixed
finish_reason. Protocol-only frames no longer count as answer
content or time-to-first-token, and a provider continuation that ends after
tool results with no answer or tool call fails durably instead of producing
a false Completed receipt.
vision requirement
admission now fail visibly instead of silently falling back, first-matching,
or rerouting.
parallel() null slot or presents a successful-looking run. Rejected
dispatches now fail the run, persist as typed bounded receipts with an exact
count, and appear in transcript, activity detail, and workflow-panel views.
/status and /help rendering, shell/web output bounds, MCP
lifecycle reporting, and narrow-terminal onboarding received the detailed
fixes recorded below.
codewhale account login, with --no-open and
--timeout-seconds); provider API keys are configured exclusively through
codewhale auth set --provider , and the hidden legacy
--api-key/--provider flags redirect loudly instead of silently writing
a key.
0600 Codewhale secrets file on headless
hosts, SSH boxes, and containers; the CODEWHALE_CLOUD_ALLOW_FILE_SESSION_STORE
opt-in is deprecated and ignored.
docs/KEYBINDINGS.md, localized in all 15 packs) and a startup hint that
names the previous and current version on the first launch of a newer
build, pointing at /change.
length/max_tokens) is never retried, and a
persistently answerless model still fails honestly after the bound.
read or
shell result reaches the model, so code such as `password:
credentials?.password or "password-validator": "^5.3.0"` stays byte-exact
for edits and read-back. Exact configured credential values are still always
replaced, and logs/previews/exports keep the broad key-based scrubber. (#5546,
reported by @ronohara)
/status report now follows the active locale without rewriting
exact custom-provider identities that contain brace-like text.
Security
Detailed change recordThe notes below are preserved in full so the categorized highlights do not erase behavior, migration, security, compatibility, or verification details.
MessageStop only after [DONE] or a non-empty finish_reason; raw EOF
without either is a typed failure. Message-start, ping, usage/terminal
deltas, block-stop, and message-stop frames do not count as productive
content or mint time-to-first-token. After tool results, a terminal provider
step with no answer or tool call now emits a durable failed turn and never
fabricates an empty assistant message.
agent action=roster exposes that same bounded roster. The Fleet operator
supplies fresh-root and inherited-member routing unless an explicit launch
route or member pin wins, the resolved member is shown separately from the
requested alias, and requires = ["vision"] is admitted only on an exact
route with verified offline image_input support—never by silent rerouting
or custom-proxy inference. Fleet selection remains an explicit user/folder
contract independent of legacy project-profile loading.
Runtime::handle_prompt called no model: it resolved config, ran a
local ModelRegistry lookup, emitted three canned hook events
(ResponseDelta was literally the string model-selected), and returned
HTTP 200 with output set to a stringified JSON echo of the caller's own
routing metadata — the prompt included. Worse, when a thread_id was
supplied it appended a real user row, flipped the thread to Running, and
then wrote that echo into durable history as an assistant message plus a
prompt_response checkpoint. Nothing marked the row synthetic and nothing
ever moved the thread out of Running. All three endpoints now route
through the same RuntimeBridge that stdio thread/message has always
used, so output is the model's streamed text, model is what the runtime
reports for the thread that ran the turn, and events are the real
streaming frames. Runtime::handle_prompt and its synthetic history write
are gone.
POST /prompt returns
{"error":{"code":...,"message":...}} with 400 (invalid request), 404
(thread not found), 503 (runtime_unavailable) or 500, instead of HTTP
500 carrying a PromptResponse with the error text stuffed into output
where model text belongs. The stdio surface gained JSON-RPC -32005
runtime_unavailable for "the turn engine could not be reached, so nothing
ran" — distinct from -32603, and retryable. There is no configuration in
which a prompt silently echoes instead of running.
status: "accepted" with a
ResponseDelta("queued") frame while starting no worker and calling no
bridge — the stdio path for the same request has always done real work, so
the two transports disagreed about what accepted meant. HTTP now replies
status: "completed" once the turn reaches a terminal state, with the
streamed frames in events and the turn id in data. Runtime::handle_thread
no longer accepts ThreadRequest::Message at all: it owns thread
bookkeeping, not the turn engine, and returns an error naming
POST /v1/threads/{id}/turns rather than a canned acceptance.
ok: false, error: "user_input_reply_unsupported") instead of
returning resolved: true and filing the answers in a map that had no
reader anywhere in the crate — every answer submitted was silently
discarded. It cannot be made to work on this transport: while a turn
streams, the stdio loop executes only thread/interrupt and queues
everything else, so an answer sent there would wait on the very turn
waiting for it. The refusal names the surface that does accept it,
POST /v1/user-input/{thread_id}/{request_id} on the runtime API. The
client-visible refusal is whitespace-clean, and the /tool path that mints
the UserInputRequest is unchanged and still genuine.
tools/subagent/coord/ledger.rs. The file held two unrelated things: the
model-facing agents/* tool wrappers, and the durable decision/claim/
contention records those wrappers happen to write — records whose consumers
are mostly not in the tool layer (tui::coordination_detail,
tui::work_surface, tui::ui::tests, core::engine::tests all name these
types). At 3.8k lines, reading either one started by scrolling past the
other. A pure move with a glob re-export from coord, so every
crate::tools::subagent::coord::{…} path still resolves and no consumer file
was edited; the only content change the move required is one constant going
from private to pub(super) because its caller stayed behind. coord.rs is
now 2.3k lines and ledger.rs 1.6k.
agent only" since the lifecycle
tools were removed, but six more were reachable: agents/list,
agents/message, agents/followup, agents/interrupt, agents/coordinate,
and agents/wait all defaulted to model-visible, so they shipped in the
catalog and tool_search could load any of them — and the agent
description told the model they existed. They now declare
model_visible() -> false, the same shape rlm and exec_shell use: still
registered, still executable by name so a persisted transcript replays
against the same implementation, never advertised and never returned by
either tool_search matcher.
Five of the six were already duplicates of an agent action. The sixth was
not: agents/coordinate action=claim was the only way to widen a write
claim, and write enforcement fails closed, so hiding it would have left a
refusal ("expand it first with…") pointing at a tool the model could no
longer call. agent gains one action, claim, taking the write scope
vocabulary action=start already uses (write_roots, plus parse-accepted
exact_files and coordination_contracts). It keeps agents/coordinate's
Auto approval — gating it deadlocks autonomous fan-in — and it can only
widen the caller's own scope; peer contention still fails. A scopeless claim
is refused rather than reported as granted, because expand_write_claim
returns the unchanged claim with Ok when every list is empty.
Collapsing six tools into one action set also collapses the gating: agent
is deliberately exempt from both name-keyed gates (posture_permits_tool
short-circuits it so delegation depth governs spawning, and
execution_envelope classifies it Bounded so a read-only member can fan
out read-only work), so a capability folded into it inherits no gate. claim
is therefore gated per action, reproducing the envelope check that kept
agents/coordinate off a read-only role's catalog — in the catalog and again
at dispatch, since catalog shaping is not an authority boundary. The other
actions keep exactly the visibility they had.
DeepSeekClient::prepare_outbound_request) instead of at the provider.
Chat Completions and OpenAI Responses used to drop an unfamiliar role
silently, Anthropic Messages forwarded message.role verbatim and took an
opaque provider 400 for it, and Google cloud-code was alone in failing
closed. Positioned system and developer history — including compaction
and branch summaries — is carried natively by Chat Completions and Responses
and projected, in place, onto Anthropic's user channel. It is neither
hoisted nor dropped. Genuinely unknown roles keep the previous
dialect-specific fail-closed/omit behavior, now decided in one table. The
dead "tool" arm in the Responses adapter is gone — nothing constructs that
role.
String on Message. Four wire adapters each decided
independently what an unfamiliar role meant, and a typo in a role string was
a silent transcript edit rather than a compile error. Role keeps an
Unrecognized(String) variant and serializes via as_str(), so a saved
session's bytes are unchanged, a transcript written by a newer build still
loads here, and assistant_interrupted stays a distinct session item — no
session schema bump and no migration ladder.
codewhale config import applies one with a
strict versioned envelope, a printed added/changed/skipped/conflicting/
rejected plan, consent gating (--yes required headless), a timestamped
backup with rollback, and idempotent re-import. Credential-shaped entries
are rejected by key name and value shape — rejections name the field,
never the value. Remote imports revalidate the HTTPS-or-loopback-HTTP policy
on every same-scheme redirect hop, and duplicate keys across applicable
section labels fail before any backup or write instead of silently resolving
by section order. Structured TOML values round-trip with their original
types and exact named-provider identity; recursive sanitization covers
arrays/tables and camel-case, dotted, cookie, and access-key spellings while
retaining ordinary token-count metrics. Machine-bound project trust,
credential-source consent, automatic hook/LSP execution, and local-path
authority are non-portable and fail before mutation. A missing target is
created transactionally and removed again on rollback, and project/global
scope validation rejects the wrong document before import or export.
Imported tables deep-merge portable fields into the target, so omitted
machine-local provider credentials, endpoints, and executable definitions
remain intact instead of being erased by a sanitized bundle.
/rc stop, and the
fail-closed shared-approval channel on transport loss are unchanged.
The takeover vocabulary ("web owns prompts and approvals") is gone from
every surface.
auto
identity, which never matched any recorded check — so the setup receipt,
model picker, and fleet setup view showed an eternal unchecked badge
even after hundreds of successful turns. The read now falls back to the
most recent check on the same route (provider + endpoint + auth class);
concrete-model reads keep exact per-model scoping.
ChatWidget whose
constructor consumed pending_scroll_delta — every PageUp/PageDown and
wheel event was swallowed by an invisible widget before the focused pane
could read it. The delta is now parked across the sample; the pane pins
on user scroll-up, follows new child activity at tail, and
jump-to-bottom releases the pin.
verification_uri from the device-code response went straight to
webbrowser::open with no parse, no scheme check and no credential check, so a
spoofed or compromised issuer could hand the platform's "open this" call a
file: path, a custom application scheme (vscode://, slack://), or a
credential-bearing URL. The shared primitive now refuses anything that is not a
web page before the URI is printed or opened. Behaviour change: a
non-loopback plain-http: verification URI now aborts login where it
previously opened; http: on a loopback host is still allowed, because local
runtimes legitimately use it.
GrokAuthEntry, TokenResponse, DeviceCodeResponse,
DeviceCodeGrant and the poll outcome) either redact or no longer derive
Debug at all, so a token has no printable path through a {:?} on any
surrounding struct. The shared DevicePollOutcome derives nothing, which the
compiler enforces.
codewhale-config's device_code), ported from pi. Three fixes come with
it. slow_down now honours a server-supplied interval instead of always
adding five seconds, which is what stops polling from running early forever
under WSL and VM clock drift. Timing out after a slow_down now says so and
names clock drift, rather than reading as a plain timeout. And the xAI
verification URI is validated before it is handed to the browser opener —
Codewhale previously opened whatever the device-code response said, so a
spoofed or compromised issuer could point the platform "open this" call at a
file: path or a custom application scheme. It must now be https:, or
http: on a loopback host for self-hosted issuers. Stored credential files
are unchanged and existing logins keep working. MCP OAuth is untouched: it
delegates to rmcp/oauth2 and was never hand-rolled.
error: line carrying a long inferred type or a minified
bundler frame is routinely hundreds of kilobytes, so a "30 KB" result could
arrive at 430 KB with the omitted line pasted back in whole. Each rescued
line is now clipped and the block has a 4 KiB ceiling; the signal survives,
the payload does not.
timeout field both
claimed otherwise — steering the model away from the one parameter that
would have saved a longer build. Both now name the real bound.
serde_json bytes produced straight
from the config's HashMaps, and two HashMaps with identical contents do
not iterate in the same order. Any touch of any watched file therefore hashed
differently, tore down every connection, and SIGTERMed and respawned every
stdio child. Keys are now sorted before hashing.
connect_all appended a generic "required MCP server failed to initialize"
entry after the real per-server error, and the snapshot folds those pairs into
a map keyed by server name — so the contentless entry replaced the diagnosis
and /mcp showed the marker instead of "No such file or directory". The marker
is now only synthesized when nothing else reported a cause.
Ready, the pool reused
it on every later tool call, and /mcp kept listing the server as connected.
null payload, indistinguishable from a tool that did nothing.
An explicit "result": null is still a valid empty success.
ping receives its prompt empty result, unsupported methods
receive JSON-RPC -32601, and the reader keeps only a weak stdin handle so
dropping the client still delivers graceful EOF.
ProviderCatalogCache and
LiveOffering, pricing defect receipts, and
TurnRecord.routed_usage_source_ids — it is not an in-memory-only cache
label. Empty or whitespace-only values (and scheme-less query-only strings
that strip to an empty authority) now hash the invalid-or-secret-bearing
sentinel instead of SHA-256 of the empty string. Scheme-less URLs that
contain @ now strip userinfo before hashing, matching the
scheme-bearing branch, so a typed user:pass@host/v1 no longer embeds the
password in a stored digest. routed_usage_source_fingerprint feeds
arbitrary scheme-less source ids into the same function, so a turn
rehydrated from an older build can fail to dedupe one routed-usage row.
Recovery is a cache miss and a re-fetch, not corruption. Empty input was
not restored to the old digest: an empty authority is not a usable
endpoint, and mapping it to the same sentinel the scheme branch already
uses for an empty host keeps invalid inputs from minting a unique cache
scope.
was matching the token` hint as a substring of the English word
tokens, and the spaced-assignment pass then dropped the rest of the
line, leaving max tokens = [redacted]. Token counts are not credentials;
the hint now matches a credential identifier (token, api_token) rather
than an English word, so the numbers survive while token = Bearer … is
still redacted.
GET /v1/threads/summary?search= walked the full
thread list and called get_thread_detail on each row before matching, and
that detail read is itself a whole-store walk of every turn JSON and every
item JSON. A non-matching keystroke was therefore
O(threads × (all_turns + all_items)) file reads — on the order of 10^8 JSON
parses at a few thousand threads. Search now matches id, title, and model
from the thread record (and, when the title is unset, the single latest-turn
file that supplies the displayed title) and loads detail only for matches, so
preview stays a display field rather than a search key. Session summary
already refused to search last-message text for the same reason.
#[expect(dead_code)], or gone where the lint was already
stale. A suppression that stops matching the lint fails the -Dwarnings
gate instead of sitting quiet. The same gate is recorded in
[workspace.lints] so member crates inherit it from the manifest rather
than only from CI RUSTFLAGS.
missing key / key:not-set,
which named no source at all — so a home whose secret store held a working
DeepSeek key could show DeepSeek missing key in the picker while a real
turn from that same home completed, and nothing on screen said which layer
disagreed. Every row now resolves through one sourced resolver and states
the place its credential came from ("OPENROUTER_API_KEY", `secret store
"deepseek", [providers.x] api_key`, "xAI OAuth", a consented external CLI
file); a row without a credential lists the places that were probed, in
precedence order, and the command that fixes the first of them. Where a
durable slot is deliberately not read — an inactive provider whose config
table carries no api-key marker — the row says so rather than implying an
empty slot.
has_api_key_for is now a wrapper over that resolver, and a test asserts
the two agree for every provider.
docs/THIRD_PARTY_NOTICES.md.
/provider and logout treat every other stored
credential as missing. Enumeration now skips the unreadable slot and
continues, matching the probe loop it replaced.
codewhale-tui binary, which
rendered completions from its own clap tree under its own name, so the
output ended in complete -F _codewhale__tui ... codewhale-tui (bash),
#compdef codewhale-tui (zsh), and
Register-ArgumentCompleter -Native -CommandName 'codewhale-tui'
(PowerShell). Sourcing it registered nothing for codewhale or codew —
the two commands current installers expose — so tab completion appeared to
do nothing. The forwarded tree was also stale against the real CLI: it offered
pr, scorecard, and session-diagnostics, which codewhale does not
have, and omitted run, rc, config, model, thread, lane,
workflow, web, account, app-server, mcp-server, metrics,
update, cloud, completion, and lane-log-proxy, which it does.
Completions are now rendered in-process from the CLI's own command tree,
and completions is an alias of the existing
completion subcommand rather than a second, divergent path. Regenerate any
script you installed from an earlier release. Reported by RepentStar
(#5526); part of the deepseek-tui-era identifier retirement in #5443.
codewhale. Releases publish codew as a byte-identical copy of the
codewhale binary, so a script bound to only one of the two names was half
installed for anyone who types the short one. Each shell gets its own
idiomatic hook rather than a second copy of the script: bash re-binds the
generated function, zsh widens the #compdef tag line to
#compdef codewhale codew, fish adds complete -c codew -w codewhale,
PowerShell registers -CommandName 'codewhale','codew', and Elvish aliases
the completer with
set edit:completion:arg-completer[codew] = $edit:completion:arg-completer[codewhale].
/status landed you on the tail of the report: the version, route,
directory, mode and sandbox rows had already scrolled past, and what stayed on
screen was five not reported rows and a $0.0000. The report is 18 rows on a
fresh session — Window override: is present unless the value is already
configured. That is the viewport's height, so once /status itself occupies a
history cell the title row still scrolls off; it does not fit that terminal
whole. Provider, model and reasoning effort are one Route: lockup, the way
the header rail already writes them. Mode and its permissions are one statement
of posture. Rate limits: is gone — it was a push_row of a string literal
and could never say anything but "not available from provider telemetry". The
per-turn token ledger is gone too, because /tokens is that ledger's whole
subject and /status was printing six rows of it at the same weight as the
sandbox policy; the two facts that lived nowhere else, the cumulative in/out
split and the cumulative cache totals, survive on one Session tokens: row.
Footer items: no longer prints ten internal config keys across the full
width — /statusline owns them, and the report now points there in the same
row that points at /tokens. Two blank gutters do the grouping; the
=================== rule under the title is gone.
context_window in config.toml past the right edge at 80 columns and wrapped
the sentence. Window source: states the provenance and Window override:
names the exact key — and the override row is omitted entirely when the value
is already configured, rather than advising you to set what you have set.
truncate_to_width that appended …, which in a two-hundred-row
list promises text no keystroke can reveal and lands mid-token:
(aliases: /qin… left the parenthesis hanging open. Descriptions now shed
whole fields — the alias parenthetical first, then trailing clauses at their
own joints, and only where there is no joint at all, the sentence's short form
on a whole word with no mark, keeping the head noun of a simple verb +
modifier + noun phrase rather than the adjectives that qualified it. The
focused row's description is restated under the filter at the panel's full
width, only when the row itself could not hold it, so a wide terminal does
not say the same sentence twice. At 60 columns that restatement is itself
shed — the /advisor detail stops before session — so the detail is longer
than the row, not a copy of the original sentence.
/advisor and a description cut down to 21. Each group now sizes its
column to the labels it actually holds, which nearly doubles the description
column on a narrow terminal, and the label — the string you have to type —
reads one step brighter than the description that qualifies it.
type to filter while the filter box says Type to filter two lines
above — at 60 columns that duplicate was what pushed the footer onto a second
row. A group header also stopped printing ▸ ▾: the selection cursor and the
collapsed chevron are the same glyph, and a focused collapsed group was
showing it twice for two different facts. Help now opens focused on the first
entry rather than the header above it.
Esc to interrupt reads in the same hint
weight as the right-hand chords rather than in the separator weight.
deepseek-v4-flash-prev…; a clipped model name is worse than no model name
because routes share prefixes. Clauses rejoin without a Latin space after a
full-width stop, so the Japanese receipt reads as Japanese.
/; the rail advertises chords you cannot
discover any other way. The rail now reads the same at 80 columns as at 200.
(width - caption.width()) / 2 evaluate to zero — so a
line that was written to be centered rendered flush-left and full-bleed,
cutting the centered whale/wordmark/prompt composition in half. The clipping
also destroyed the information it was supposed to carry: at 80 columns the
line read /private/tmp/claude-501/-Volumes-.../34267917-11f4-4d15-911a-…,
which tells the reader nothing about where they are. The caption now sheds
detail instead of being cut — MCP count first, then branch, then leading path
components — so it always fits with room to center, and the folder you are
standing in is the last thing to go. Elisions land on a path separator rather
than mid-directory.
Engine::run accepted Op::SendMessage, appended to a journal, and emitted
TurnComplete { status: "completed" } without ever contacting a model, and
TurnExecutor was a struct with a field-copy constructor and a
step < max_steps comparison. Nothing in the workspace referenced any of it —
the only mention of codewhale_core::engine anywhere was a doc comment inside
the tree itself — but its comments ("the real turn loop is wired here in the
next slice") were what docs/ARCHITECTURE.md leaned on to claim that
crates/core owns the agent loop. There is now exactly one turn loop in the
workspace, Engine::run_turn, and a guard test fails if a second one appears.
docs/ARCHITECTURE.md and AGENTS.md now say where it actually lives.
wrap_words split on
whitespace, so a Japanese sentence arrived as a single token, the
line-break check (which only fires once a line is non-empty) never
triggered, and the over-wide line was clipped by the terminal. At 80
columns the provider screen read
"Hosted providers need a key, but loca" and stopped — losing exactly the
half that tells the reader local runtimes need no key, on the screen where
they choose a provider. Space-less scripts now break by display width on
grapheme clusters, and a line may not begin with closing punctuation
(。, 、, 」, ) and friends). Wrapping for languages that do use
spaces is unchanged.
.claude/instructions.md and
CLAUDE.md sat at ranks 2 and 3 of the canonical instruction list — above
Codewhale's own .codewhale/instructions.md — .claude/rules/ was an
auto-discovered rules directory, and .cursorrules, .cursor/rules,
.clinerules, .windsurf/rules, .gemini, .github/copilot-instructions.md
and .github/muse-instructions.md were all imported into the system prompt
with no opt-in. Dropping a CLAUDE.md written for a different tool into a
repository silently made it standing authority here, which is an injection
surface rather than a convenience. Codewhale now reads AGENTS.md, the
cross-agent .agents/AGENTS.md, and its own instruction files by default;
every other agent's format is opt-in by name through
project_instruction_imports (env CODEWHALE_PROJECT_INSTRUCTION_IMPORTS),
imported files rank below Codewhale's own, and a workspace that contains an
un-imported format says so in a warning naming the exact setting.
.windsurf/rules, or .gemini pointing outside the workspace — was
traversed and its contents imported as instruction authority, because the
directory check followed the link while only the files inside it were
checked. The two instruction loaders now apply the same no-follow rule that
.codewhale/rules/ already had.
workspace-write sandboxes are created network-restricted; curl, package
installs, and git fetch inside a sandboxed shell are denied by the OS
sandbox unless network is granted explicitly. This closes a real gap rather
than tightening a working boundary: the elevation added in #273 was justified
by the application-level NetworkPolicy remaining "the only outbound
boundary", but that policy governs fetch_url, web_search, and MCP HTTP
and never constrained shell subprocesses, so workspace-write turns had
unrestricted egress with nothing enforcing anything. Network now comes from
one of three explicit places: the new sandbox_network_access config key
(also CODEWHALE_SANDBOX_NETWORK_ACCESS), a danger-full-access posture, or
the existing post-denial elevation prompt that grants network for a single
call. Yolo and --yolo/Bypass are unchanged — they resolve to
danger-full-access, which applies no sandbox at all. external-sandbox
reports the network it was actually granted instead of hardcoding true, and
/status reads the flag instead of printing "network on" for every
workspace-write policy. Platforms with no sandbox backend (default Linux
without bubblewrap, and Windows) still enforce nothing, and both /status
and doctor continue to say so.
codewhale-tui, deterministically on the
same codegen unit across all three build attempts, with
thread 'optimize module codewhale_tui...-cgu.13' has overflowed its stack.
The trigger is stack depth in the LLVM worker threads that run per-codegen-unit
optimization, not the workflow's lto=off override: holding the crate and
every flag fixed and varying only RUST_MIN_STACK on aarch64 shows 1 MiB
crashes rustc while 2 MiB and 4 MiB succeed. Unix std defaults to 2 MiB and
passed; the Windows ARM64 runner sat under the requirement. Nightly now sets
RUST_MIN_STACK explicitly for every target, because the requirement follows
from the size of crates/tui rather than from the platform. The redundant
codegen-units override is gone -- [profile.release] already sets 16, so
restating it never changed anything. Shipped binaries were never affected;
release-artifacts.yml builds --profile dist with fat LTO and
codegen-units = 1.
assert_eq!(spans[1], "⣤") for the
reduced-motion marker, title_span.style.fg == theme.tool_title_color,
visible[1] == "▏ done: scan repo", four separate tests each asserting one
shape of fenced code never takes the transcript rail, and one test whose
only assertion was !text.is_empty() under a name promising it checked the
rendered tool id. Assertions like those break on every legitimate visual
change and catch nothing a reader of the transcript would notice, which is
the liability d64b9429b named. The replacement is 40 tests, each named
for the property it protects and asserting the property instead of the
token: reduced motion is checked by rendering the same running card at two
different elapsed times and requiring the frames to match -- which also
catches an animation leak the glyph constant missed -- and a frozen marker
must stay visible rather than landing on the spinner's invisible blank
(U+2800). Severity colors are checked by requiring warning not to read as
error rather than by naming a palette entry. A streaming assistant glyph
must actually pulse when motion is allowed, checked against
pulse_brightness rather than by sleeping on the 2s sine. Each of the
invariants claimed was verified to fail the new suite when deliberately
broken in the renderer.
Contributors
read_lints support (#5524), plus independently reviewed
completion-routing overlap in #5530.
codew
registration fixed for #5526.
See CHANGELOG.md for full notes and docs/CHANGELOG_ARCHIVE.md for older releases. |
Key Features of DeepSeek TUI
Terminal Native Workflow
DeepSeek TUI operates entirely within the terminal, allowing developers to work without switching between browser windows or external interfaces.AI Assisted Coding
The assistant can read and edit files, generate code, explain logic, and help automate development tasks.Shell Command Execution
It supports running terminal commands directly through the interface, streamlining development workflows.Git Integration
Developers can manage repositories, review changes, and handle version control related tasks from inside the tool.Multiple Work Modes
DeepSeek TUI includes modes like Plan, Agent, and YOLO, offering different levels of automation and approval control.Large Context Handling
The tool is built around DeepSeek V4 capabilities, including support for extremely large context windows that help with understanding bigger codebases.
User Experience
DeepSeek TUI is designed for developers comfortable with terminal environments. The interface is keyboard focused and efficient, though beginners may need time to adapt.
The workflow feels closer to tools like Claude Code or Codex style assistants rather than traditional chat applications. Community feedback on Reddit and GitHub discussions highlights its speed and practical coding workflow.
Performance and Compatibility
Because it is written in Rust, DeepSeek TUI is lightweight and responsive. It supports Linux, macOS, and Windows, with installation available through npm, Cargo, or direct binaries.
Performance depends heavily on the connected DeepSeek models and API configuration, but the local interface itself remains fast and efficient.
Pros
Fast terminal native workflow
AI powered coding and automation
Lightweight Rust based architecture
Supports file editing and shell execution
Open source and actively developed
Cons
Requires familiarity with terminal workflows
Depends on external AI APIs
Advanced automation modes may feel risky for inexperienced users
DeepSeek TUI is ideal for developers, DevOps engineers, and terminal focused power users who want AI assistance integrated directly into their command line workflow. It is especially attractive for users seeking a lightweight alternative to browser based AI coding tools.
DeepSeek TUI combines AI coding assistance with the efficiency of terminal workflows. Its fast performance, flexible automation, and developer focused design make it one of the most interesting open source AI terminal tools currently gaining traction.
