Replaces the two ~65MB bun-compiled sidecar binaries with a single
~66MB merged binary. The bun runtime + shared dependency code (anthropic
SDK, MCP SDK, ws, undici, etc) was previously duplicated across both —
merging eliminates that duplication entirely.
Combined with the previous P0 commit (static-import inlining + drop
src/ + node_modules/ from Resources), this brings the macOS .app from
the original 435MB baseline down to 87MB (-80%), and the DMG from 113MB
to 37MB (-67%).
Final breakdown of the 87MB .app:
Contents/MacOS/claude-sidecar 66MB (was 57+57=114MB)
Contents/MacOS/claude-code-desktop 18MB (Tauri Rust main)
Contents/Resources/icon.icns 2MB
+ plist + frameworks ~1MB
This is essentially the floor — bun runtime + Tauri main + minimum
overhead. Going lower would require swapping toolchains.
Implementation
==============
* desktop/sidecars/claude-sidecar.ts (new): single entrypoint that
takes a positional mode argument ("server" or "cli") then dispatches
via `await import('../../src/server/index.ts').startServer()` or
`await import('../../src/entrypoints/cli.tsx')`. Same env / argv setup
pattern as the old launchers.
* desktop/sidecars/server-launcher.ts + cli-launcher.ts: deleted.
* desktop/scripts/build-sidecars.ts: compiles only claude-sidecar now.
* desktop/src-tauri/tauri.conf.json: externalBin → ["binaries/claude-sidecar"]
* desktop/src-tauri/src/lib.rs: spawns sidecar with leading "server"
positional arg.
* src/server/services/conversationService.ts resolveBundledCliPath /
resolveCliArgs: when current process is claude-sidecar, reuses the
same exe and spawns it with leading "cli" positional arg. Backward
compat path for old claude-server / claude-cli pair preserved for the
bin/claude-haha dev mode.
Verification
============
* claude-sidecar cli --version → 999.0.0-local (Claude Code) ✓
* claude-sidecar cli --help → full Commander spec ✓
* claude-sidecar server --port N → HTTP listening, CronScheduler running ✓
* All three above run in /tmp with no src/ or node_modules/ on disk
* bun test on src/ → 358 pass / 45 fail / 1 error, identical to baseline
(44 fails are pre-existing on main, unrelated to this change)
* Full DMG round-trip via build-macos-arm64.sh succeeds; new .app
installs cleanly in /Volumes/
Bundle size summary (vs original baseline)
==========================================
metric baseline final delta
.app total 435 MB 87 MB -348 MB (-80%)
.dmg 113 MB 37 MB -76 MB (-67%)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cuts the macOS .app from 435MB → 152MB (-65%) and the DMG from 113MB → 60MB
(-47%) by inlining src/server and src/entrypoints/cli into the bun-compiled
sidecar binaries instead of dynamic-importing them from disk at runtime.
Architectural change
====================
Before:
desktop/sidecars/server-launcher.ts → bun build --compile (≈57MB shell)
└─ at runtime: dynamic file:// import of <appRoot>/src/server/index.ts
which transitively requires ALL of src/ + the entire root node_modules/
to be shipped as Resource. tauri.conf.json copied 254M of node_modules
and 47M of src/ into Contents/Resources/app/ on every build.
After:
desktop/sidecars/server-launcher.ts → bun build --compile (≈65MB)
└─ uses `await import('../../src/server/index.ts')` with a literal
specifier so bun's bundler walks the whole graph statically and
inlines everything into the binary.
Same treatment for cli-launcher.ts → src/entrypoints/cli.tsx.
Resolver gymnastics
===================
This fork carries dozens of ant-internal feature() gated require/import
calls referencing modules that simply don't exist on disk
(cachedMicrocompact, devtools, proactive, coordinator, etc). Bun's resolver
walks the static dep graph BEFORE bun:bundle macro DCE, so even though
the dead branches never execute at runtime, they still fail to resolve
at compile time.
Two complementary mechanisms:
1. desktop/scripts/scan-missing-imports.ts walks src/, regex-greps every
relative import / require / type-import specifier, and writes a Proxy
noop stub for any target that doesn't exist on disk. Stubs are tagged
with "@generated stub from scan-missing-imports" for idempotency. Text
resources (.md / .txt / .json) get appropriate format-specific stubs.
Runs as a pre-step inside build:sidecars.
2. desktop/scripts/build-sidecars.ts adds an `external: [...]` list for
bare-package optional deps not in package.json (OTLP exporters,
@aws-sdk/*, @anthropic-ai/{bedrock,vertex,foundry,mcpb}-sdk,
@azure/identity, fflate, turndown, sharp, react-devtools-core).
These remain runtime imports, fail benignly when their gating env
var or feature flag is off.
Tauri side
==========
- desktop/src-tauri/tauri.conf.json: dropped all `resources` entries.
Was 7 entries totaling ≈301MB. Now `{}`.
- desktop/src-tauri/src/lib.rs `resolve_app_root` no longer calls
BaseDirectory::Resource (the app/ resource dir doesn't exist anymore);
instead returns the directory of the current sidecar exe. The launchers
still accept --app-root for backward compat with conversationService's
CLI subprocess spawn.
Optimisations
=============
- bun build now uses minify whitespace+identifiers+syntax. Saved another
≈16MB across both binaries (server: 72MB→65MB, cli: 75MB→66MB).
Bonus fix
=========
src/services/remoteManagedSettings/index.ts had a typo importing
'./securityCheck.jsx' instead of '.js'. Bun's runtime resolver tolerated
it; bun build didn't.
Verification
============
- Both binaries boot successfully in /tmp with no src/ or node_modules/
on disk. Verified `claude-cli --version` returns the build version,
`claude-cli --help` prints the full Commander spec, and claude-server
starts CronScheduler + listens on the requested port.
- bun test on src/ shows 358 pass / 45 fail / 2 errors vs main baseline
of 359 / 44 / 2 — net 0 new failures (1 different flake direction).
All 44 baseline failures pre-exist on main and are unrelated.
- Full DMG round-trip via build-macos-arm64.sh succeeds; new bundle
installs cleanly in /Volumes/.
Bundle size summary
===================
metric baseline after P0 delta
Resources/app/ 301 MB 0 MB -301 MB
MacOS/claude-server 57 MB 65 MB +8 MB
MacOS/claude-cli 57 MB 66 MB +9 MB
MacOS/claude-code-desktop 18 MB 18 MB —
─────────────────────────────────────────────
.app total 435 MB 152 MB -283 MB (-65%)
.dmg 113 MB 60 MB -53 MB (-47%)
Generated stub files (173 of them under src/) are committed for
reproducibility — the scanner is idempotent and will re-create them
identically on every build, but tracking them avoids dirty working trees
on first compile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task card was expanded by default and overlapped the main chat area
whenever todos appeared. Start collapsed and reset expanded state on
clearTasks so new sessions also begin collapsed; users can click the
header to expand when needed.
This folds together the desktop-side fixes needed before broader rollout.
Session resume no longer deadlocks waiting on init, Mermaid and inline image
output render inside chat, task and sub-agent state stay visible during
execution, local build/release paths are safer, and Feishu/Telegram now expose
lightweight mobile commands (/help, /status, /clear) without adding a new
adapter-specific protocol.
Constraint: Desktop releases must publish updater artifacts from non-draft GitHub releases
Constraint: IM commands need short, phone-friendly responses and low operational complexity
Rejected: Add a dedicated IM command API surface | re-used existing slash commands and session/task REST endpoints to keep adapters thin
Rejected: Wait for task_update push events in WebUI | added low-risk polling because the current frontend ignores that event path
Confidence: medium
Scope-risk: broad
Reversibility: clean
Directive: Keep IM command replies terse and mobile-first, and merge local fallback slash commands when server-provided lists are partial
Tested: cd desktop && bun x vitest run src/components/chat/MermaidRenderer.test.tsx src/components/markdown/MarkdownRenderer.test.tsx
Tested: cd desktop && bun x vitest run src/components/chat/composerUtils.test.ts src/pages/ActiveSession.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "SDK init arrives only after the first user turn" --timeout 60000
Tested: cd adapters && bun test common/ feishu/ telegram/
Tested: cd adapters && bunx tsc --noEmit
Not-tested: Full GitHub Actions release run on all three desktop platforms
Not-tested: Local DMG packaging end-to-end on Apple Silicon
Not-tested: Real Feishu/Telegram device sessions against a live adapter process
Add a protocol-translating reverse proxy that allows using OpenAI-compatible
API providers (DeepSeek, OpenRouter, Groq, etc.) with Claude Code.
The proxy intercepts Anthropic Messages API requests from the CLI, transforms
them to OpenAI Chat Completions or Responses API format, forwards to the
upstream provider, and transforms streaming/non-streaming responses back.
Key features:
- Request transform: Anthropic Messages → OpenAI Chat/Responses
- Response transform: OpenAI → Anthropic (streaming SSE + non-streaming)
- Provider-agnostic reasoning support (reasoning_content, thinking_blocks,
reasoning fields from DeepSeek, OpenAI o-series, GLM-5, Groq, etc.)
- Event queue pattern for correct Anthropic SSE event ordering
- Two-step test: ① connectivity check ② full proxy pipeline validation
- Desktop UI: API format selector, two-step test results display
- License attribution for cc-switch (MIT, Jason Young)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop Chinese spinner verb array and locale-based selection. StreamingIndicator
now uses English status verbs directly without going through the i18n system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the Copy button from always-visible below messages to the
bottom-right side of each message bubble, only appearing on hover.
Reduces visual clutter in chat conversations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of close tab → delete session → create session → open tab (which
causes visual flicker and tab jumping), create the new session first then
replace the tab's sessionId in-place via a new replaceTabSession method.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update AgentDefinition type with agentType, source, isActive, modelDisplay fields
- Refactor agentStore to support activeAgents/allAgents with cwd parameter
- Add i18n strings for agent browser UI (source labels, summary, status)
- Update server agents API with serialization helpers and override resolution
- Update tests to match new agent data structure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Agent list items: replace dot with colored smart_toy icon, bold name
- Agent description: use MarkdownRenderer for rich text support
- Remove max-width constraints on Agent/Skill containers for responsive layout
- Dynamically switch grid columns based on visible group count (single group fills full width)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Improve the desktop Skills browser so SKILL.md metadata renders cleanly and the settings view uses space like a real document browser. Add coverage for the new detail, markdown, and i18n behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Server: GET /api/skills (list) and GET /api/skills/detail (tree + files)
- Desktop: Skills tab with grouped list, file tree navigation, Markdown/code preview
- i18n: EN/ZH support
- Types, API client, Zustand store
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add API client, Zustand store, and i18n for agents list
- Display installed agents from ~/.claude/agents/ with detail view
- Render agent system prompt as Markdown via MarkdownRenderer
- Include error state with retry and empty state guidance
- Add 11 component tests covering all UI states and navigation
- SkillList: grouped display by source with icons and token estimates
- SkillDetail: two-panel layout with file tree navigation + Markdown/code preview
- Settings: integrated Skills tab with auto_awesome icon
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Server: GET /api/skills (list) and GET /api/skills/detail (tree + files)
- Desktop: type definitions, API client, Zustand store
- i18n: EN/ZH translation keys for Skills tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Tab drag-and-drop reordering with visual drop indicator
- Add moveTab action to tabStore with localStorage persistence
- Context menu: add "Close Left" and "Close All" options
- Add i18n translations for new menu items (en/zh)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TabBar: darker background (surface-container), active tab uses top brand
accent + lighter bg, consistent bottom border, draggable empty area
- Settings sidebar width matches TAB_WIDTH (180px) for vertical alignment
- Remove rounded corners and side borders from tabs for cleaner look
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 38px absolute drag overlay was blocking tab clicks, requiring
padding that created a visible gap. Sidebar already handles its own
drag region, and TabBar uses data-tauri-drag-region directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move Tauri title bar padding into TabBar instead of main container,
eliminating the double spacing (38px gap + 36px tab bar)
- Remove StatusBar entirely (project/model info already in ChatInput)
- TabBar renders a drag region spacer even when no tabs are open
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Settings and Scheduled Tasks now open as special tabs in the TabBar,
matching IDE conventions (closeable, icon-prefixed, persistent)
- New Session directly creates a session with the current workDir
- ChatInput shows DirectoryPicker before first message, locks after
- Remove connection status from StatusBar (unnecessary noise)
- ContentRouter routes by tab type instead of activeView
- Settings page no longer has a back-button header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove .claude/ and docs/superpowers/ from git tracking as they are
local-only config and planning files. Also remove stale screenshots
and update docs/channel content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update all components, hooks, and tests to use the new per-session
chatStore API where state is keyed by sessionId under `sessions` and
all action methods require sessionId as the first parameter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both components now look up state via activeTabId from useTabStore,
reading from sessions[activeTabId] in chatStore. Action calls
(sendMessage, stopGeneration) pass the sessionId as first argument.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add useTabStore.openTab call after session creation so new sessions are
registered as tabs, and update sendMessage to pass sessionId as first arg.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add TabBar, useTabStore, useChatStore imports to AppShell
- Restore tabs from localStorage during bootstrap and connect active session
- Render <TabBar /> before <ContentRouter /> in the main content area
- Replace useSessionStore/activeSessionId with useTabStore/activeTabId in ContentRouter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The desktop chat view flattened Agent tool activity, which made sub-agent work
hard to follow and separated key evidence from the main conversation. This
change threads parent tool linkage through the server bridge and desktop store,
renders dispatched sub-agents as grouped cards with nested tool activity, and
moves long final outputs into a markdown preview dialog so the main transcript
stays readable on narrow layouts.
Constraint: Existing sessions and live websocket events both needed to preserve parent-child relationships
Rejected: Add brand-new subagent websocket event types | unnecessary protocol expansion when parent linkage already existed upstream
Rejected: Inline full sub-agent markdown in the card body | too cramped for narrow desktop chat layouts
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep Agent card summaries compact; route long-form sub-agent output through the preview dialog unless the main chat layout is widened substantially
Tested: cd desktop && bun run test -- MessageList.test.tsx chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/sessions.test.ts -t should\ reconstruct\ parent\ agent\ tool\ linkage\ from\ parentUuid\ chains
Not-tested: Full end-to-end visual verification against live CLI sessions with sub-agent text/thinking nested inline
The session view exposed copy affordances in a way that implied the
whole conversation would be copied, while user prompts had no copy
path at all. This changes copy to a per-message footer action so the
scope is explicit for both prompts and assistant replies, and reuses
the shared clipboard helper instead of maintaining a second copy path.
Constraint: The worktree contains unrelated in-progress changes that must stay uncommitted
Rejected: Keep a floating action above each message | it interrupted reading order and still looked session-scoped
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep copy affordances attached to individual message blocks so the copied scope stays obvious
Tested: bun run test src/components/chat/MessageList.test.tsx; bun run lint
Not-tested: Manual desktop visual QA across dense multi-message sessions
Previously, creating a new session without selecting a project directory
defaulted to process.cwd(), which in the desktop app resolves to the app
installation path. Now it defaults to os.homedir().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add core🪟allow-start-dragging permission and acceptFirstMouse
config to fix window dragging on macOS with overlay title bar. Also add
JS-based startDragging() fallback in AppShell and Sidebar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use rejection sampling to ensure uniform distribution across the
safe alphabet. Values >= 232 are rejected and resampled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After the first assistant response, derive a quick placeholder title from
the user message, then asynchronously call the provider's Haiku model to
generate a polished 3-7 word title. Titles update again at message 3 with
fuller conversation context. Updates push to frontend in real-time via
WebSocket `session_title_updated` event.
- extractTitle now reads `ai-title` JSONL entries (priority: custom > ai > first message)
- New titleService with deriveTitle + generateTitle using active provider config
- Handler tracks per-session message count and triggers generation on result
- Frontend sessionStore receives live title updates for sidebar + header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement IM adapters allowing users to chat with Claude Code from Telegram
and Feishu/Lark. Includes persistent session management (chatId→sessionId
mapping), project selection via /projects command, and a web UI settings page
for configuring bot tokens, allowed users, and default project directory.
Key changes:
- adapters/: Telegram and Feishu adapter scripts with shared common modules
(WsBridge, MessageBuffer, SessionStore, HttpClient, config, formatting)
- Backend: adapterService + REST API (GET/PUT /api/adapters) with secret masking
- Frontend: AdapterSettings page in Settings tab with i18n support
- DirectoryPicker: use React Portal for dropdown to fix overflow clipping
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 7 frequency modes (every N min/hours, daily, weekdays, specific days, monthly, custom cron) with progressive disclosure UI
- Add "Run Now" button with confirmation popover and fire-and-forget API
- Add execution logs panel (TaskRunsPanel) with auto-polling and accordion behavior
- Add task edit mode with cron reverse-parsing (parseCron) to populate form
- Add server-side extractAssistantText to store meaningful AI responses instead of raw NDJSON
- Fix session linking: pass --session-id to CLI subprocess so "View conversation" navigates to actual content
- Fix MACRO undefined error by adding --preload to Bun.spawn
- Add confirmation popovers for all destructive actions (run/disable/delete)
- Add DayOfWeekPicker component for specific-days scheduling
- Add cronDescribe utility with i18n support and unit tests
- Display task creation time and last run time
- Add ~50 i18n keys (en/zh) for all new UI elements
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>