Release Notes
Performance
- Typed SSE pipeline — per-token DOM allocation eliminated — the streaming hot loop no longer builds a
serde_json::Value per event
- New
runtime/sse.rs::SseLineBuffer — zero-copy line buffering: lines borrowed as &str, memchr (SIMD) newline search, O(1) read-cursor advance with periodic compaction (was two copies + an O(n) drain per line); UTF-8 splits across chunk boundaries handled by construction, with an exhaustive every-offset re-chunking test suite
- Typed
AnthropicEvent wire model with Cow<'a, str> borrowing — text deltas borrow from the line buffer on the escape-free fast path, own only when JSON escapes force decoding
ParseState + process_event consolidate the three parse sites (main loop, tail-flush, end-of-stream finalize) into a single write path — duplicate-site drift now structurally impossible; 15 behavioral seam tests pin the contract
- Dirty-flag render loop + streaming redraw coalescing — the TUI burned a full core during streaming (unconditional full-frame draw on every 16ms tick and every SSE delta);
needs_redraw flag draws only on actual state change, idle costs zero draws
- Streaming raised 30→60fps — redraw coalescing throttle tightened now that frames are cheap
- Draw-path allocation elimination — viewport
Paragraph moves the line Vec instead of deep-cloning every frame; ASCII-art rendering collapses same-style runs from one Span-per-char to one Span-per-line; per-frame version format! → concat! const
mem::take at session resume — rebuild_display_messages no longer deep-copies the entire message history (potentially MBs) to dodge the borrow checker
- Cache strategy: sliding-4 → single-last — one stationary
cache_control marker on the last message replaces ~57 lines of sliding-breakpoint logic
- Bench evidence: equivalent hit rate (96–97% both strategies); live-verified 99% hit on warm turn
- Prefix-invalidation bug class (mutating old markers) eliminated by construction; 5 new unit tests on a previously untested function
Fixed
- SSE double-emit on partial final line — when the final
content_block_stop arrived without a trailing newline, the tail-flush pushed the block but left in_thinking/in_tool_use set, so the end-of-stream flush pushed it again — duplicated content block sent to the model on the next turn
- PTY zombie reaping —
PtyHandle::Drop killed the child but never wait()ed; every timed-out or dropped shell session left a zombie. Now reaped with bounded-retry try_wait() (all other child-process sites already used kill_on_drop)
- Extension crash-loop health lied —
restart_count reset on successful handshake, so an extension that initialized fine and died on the next request never hit the exhaustion limit and reported Running. Consecutive-failure counter now resets only when the restarted process actually serves a call; new total_restarts (never reset) feeds health so recovered extensions report Degraded
- Integration test rot from
env_clear — security-hardened extension spawns silently broke 20+ test fixtures parameterized via env vars; fixtures moved to argv parameters, plus poison-cascade and parallel-env-race cleanup — full suite green, 3 consecutive runs
- Hardcoded Claude Code identity removed from API preamble — replaced with a configurable
identity config key + proper SynapsCLI default
- Dotted (namespaced) config keys exempt from unknown-key warnings
Added
- claude-fable-5 support — known-models entry with adaptive thinking, pricing ($10/$50 per MTok), and 1M context opt-in
- Telemetry module — structured per-request API records:
TelemetryLevel (off/basic/full), usage with cache TTL breakdown, rate-limit headers, cache-diagnosis records; JSONL writer to ~/.cache/synaps/api-log.jsonl (0600 + O_NOFOLLOW), wired into config keys and the Anthropic SSE stream
- Cache strategy benchmark suite (
bench/) — 21 tool-heavy questions with deterministic outcomes across 4 swappable breakpoint strategies (none / single-last / last-3 / sliding-4); per-turn JSONL logging of tokens, cache read/write, hit %, cost, latency; compare.py for side-by-side runs
synaps completions <shell> — shell completions via clap_complete (bash, zsh, fish, elvish, powershell)
- First-run banner — boot with no OAuth, no
ANTHROPIC_API_KEY, and no provider keys shows a getting-started banner with the three setup paths instead of a silent TUI
Changed
- reqwest 0.11 → 0.12 — duplicate HTTP stack collapsed — reqwest 0.11 was the sole consumer of hyper 0.14/http 0.2/h2 0.3 alongside axum's hyper 1.x; the old stack (plus native-tls/OpenSSL) is gone, TLS is now rustls with native root certs — explicit in the manifest, smaller binary, zero source changes
- Humanized API + network errors — raw JSON dumps and reqwest debug strings replaced with actionable text (529 → "overloaded, wait", 401 → "run synaps login", context overflow → "run /compact"); retry notices ("⏳ API error, retrying…") are now display-only system lines instead of fake assistant content persisted into session history
- Input recovery on stream error — when a stream dies after retries, the user's popped message is restored to the input box instead of silently destroyed
- Config parse warnings with did-you-mean — unknown keys suggest corrections via levenshtein (
modle → did you mean model?); unparseable values warn and fall back to defaults instead of silently misbehaving
- Login/status polish — token-refresh errors now say
synaps login (was a nonexistent command); synaps status refreshes the OAuth token before the request instead of 401ing on expired tokens; --help rewritten with real about text and --profile documented
Earlier in this cycle
- Engine refactor +
chat headless mode — daemon, run, and client subcommands deleted; chat is now fully-featured headless mode with MCP, extensions, skills, sessions, compaction, and the event bus (same engine as the TUI, stdin/stdout rendering)
- New
engine/ module: setup.rs (boot), commands.rs (headless slash commands), stream.rs (StreamEvent consumer), session.rs (ConversationState)
chatui/ module deleted — tui/ is the sole frontend
- New
pricing.rs — centralized Anthropic pricing (Opus/Sonnet/Haiku) with cache billing support
- New
harbor/synaps_agent.py — Terminal-Bench integration agent
- Clippy CI added (
cargo clippy --all-targets gates PRs)
- Published on crates.io as
synaps (v0.1.x); available via cargo install synaps, AUR (yay -S synaps), Homebrew, and GitHub Releases
- Path B Phase 6 — The Big Move (extension contracts) — synaps-cli core no longer contains whisper-specific code; the local-voice plugin owns it
- Deleted
src/voice/{models,download,rebuild}.rs (whisper.cpp catalog, HuggingFace downloader, cmake-rebuild orchestrator)
- Slimmed
src/voice/discovery.rs from ~400 LOC to ~190: only discover() / discover_in() / DiscoveredVoiceSidecar remain; read_build_info, detect_host_backend, probe_* are gone (callers use the Phase 5 info.get cache instead)
- Deleted in-core
/voice models|help|download|rebuild action arms and parsers — those subcommands now route through the plugin via the Phase 2 interactive command contract; missing-plugin case errors with a clear message
- Deleted in-core whisper download UI:
App.{download_progress, download_filename, download_rx, voice_download_in_flight, model_browser_selected, cached_voice_compiled_backend} plus start_download / on_download_progress / on_download_complete and the download_change event-loop arm; the sticky progress widget now exclusively renders generic extensions::active_tasks::ActiveTasks (Phase 3)
- Deleted
EditorKind::{ModelBrowser, WhisperModelPicker}, ActiveEditor::ModelBrowser, ModelBrowserRow, model_browser_rows, whisper_model_options, render_model_browser, render_download_progress_line, plus the voice_stt_model / voice_stt_backend / voice_language setting definitions — the plugin replaces them via the Phase 4 settings categories contract
- Migration shim: on first launch, legacy global voice config keys (
voice_stt_model_path, voice_stt_model, voice_stt_backend, voice_language) are copied into the plugin namespace ~/.synaps-cli/plugins/local-voice/config if the destination is empty; legacy keys remain in place for safety. Reads at runtime prefer the plugin namespace and fall back to the legacy global key with a deprecation warning.
- Net change:
~−2,400 LOC removed from synaps-cli core; behaviour preserved when the local-voice plugin is installed
- Phase 2 — Extensions Capability Platform — extensions are now first-class citizens
- Slash command extension contract (
commands in plugin manifest)
- Settings panel extension contract (
settings in plugin manifest)
- Keybind extension contract (User-tier binds via plugin manifest)
- Model-provider extension contract — extensions can register OpenAI-compatible providers as full agentic backends with tool-call support
- Capability discovery + permission gating per extension
- Slices P–W complete; 836 tests green (682 lib + 154 bin)
- Voice dictation — toggle-driven mic capture wired to the input buffer
/voice slash command with subcommands: models, download <id>, help, rebuild [backend]
- Configurable toggle key — defaults to F8; supported: F8, F2, F12, C-V, C-G (Ctrl+Shift and Ctrl+Alt are unreliable in terminals)
- Toggle-only flow: press once → start listening (🎤 listening pill), press again → stop, transcribe, append to input
- VAD-aware: final transcripts during an armed session insert text without disarming
- Settings → Voice category: toggle key, language cycler (14 langs), STT model, STT backend
voice_language cycler: auto / en / es / fr / de / it / pt / nl / ja / zh / ko / ar / hi / ru
- Hot-reload keybind on settings save — no restart required
- Kitty keyboard protocol enabled for reliable F-key + modifier capture
- Voice sidecar lives in the
local-voice-plugin (synaps-skills repo) — synaps core stays voice-free
- Whisper model manager — discover, download, switch models in-app
- 10-entry catalog hard-coded in
src/voice/models.rs: tiny / base / small / medium / large-v3 / large-v3-turbo + .en variants, with real SHA256s from HuggingFace
/voice models renders an installed/uninstalled table
/voice download <id> — streaming download with atomic .partial → rename install, SHA256 verification, cancellable, single-in-flight
EditorKind::ModelBrowser — Settings → Voice → STT model browses the catalog inline, Up/Down to nav, Enter installs-or-selects
- Inline footer download progress while a model fetches
- Models live under
~/.synaps-cli/models/whisper/
- Whisper backend selection — pick CPU / CUDA / Metal / Vulkan / OpenBLAS or auto-detect
voice_stt_backend cycler in Settings → Voice
auto probes the host (nvcc, vulkaninfo, pkg-config, target-os) and picks the best available
- "Current build: " annotation surfaces the sidecar's compiled feature set
- ⚠ rebuild annotation when the selected backend differs from the compiled one
/voice rebuild [backend] invokes local-voice-plugin/scripts/setup.sh --features <backend> and streams build output as System rows
- Sidecar reports its build via
--print-build-info (JSON: {backend, features, version})
- Chat UI rendering polish
- System / Error messages now split on
\n and word-wrap on each sub-line (same path as User / Text)
- Tab-aware soft wrap: continuation rows indent under the last-tab anchor column (4-col tab stops); safety clamp falls back to leading-space indent if the anchor exceeds 60% of width
- Fixes
/voice help, /chain ls, /keybinds, plugin command output rendering
- Extension System — process-based JSON-RPC hooks (before_message, before_tool_call, after_tool_call, on_session_start, on_session_end)
- Tool-specific filtering: hooks can target specific tools
- Context injection via HookResult::Inject
- Permission-gated with 6 permission types
- Drop-in plugin support
- Silent loading (only show failures)
- Watcher notify_inbox hook — event bus notification on agent completion
- Config option:
[hooks] notify_inbox = true in watcher agent config
- Drops JSON events into
~/.synaps-cli/inbox/ when agents complete
- Works in both 'once' and 'deploy' modes
- Event payload includes agent name, session number, elapsed time, exit code, timestamp
- Table rendering improvements
- Cells wrap instead of truncating with ellipsis
- Inline markdown (bold/italic/code) rendered in table cells
- Smarter column shrinking — preserves narrow columns
- Structural refactoring — improved code organization
catalog.rs → catalog/ directory with per-provider modules
palettes.rs → palettes/ directory with per-palette files
tools/subagent*.rs → tools/subagent/ directory
tools/tests.rs → distributed inline test modules per tool
runtime/api.rs split into api.rs + api_sync.rs + request.rs
chatui/mod.rs helpers extracted to helpers.rs + lifecycle.rs
- UTF-8 char boundary panics — multiple fixes for multi-byte character handling
- Hook output truncation now finds valid char boundaries
- Bash output highlighter fixed for emoji/CJK characters
- Additional edge cases found by PR review
- Hook system improvements
- Handle HookResult::Block in before_message hook
- Set tool_runtime_name in all before_tool_call hook paths
- Track truncation explicitly in bash tool instead of string matching
Added
- Multi-Provider Runtime — OpenAI-compatible provider engine
- 17 providers: Groq, Cerebras, NVIDIA NIM, OpenRouter, Google AI Studio, DeepInfra, HuggingFace, Fireworks, Hyperbolic, Scaleway, SiliconFlow, Together, Chutes, Codestral, Perplexity, OVHcloud, + Local (Ollama/LM Studio/vLLM)
- 55+ models cataloged with tier ratings (S+/S/A+/A/B)
provider/model shorthand: /model groq/llama-3.3-70b-versatile
- SSE streaming with tool calling across all providers
- StreamDecoder with HashMap-based tool call accumulation
- Anthropic↔OpenAI message/tool translation layer
- Provider router in
api.rs — model prefix routes automatically
- Config:
provider.<name> = <key> in ~/.synaps-cli/config
- Env var fallback:
GROQ_API_KEY, CEREBRAS_API_KEY, etc.
- Local model support:
provider.local.url = http://localhost:11434/v1
/ping command — health-check all configured provider models
- Non-blocking, results stream in live as each model responds
- Shows ✅/❌/⏳/🔒/⌛ with latency per model
- Results cached for model picker display
- Settings → Providers — TUI panel for API key management
- View all 17 providers with key status (✅ set / ⬚ not set)
- Enter to set/update key, d/Del to clear
- Key masking (last 4 chars only)
- Local endpoint URL editing
- Press
p to ping all models
- Scrollable list with overflow indicators
- Settings → Model Picker — expanded to show provider models
- Models grouped by provider with headers (── Groq ──, etc.)
- Only shows providers with keys configured
- Header rows are unselectable (skip on navigation)
- Health status shown when ping data available
- App starts without Anthropic credentials — users with only provider keys can boot the TUI and use free models
- Session Naming —
/saveas <name> aliases for sessions
- Name format
[a-z0-9-]{1,40}, validated and collision-checked
/saveas (no arg) clears the name
synaps --continue <name> resolves session names
/sessions shows [@name] tags on named sessions
- Chain Naming —
/chain name/list/unname bookmarks for compaction lineages with auto-advance
/chain name <name> bookmarks the current session's lineage
/chain list shows all named chains (* marks active)
/chain unname <name> removes a bookmark
/chain (no args) shows lineage + "bookmarked by: @name" if present
- Chain pointers stored at
~/.synaps-cli/chains/<name>.json
- On
/compact, chain pointers auto-advance to the new session
- Unified Session Resolution —
resolve_session(): chain name → session name → partial ID, used by --continue and /resume
- Shared by
synaps --continue, /resume, and server --continue
- Resolution path surfaced as a system message (
↳ resolved via chain 'foo' / ↳ resolved via name 'bar')
--continue value_name changed from SESSION_ID to NAME_OR_ID
- Event Bus — universal message ingestion for agent sessions
synaps send CLI command with atomic file writes
- inotify inbox watcher via
notify crate (spawn_blocking, non-blocking)
- Priority EventQueue with severity ordering (Critical→front, High→after)
tokio::sync::Notify for instant TUI wake on event push
- Events auto-trigger model turns when agent is idle
- Events buffer during streaming via
pending_events, flush on completion
- Styled TUI event cards with severity icons (🔴🟠🟡🔵)
- XML-wrapped event format with prompt injection hardening
- 256KB file size cap, symlink guard, 0700 permissions
- Reactive Subagents — dispatch, poll, steer, collect
subagent_start — spawn and return handle_id immediately
subagent_status — non-blocking progress snapshot
subagent_steer — inject guidance mid-flight via steering channel
subagent_collect — non-blocking result check
subagent_resume — restart timed-out agents (stub)
SubagentHandle with shared RwLock<SubagentState>
SubagentRegistry with cleanup_finished on stream Done
- Abort cancels all running reactive subagents
- Thread handles stored for graceful shutdown
- 13 unit tests for handle + registry
- Chain Sessions —
/compact creates linked child session
- Old session preserved on disk with
compacted_into forward link
- New session starts with
parent_session back link
/chain command walks the session lineage
- System prompt included in compaction summary
- Configurable compaction model (defaults to Sonnet)
- ModelPicker for compaction model in settings
- Non-blocking compaction with spinner animation
- Compacted summary hidden from TUI display
- Message queuing during compaction
respond tool (stub — returns honest failure until wired)
send_channel tool (stub — returns honest failure until wired)
/status command + synaps status subcommand: check account usage (5-hour, 7-day, Sonnet) with progress bars and reset countdowns. Hits OAuth usage API.
/compact slash command: summarize & compact conversation history when context gets long
- Structured checkpoint format (goals, progress, decisions, file ops, next steps)
- Iterative compaction — re-compacting merges new work into existing summary
- File operation tracking (read/write/edit paths preserved across compactions)
- Custom focus instructions via
/compact <focus>
- Uses dedicated low-effort API call (no tools, summarization system prompt)
context_window setting wired to API: 200k (default) omits beta header; 1m sends context-1m-2025-08-07 on supported models (Opus 4.6+, Sonnet 4.x); previously was UI-only display cap
- Claude Code marketplace compatibility: probe both
.synaps-plugin and .claude-plugin layouts, ${CLAUDE_PLUGIN_ROOT} substitution in skill bodies
- Plugins subdir sources and cascade uninstall: install from subdir-based plugin repos, cascade-remove plugins when their marketplace is deleted
- Settings → Plugins marketplace overlay: "Open Plugin Marketplace" action row in Settings, opens plugins modal as nested overlay
- Hidden binary: GamblersDen bundled as
hidden binary alongside synaps
- Single binary architecture: all binaries consolidated into
synaps
synaps (no args) = TUI (was chatui)
synaps chat = fully-featured headless mode (MCP, extensions, skills, sessions)
synaps server = WebSocket API (was server)
synaps agent = headless worker (was synaps-agent)
synaps watcher = supervisor (was watcher)
synaps login = OAuth (was login)
synaps send = push events into a running session
synaps status = check account usage
- (Removed:
run, client, daemon)
- Live theme preview in /settings: scroll themes to preview, Enter confirms, Esc reverts
- Theme hot-reload:
/theme <name> applies instantly without restart (ArcSwap)
- Settings picker scroll: theme/model picker scrolls with cursor
- Adaptive thinking for Opus 4.7+:
{type: "adaptive", display: "summarized"} with effort mapping (xhigh/high/medium/low/adaptive)
- Model-agnostic context window: bar denominator adapts per-model (1M Opus 4.7, 200K Sonnet/Haiku)
- Per-turn context tracking: usage bar shows actual request size, not cumulative cost
- Effort parameter: thinking depth on adaptive models controlled via
output_config.effort
- "adaptive" thinking option: new cycler value in
/settings — lets model decide thinking depth
- Tab-cycle for slash commands:
/s + Tab cycles through sessions → settings → system
- Streaming command guard: known slash commands no longer leak into model stream as steering
- Usage log opt-in:
SYNAPS_USAGE_LOG=1 writes to ~/.cache/synaps/usage.log (0600, O_NOFOLLOW)
- Opus 4.6 in model picker: was missing from
/settings model list
settings + plugins in tab-complete: were missing from BUILTIN_COMMANDS
- Plugin Keybinds — plugins declare custom keyboard shortcuts in
plugin.json
KeybindRegistry with parser, matching, and conflict resolution
- Key notation:
C-x (Ctrl), A-x (Alt), S-x (Shift), F1–F12, special keys
- 4 action types:
slash_command, load_skill, inject_prompt, run_script
- User overrides via
keybind.* in config — override or disable plugin binds
- Priority: core > user > plugin (core binds never overridable)
/keybinds command shows all registered binds with source attribution
- 23 unit tests for parser, registry, conflicts, overrides
- Plugin Agent Namespaced Resolution —
subagent(agent: "dev-tools:sage") resolves plugin agents via plugin:agent syntax
- Searches
plugins/<plugin>/skills/*/agents/<agent>.md
- Input validation, path traversal protection, ambiguity detection
- Updated error messages to mention
plugin:agent syntax
- Mouse Text Selection & Clipboard — left-click drag to select text in chat area
- Right-click with selection → copy to system clipboard
- Right-click without selection → paste from clipboard
- Singleton clipboard thread (no thread-per-copy accumulation)
- Paste suppression with 150ms TTL (prevents terminal double-paste)
- Selection highlight via
Block::inner() computed content rect
- Theme-aware highlight color
Fixed
- Empty thinking/text blocks rejected by API — fixed two related 400 errors that could permanently brick a session:
messages.N.content.M.thinking: each thinking block must contain thinking — streaming accumulator (runtime/api.rs) was pushing thinking content blocks even when no thinking_delta arrived (only a signature, or stream cut off). Now skipped at all three accumulation sites (mid-stream, tail buffer, post-loop fallthrough).
messages: text content blocks must be non-empty — defensive sanitizer (runtime/helpers.rs::sanitize_thinking_blocks) added; runs on every outbound API call from both call_api_stream_inner and call_api. Strips empty thinking, redacted_thinking.data, and text blocks; drops assistant messages whose entire content gets stripped; merges resulting consecutive same-role messages to preserve Anthropic's strict role-alternation rule. Auto-heals sessions persisted before the streaming fix landed.
- 9 unit tests covering the drop-and-merge cases.
- Config file permissions — now 0600 (was 0644, world-readable with API keys)
- API key masking — shows last 4 chars only (was showing first 8 + last 4)
- ProviderConfig Debug — custom impl redacts
api_key as [REDACTED] in logs
- Provider base URLs — corrected siliconflow (.com→.cn), chutes (→llm.chutes.ai)
- Dead models removed — groq/llama-3.1-70b, groq/qwen-qwq-32b, groq/gemma2-9b (all decommissioned)
- Google stream_options — skip
include_usage for googleapis.com (rejects it)
- Tool→user message ordering — insert space-content assistant between tool result and user message for strict providers
- Finish-frame tool call dedup — NVIDIA sends final argument chunk with finish_reason; was being dropped as "resend"
- Model ID extraction — health prefix (✅ 253ms) no longer embedded in model string sent to API
/status on non-Anthropic — shows friendly message instead of crashing
- "Calling Claude..." — now shows actual model name in
synaps run
- Paste in settings —
Event::Paste now handled in API key, text, and custom model editors
- Missing provider key —
/model sambanova/llama with no key shows clear error instead of silent Anthropic misroute
- UTF-8 truncation panic —
bash and shell output truncation now finds valid char boundaries before truncating, preventing crash on multi-byte characters (emoji, CJK)
- Zero-width terminal panic — markdown word-wrap
chunks(0) crash on rapid tmux pane resize, clamped to .max(1)
- Local model connection — friendly "is Ollama running?" instead of raw TCP error
/help updated — mentions provider/model syntax and /settings for key management
ping_print lifecycle — resets after all results arrive via pending counter
- MCP tool name causes 400 errors — Anthropic rejects
mcp_ prefixed tool names (rate limit pool misrouting). Renamed mcp_connect → connect_mcp_server, tool prefix mcp__server__tool → ext__server__tool.
/saveas on empty sessions — save_session() bailed on empty api_messages, so the name never persisted. Now calls session.save() directly.
- Compaction no longer overwrites original session (loads from disk)
- Event content sanitized against prompt injection (XML tags, case-insensitive)
- Atomic inbox writes (.json.tmp → .json rename)
- inotify watcher runs on spawn_blocking (no longer starves tokio runtime)
- Events during streaming buffered and flushed after MessageHistory
- Spurious auto-triggers prevented by event_received guard
- push() calls notify_one() (was missing — events silently queued)
- Session save ordering: new session saved before old session updated
- chars().count() moved outside lock scope
- High-priority event FIFO ordering (was LIFO among Highs)
- Queue-full events left in inbox for retry (not silently dropped)
- push_priority logs evicted event ID
- Session file corruption: atomic writes via write-to-tmp then rename
- Tool input parse errors surfaced to model: malformed tool_use JSON no longer silently falls through to empty input; model sees
invalid tool input JSON: ... and can self-correct
- Custom theme crash:
unreachable!() in draw replaced with graceful fallback colors for non-Rgb themes
- ASCII logo alignment: use unicode display width for consistent centering
- 'default' theme missing from settings picker
- Context bar pinned at 100% after 2-3 turns (was using cumulative tokens / hardcoded 200K)
- Thinking blocks invisible on Opus 4.7 (display defaulted to "omitted")
budget_tokens: 0 sentinel leaked to non-adaptive models → 400 error
/settings cycling capped at xhigh (apply_setting silently rejected "adaptive")
- Stale test asserting
thinking_level_for_budget(0) == "low" (production returns "adaptive")
- Usage log world-readable at
/tmp/ → moved to ~/.cache/ with 0600
Changed
- Compaction logic moved from chatui to
core/compaction.rs
SubagentHandle/Registry/Status moved to runtime/subagent.rs
ApiOptions struct replaces use_1m_context: bool threading
build_auth_header + build_beta_header extracted as helpers
clone_repo helper deduplicates plugin installer
- All compaction prompts colocated in
core/compaction.rs
- Tool descriptions guide model choice (subagent vs subagent_start)
- Stub tools unregistered from tool registry (model doesn't see unimplemented tools)
- SubagentState uses RwLock instead of Mutex
define_settings! macro: settings schema + apply handler defined once in settings/defs.rs via declarative macro — zero drift possible (replaced manual sync + parity tests)
- Single source of truth for commands: removed duplicate
ALL_COMMANDS array; commands.rs now sources from skills::BUILTIN_COMMANDS
src/cmd_*.rs → src/cmd/ module: subcommand handlers moved to dedicated directory, cmd_ prefix stripped
- Binary name:
chatui/synaps-cli/synaps-agent → synaps (single binary with subcommands)
src/chatui/main.rs → src/chatui/mod.rs (module, not binary)
- watcher spawns
synaps agent instead of standalone synaps-agent
thinking_level_for_budget() consolidated from 4 copies into single source of truth in core/models.rs
DEFAULT_LEGACY_ADAPTIVE_FALLBACK constant replaces magic 16384 in clamp sites
- Dead-code warnings suppressed with explanatory comments (reaper handles, settings help field)
- Auto-cache toggle removed (manual breakpoints won: 90% vs 53%)
- README rewritten from internal documentation to product landing page
Removed
SPEC-WATCHER.md — internal spec, not needed in repo
- Auto-cache config toggle (
auto_cache = true/false)
Install synaps 0.2.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/HaseebKhalid1507/SynapsCLI/releases/download/v0.2.0/synaps-installer.sh | sh
Download synaps 0.2.0