- Add locale option to config.example.toml [tui] section with all
supported values (auto/en/ja/zh-Hans/pt-BR) and clear notes that
this controls TUI chrome only, not model output language.
- Fix README.zh-CN.md: settings.toml → config.toml (wrong filename).
- Expand README.zh-CN.md locale section with concrete config snippet
and LANG= env-var example; add link to docs/LOCALIZATION.md.
The zh-Hans locale has been fully implemented in localization.rs since
v0.7.6 — this commit makes it discoverable without reading source code.
Closes#566
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New `HookEvent::ShellEnv` fires immediately before each `exec_shell`
invocation. The hook's stdout is parsed as `KEY=VALUE\n` lines and the
resolved env vars are merged on top of the spawned process environment.
Useful for ephemeral credentials (`aws-vault export …`), per-skill
PATH adjustments, short-lived tokens.
* `HookExecutor::collect_shell_env(&context)` runs every matching
`shell_env` hook synchronously, captures stdout, parses it, returns
the merged map. Later hooks override earlier ones.
* `parse_env_lines` tolerates `export KEY=VAL`, quoted values
(`"…"` / `'…'`), comments (`#`), blank lines. Lines without `=` are
silently dropped — easier than failing the whole hook for one stray
human-friendly line. Values are taken verbatim; we don't run the
string through a shell to avoid expansion surprises.
* Resolved KEY names (NEVER values) are written to
`~/.deepseek/audit.log` so a session can be reconciled later
without leaking the secret material.
* Hook failure / timeout contributes no vars — `exec_shell` is never
aborted because of a misbehaving env hook.
Plumbing:
* `RuntimeToolServices` gains an optional
`Arc<HookExecutor>`. Wired in `tui/ui.rs` from the App's existing
`app.hooks` clone. Test contexts default to `None`.
* `ShellManager::execute_with_options_env` and
`execute_interactive_with_policy_env` are new variants that accept
an `extra_env: HashMap<String, String>` and forward it via
`CommandSpec::with_env` so `prepare()` carries it into `ExecEnv.env`.
* The original `execute_with_options` / `execute_interactive_with_policy`
call the new variants with an empty map so existing callers
(including all 5 internal call sites) keep working unchanged.
* `commands/hooks.rs` `event_label` covers the new variant.
Tests cover `parse_env_lines` against realistic hook output (bare
assignments, `export` prefix, quoted values, comments, blanks, malformed
lines). `cargo clippy --workspace --all-targets --all-features --locked --
-D warnings` clean.
`config.example.toml` documents the new event with an `aws-vault`
example and the audit-logging contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bridge work to unblock whalescale-desktop's Settings/Composer/Archived-chats
flows without requiring a daemon recompile per dev-port or client-side
aggregation.
#561 / whalescale#255 — CORS allow-list configurable
* Add `[runtime_api] cors_origins` config field, `--cors-origin URL`
(repeatable) flag on `deepseek serve --http`, and `DEEPSEEK_CORS_ORIGINS`
env var. User entries stack on top of the built-in defaults
(localhost:3000, localhost:1420, tauri://localhost). Resolution preserves
first-seen order and drops empty/duplicate values; invalid HeaderValues
log a warning and are skipped.
* Refactor `cors_layer()` to read merged origins from `RuntimeApiState`.
#562 / whalescale#256 — `PATCH /v1/threads/{id}` accepts the full editable
field set
* Extend `UpdateThreadRequest` with `allow_shell`, `trust_mode`,
`auto_approve`, `model`, `mode`, `title`, `system_prompt`. Each is
optional; missing means no change. Empty-string clears `title`/
`system_prompt`. Empty `model`/`mode` rejected with 400.
* Add `title: Option<String>` to `ThreadRecord` (additive, no schema bump
per documented criteria — old readers ignore the field without
misinterpretation). `list_threads_summary` now returns the user-set title
when present, falling back to the derived input-summary title.
* `thread.updated` event payload now carries a `changes` map with only the
fields that actually changed.
#563 / whalescale#260 — list-archived-only filter
* New `archived_only=true` query param on `GET /v1/threads` and
`GET /v1/threads/summary`. Backed by a new `ThreadListFilter` enum
(`ActiveOnly` | `IncludeArchived` | `ArchivedOnly`). `archived_only`
takes precedence over `include_archived`. Default behavior unchanged.
#564 / whalescale#261 — `GET /v1/usage` aggregation
* New `RuntimeThreadManager::aggregate_usage` walks all threads/turns,
filters by inclusive `since`/`until` RFC 3339 bounds, accumulates token
totals + cost (via `pricing::calculate_turn_cost_from_usage`), and
groups by `day` (default), `model`, `provider`, or `thread`.
* New `GET /v1/usage` route. `since`/`until`/`group_by` query params,
`since > until` and unknown `group_by` rejected with 400. Empty time
ranges yield empty `buckets` (never 404).
5 new tests cover preflight Allow-Origin echoing for both default and
extra origins, the extended PATCH field set + clear-by-empty + 400 paths,
the archived_only filter on list + summary endpoints, and the
/v1/usage envelope + validation errors. Existing 13 runtime_api tests
continue to pass; the parity gates and full workspace test suite are clean.
`docs/RUNTIME_API.md` and `config.example.toml` updated to document the
new params, body shape, endpoint, and CORS knob.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the post-#514/#517/#518 conflicts:
- CHANGELOG.md: kept both polish-stack and Linux ARM64 entries under
[Unreleased]; reordered so the ARM64/install-message Changed/Docs
sections precede the Releases footer.
- config.example.toml: kept both the `instructions = [...]` example
and the `[memory]` opt-in stanza in sequence.
- crates/tui/src/config.rs: kept both `instructions_paths()` (#454)
and `memory_enabled()` (#489) on the Config impl.
- crates/tui/src/prompts.rs: extended
`system_prompt_for_mode_with_context_and_skills` to take BOTH
`instructions: Option<&[PathBuf]>` and `user_memory_block:
Option<&str>`. Section 2.5a renders instructions; 2.5b renders the
memory block — both above the skills block so KV prefix caching
still wins.
- crates/tui/src/core/engine.rs: thread both args through the two
call sites.
- crates/tui/src/prompts.rs: update the `system_prompt_for_mode_with_context`
forwarder and the test caller to pass `None` for the new arg.
- .gitignore: ignore `.claude/*.local.md` and `*.local.json` so
local ralph / Claude-Code notes can't leak into commits.
Folds in two valid suggestions from the gemini-code-assist review on #519:
- `client.rs`: collapse the duplicated `LlmError → label` match and the
`human_retry_reason` body into a single
`retry_reason_label_and_human(err) -> (&'static str, String)` helper.
- `widgets/footer.rs::retry_banner_spans`: merge the two separate
`match &props.retry` blocks into one that returns both `(label, color)`.
Behavior is unchanged; refactor is a pure DRY win.
Adds a new optional `instructions = ["./AGENTS.md", "~/.deepseek/global.md"]`
config field that's loaded at startup and concatenated into the
system prompt, in declared order, above the skills block.
* `Config::instructions: Option<Vec<String>>` — raw paths from
`~/.deepseek/config.toml` or the per-project overlay.
* `Config::instructions_paths()` — `expand_path` each entry,
drop empties, return the resolved `Vec<PathBuf>`.
* `merge_project_config` — project's array replaces the
user-level array wholesale (including `instructions = []` to
clear the user list for the current repo). The typical "merge"
pattern is for users who want both — they list `~/global.md`
inside the project array.
* `EngineConfig::instructions: Vec<PathBuf>` — threaded from
config through both engine entry points (`Engine::new` for
Default and `refresh_system_prompt` for runtime swaps).
* `prompts::render_instructions_block(paths)` — loads each file
in order, caps each at 100 KiB with a `[…elided]` marker on
overflow, skips missing files with a tracing warning. Returns
`None` when nothing renders so the caller appends nothing.
* `system_prompt_for_mode_with_context_and_skills` gains an
`instructions: Option<&[PathBuf]>` parameter. Block lives
between the project-context block and the skills block so it
benefits from KV prefix caching and per-project overrides
apply consistently turn-over-turn.
Documentation:
* `config.example.toml` documents the field, the wholesale-
override semantics, and the size cap.
Tests:
* 5 new tests in `prompts.rs`: no-op for empty input, skip
missing files, declared-order concatenation, skip empty files,
truncate oversize files, plus an end-to-end test that the
block appears in the assembled system prompt when configured.
Adds a small, opt-in user-memory layer so the model has access to durable
preferences and conventions across sessions, and the user can dump quick
notes without leaving the TUI.
### What ships
- **Hierarchy loader** (#490): on every prompt assembly the engine reads
`Config::memory_path()` (defaults to `~/.deepseek/memory.md`, override via
`memory_path` in config or `DEEPSEEK_MEMORY_PATH`) and injects the file
as a `<user_memory>` block alongside the existing `<project_instructions>`
block. Goes above the volatile-content boundary so prefix-cache stays warm.
Oversize files (>100 KiB) are truncated with a marker.
- **`# foo` composer quick-add** (#492): typing a single line that starts
with `#` (but not `##` / `#!`) appends a timestamped bullet to the memory
file and consumes the input — no turn fires. The composer status line
surfaces the path that was written. Multi-`#` prefixes deliberately fall
through so users can paste Markdown headings.
- **`/memory` slash command** (#491): `/memory` (or `/memory show`) prints
the resolved path and contents inline; `/memory path`, `/memory clear`,
and `/memory edit` (prints `${VISUAL:-${EDITOR:-vi}} <path>`) cover the
rest of the manual-curation surface.
- **`remember` tool** (auto-update): model-callable tool that takes a
`note` string and appends it as a bullet — the same persistence path as
`# foo`. Auto-approved (writes only the user's own memory file). Only
registered when memory is enabled, so it doesn't pollute the catalog when
the feature is off.
- **Opt-in toggle** (#493): default behaviour is off. Enable with
`[memory] enabled = true` in `config.toml` or `DEEPSEEK_MEMORY=on` in
the environment.
### What's wired
- New `crates/tui/src/memory.rs` module (`load`, `as_system_block`,
`compose_block`, `append_entry`).
- New `crates/tui/src/tools/remember.rs` (`RememberTool` + 3 tests).
- New `crates/tui/src/commands/memory.rs` (`memory(app, arg)` handler).
- `EngineConfig` gains `memory_enabled: bool` + `memory_path: PathBuf`.
- `ToolContext` gains `memory_path: Option<PathBuf>`.
- `App` exposes `memory_path` + `use_memory` from `AppOptions` (previously
destructured-and-dropped); `main.rs` populates `use_memory` from
`config.memory_enabled()`.
- `system_prompt_for_mode_with_context_and_skills` accepts an optional
`user_memory_block` parameter; the engine computes it via
`memory::compose_block(...)` and threads it through.
- Composer Enter handler intercepts `# foo` only when
`config.memory_enabled()` is true; otherwise falls through to existing
turn-submission path.
- `MemoryConfig` table (`[memory] enabled`) added to `Config`, surfaced
in `config.example.toml`, plumbed through `merge_config`.
### Tests
- 8 unit tests in `memory::tests` covering `load` (missing / whitespace /
real), `as_system_block` (xml shape, empty input, oversize truncation),
and `append_entry` (creation, repeated append, empty-after-strip rejection).
- 3 unit tests in `tools::remember::tests` covering disabled-state error,
successful append, and missing-`note`-field validation.
### Verification
cargo fmt --all -- --check ✓
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings ✓
cargo test --workspace --all-features --locked ✓ (1821 + supporting; was 1809 on main)
Closes#490#491#492#493
Refines #489 (EPIC parent — phase-1 MVP delivered; phase-2 items
494–497 stay on the v0.9.0 board)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modern terminals (iTerm2, Terminal.app 13+, Ghostty, Kitty, WezTerm,
Alacritty, recent gnome-terminal/konsole) make a URL clickable when it's
wrapped in:
\x1b]8;;TARGET\x1b\\LABEL\x1b]8;;\x1b\\
Terminals that don't understand the sequence simply render the visible
LABEL and ignore the escape, so emitting OSC 8 is a strict UX upgrade
for supporting terminals and a no-op for the rest.
### What's wired
- New `crates/tui/src/tui/osc8.rs` module with `wrap_link(target, label)`,
`strip_into(s, &mut out)`, and a process-wide `ENABLED` AtomicBool that
defaults to `true`.
- `markdown_render::render_line_with_links` now wraps recognized URLs
(`http(s)://…`) in OSC 8 when the runtime flag is on. Display width is
computed from the bare URL — the escapes are zero-width on supporting
terminals.
- `ui_text::line_to_string` and `line_to_plain` strip OSC 8 wrappers when
the span content contains an escape, so selection / clipboard output
carries clean URLs and not the raw escape codes.
- `[tui] osc8_links: bool` config (default `true`) added to `TuiConfig`,
documented in `docs/CONFIGURATION.md`, and surfaced in
`config.example.toml`. `run_tui` applies it at startup.
### Tests
- 7 unit tests in `osc8::tests` covering wrap, strip-with-ESC-terminator,
strip-with-BEL-terminator, plain passthrough, mixed escapes, default
state, and round-trip set/unset.
- 2 markdown_render tests proving URLs in paragraph blocks emit the OSC 8
wrapper when enabled and emit plain text when disabled.
- 2 ui_text tests proving `line_to_plain` strips OSC 8 wrappers from spans
and passes plain spans through unchanged.
Tests that touch the global ENABLED flag serialize through a static
Mutex inside the test module so cargo's parallel runner can't observe a
torn read.
### Verification
cargo fmt --all -- --check ✓
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings ✓
cargo test --workspace --all-features --locked ✓ (1820 + supporting; was 1809)
Closes#498
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles the v0.8.8 stabilization fixes that were already implemented in the
working tree, plus the workflow/doc reconciliation called out in #507.
### Sub-agent runtime fixes
- **#509** Default sub-agent cap raised to 10 (configurable via
`[subagents].max_concurrent` in `config.toml`, hard ceiling 20). The
running-count calculation now ignores non-running, no-handle, and finished
handles so completed agents stop counting against the cap.
- **#510** `SharedSubAgentManager` is now `Arc<RwLock<...>>`; the read paths
that previously held a `Mutex` for inspection now take a read lock,
eliminating the multi-agent fan-out UI freeze.
- **#511** `compact_tool_result_for_context` summarizes `agent_result` /
`agent_wait` payloads before they are folded into the parent context.
- **#512** RLM tool cards map to `ToolFamily::Rlm` and render `rlm`, not
`swarm`. Stale "swarm" wording cleaned in docs/comments/tests.
- **#513** (foreground stopgap only) Foreground RLM work is visible in the
Agents sidebar projection. Full async RLM lifecycle remains v0.8.9 — the
issue stays open with a refined scope.
### TUI / UX fixes
- **#487** Offline composer queue is now session-scoped; legacy unscoped
queues fail closed.
- **#488** Composer Option+Backspace deletes by word; cross-platform key
routing helpers added.
- **#443/#444** Keyboard enhancement flags pop on normal AND panic exit; the
raw-mode startup probe is now bounded by a configurable timeout.
- **#449** Production footer reads statusline colors from `app.ui_theme`
rather than the bespoke palette.
- **#506** `display_path_with_home` no longer mutates `HOME` in tests; the
flake on shared-env CI is gone.
### Self-update / packaging
- **#503** `update.rs` arch mapping uses release-asset naming (`arm64`/`x64`)
instead of the raw Rust constants. The platform-asset selector also rejects
`.sha256` siblings as primary binaries. Tests now live alongside the source
in `mod tests` (the `#[path]`-based integration test was removed because it
duplicated test runs and forced a `pub(crate)` helper that no real caller
used).
- **`Max 5 in flight` wording updated** in `agent_spawn` description,
`prompts/base.md`, and `docs/TOOL_SURFACE.md` so the model sees the real
default cap (10) and the configuration knob name.
### CI / release docs (#507)
- Pruned three duplicated/dead workflows: `crates-publish.yml`, `parity.yml`,
`publish-npm.yml`. Their gates already run in `ci.yml` for every push/PR.
- `release.yml` build job now allows `parity` to be skipped (it only runs on
tag push), unblocking `workflow_dispatch` reruns. The job still fails
closed on a real parity failure.
- `RELEASE_RUNBOOK.md` reconciled: crate publishing is documented as the
manual `scripts/release/publish-crates.sh` flow (no automated workflow);
references to the deleted workflows removed.
- `CLAUDE.md` notes the `RELEASE_TAG_PAT` requirement for the auto-tag →
release.yml chain (without it, the tag is created but `release.yml` does
not fire) and documents the `workflow_dispatch` parity-skip behavior.
### Docs
- `docs/COMPETITIVE_ANALYSIS.md` added — capability matrix vs OpenCode and
Codex CLI, gap analysis, and recommended implementation order.
### Verification (this branch)
- `cargo fmt --all -- --check` ✓
- `cargo check --workspace --all-targets --locked` ✓
- `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings` ✓
- `cargo test --workspace --all-features --locked` ✓ (1809 + supporting)
- Parity gates ✓ (snapshot, parity_protocol, parity_state)
- `cargo build --release --locked -p deepseek-tui-cli -p deepseek-tui` ✓
- Lockfile drift guard ✓
- `deepseek doctor --json` clean
- `deepseek eval` (offline harness) success=true, 0 tool errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issues #202, #203, #204, #205:
- Cycle/seam triggers use active request input size + response
headroom reserve, not lifetime cumulative API usage.
- V4 hard-cycle headroom calibrated around fixed TURN_MAX_OUTPUT_TOKENS
plus CONTEXT_HEADROOM_TOKENS safety buffer.
- /tokens, /cost, footer/header labels, and docs now separate
active context, turn telemetry, cumulative usage, cache hit/miss,
context percent, and cost.
- Foreground exec_shell timeout output tells the model the process
was killed and suggests task_shell_start or background exec_shell
plus poll/wait.
- Added regression tests for active-token basis, V4 headroom,
seam trigger basis, footer label behavior, and shell timeout
recovery metadata.
- Preserved #200/#201 policy: V4 default is append-only,
prefix-cache preserving; replacement compaction, Flash seams,
and capacity intervention remain opt-in.
`run_interactive` now calls `session_manager::prune_workspace_snapshots_at_boot`
right after the system-skills installer, dropping any snapshot in the
side-git repo older than 7 days (default; configurable via the new
`[snapshots]` section in `config.example.toml`). The helper is
non-fatal: a missing `git` binary, read-only home, or absent snapshot
dir all log a single WARN (or DEBUG for the count of pruned commits)
and return, so the TUI keeps starting even when retention can't run.
Also document the snapshot subsystem in `config.example.toml` —
disk-footprint expectations, where the side repo lives, and how
`/restore` / `revert_turn` consume it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README: add a "Publishing your own skill" section explaining the
`github:owner/repo` install path, the multi-skill `skills/<name>/`
layout, and how to submit to the curated registry.
- config.example.toml: document `[skills] registry_url` /
`max_install_size_bytes` next to the existing `[network]` section so
users see the network-gate dependency in context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inject LSP diagnostics as a synthetic user message after every successful
file edit (`edit_file`, `apply_patch`, `write_file`) so the agent sees
compile breaks before its next reasoning step. Largest agent-quality
lever in v0.7.0.
Pieces:
- `crates/tui/src/lsp/`: thin JSON-RPC stdio client (no `tower-lsp`),
per-language registry, diagnostics renderer producing the
`<diagnostics file="…">` block format. `LspManager` owns lazily
spawned per-language transports keyed by `Language`.
- `core/engine.rs`: hook on the success branch of the tool-result loop
derives the edited file path(s) per tool, queries the LspManager
with a 5 s timeout, and collects rendered blocks into
`pending_lsp_blocks`. The queue is flushed as a `text` content
block on the next request iteration so the model sees the
diagnostics before it streams its next turn.
- `[lsp]` config schema (`enabled`, `poll_after_edit_ms`,
`max_diagnostics_per_file`, `include_warnings`, optional
`servers` override) with built-in defaults for rust-analyzer,
gopls, pyright, typescript-language-server, and clangd.
- Failure modes are non-blocking by design: a missing LSP binary
logs a one-time warning and skips the hook; a crashed server or
poll timeout simply drops that turn's diagnostics. The agent's
work is never blocked.
Tests: 24 unit tests cover language detection, registry overrides,
filter/sort/truncate behavior, and the rendered block format. Three
engine-level tokio tests exercise the full path through a fake
transport (no real LSP server is ever spawned in CI).
Acceptance criteria (per #136):
- Edit introducing a type error -> next request body contains
`<diagnostics file="…">` block at the right line/col.
- `[lsp] enabled = false` -> no diagnostics injected.
- Snapshot test exercises full path with mock transport.
- LSP binary not on PATH -> one-time warning, agent proceeds.
- 5 s timeout, errors-only by default.
- Transports spawn lazily on first edit per language.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the `[network]` table to both the workspace config crate (`ConfigToml`)
and the live tui config (`Config`), plus a documented example block in
`config.example.toml`. Schema:
```toml
[network]
default = "prompt" # allow | deny | prompt
allow = ["api.deepseek.com", "github.com"]
deny = []
audit = true
```
`NetworkPolicyToml::into_runtime()` builds a runtime `NetworkPolicy` so the
engine can construct a `NetworkPolicyDecider` without reaching across crate
boundaries. Defaults preserve pre-v0.7.0 behavior: when the section is
absent, no policy is enforced.
Adds crates/tui/src/tui/notifications.rs with Method enum (Auto/Osc9/Bel/Off),
notify_done / notify_done_to helpers, tmux DCS passthrough, and 9 unit tests.
Wires the hook at the TurnComplete event in tui/ui.rs so turns >= threshold_secs
(default 30 s) emit an escape to stdout; method auto-detects iTerm.app/Ghostty/
WezTerm for OSC 9 and falls back to BEL. Config exposed under [notifications] in
config.toml and documented in config.example.toml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A user couldn't find an `NVIDIA_API_KEY` block in `~/.deepseek/config.toml`
because the example file only mentioned NIM as commented-out alternates
to the top-level keys. Two fixes:
- `config.example.toml` now has explicit `[providers.deepseek]` and
`[providers.nvidia_nim]` sections (placed after all top-level keys so
the TOML still parses cleanly), each documenting `api_key` /
`base_url` / `model` plus the env vars that override them. Both
providers can be stored at once and toggled via `/provider` or
`--provider` without re-entering keys.
- `setup --status` "missing api_key" message is now provider-aware: on
`nvidia-nim` it points at `NVIDIA_API_KEY` + `[providers.nvidia_nim]`
+ `deepseek auth set --provider nvidia-nim`, instead of the
DeepSeek-only hint.
Audit verified: the v0.5.0 multi-turn replay fix path
(`should_replay_reasoning_content` → `requires_reasoning_content` in
`crates/tui/src/client.rs:1796`) keys off the model name (matches
`deepseek-v4`), not the provider, so NIM-hosted V4 models get the
replay automatically. No NIM-specific 400-class regression there.
Closes#37 (docs/UX); the live multi-turn-against-NIM verification
remains a manual smoke step listed in the issue (no NIM creds in CI).
Adds first-class DeepSeek V4 Pro and Flash support, updates the default model to deepseek-v4-pro, aligns legacy aliases with the current V4 1M context behavior, and fixes thinking-mode request handling.
Key fixes:
- Send DeepSeek's raw Chat Completions `thinking` parameter at the top level instead of SDK-only `extra_body`.
- Preserve assistant `reasoning_content` for all prior thinking-mode tool-call turns so subsequent requests satisfy DeepSeek V4's replay requirement.
- Fix npm wrapper concurrent first-run downloads by using per-process temporary download paths.
- Add `.mailmap` so historical bot-attributed commits aggregate under Hunter Bown where mailmap is honored.
Verified with the full local Rust gate, live DeepSeek V4 smoke, npm wrapper temp-install smoke, and green PR CI across Linux, macOS, and Windows.
- Move src/* into crates/tui/src/ to create a proper workspace structure
- Add .claude/ and .trimtab/ directories for Trimtab closed-loop workflow
- Add DEPENDENCY_GRAPH.md and update documentation
- Update Cargo.toml files to reflect new crate dependencies
- Update CI workflows and npm package scripts
- All tests pass, release build works
Major Features:
- Runtime API for external integrations and turn management
- Task manager with persistence and recovery
- Shell output streaming and improved tool execution
- Error taxonomy and audit logging
- Command palette and UI enhancements
Documentation:
- Runtime API documentation
- Operations runbook
- Architecture updates
Fixes:
- Auto-compaction threshold and triggering logic
- Doctor command API key validation
- Clippy and formatting compliance