This merges the detached-worktree desktop fixes back into main, preserving the
existing Computer Use modal changes on main while bringing over the project
skill discovery fallback, skills settings cwd-awareness, sidebar tab cleanup,
and the empty-session hero composer styling.
Constraint: Main already carried newer desktop session UI changes that had to be preserved during merge resolution
Rejected: Abort the merge and cherry-pick individual files | unnecessary once the only conflict was isolated to ActiveSession.tsx
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: When merging detached-worktree desktop branches into main, keep ActiveSession conflict resolution additive so session overlays and composer variants survive together
Tested: bun test src/server/__tests__/sessions.test.ts; cd desktop && bun run test -- --run src/__tests__/skillsSettings.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx src/pages/ActiveSession.test.tsx; cd desktop && bun run lint
Not-tested: Manual post-merge GUI verification in the local main worktree app bundle
The desktop app previously lost project-level skills in two places: the
settings browser only queried user skills, and the slash-command picker for a
fresh session depended on CLI init state that does not exist before the first
turn. This change makes the skills APIs cwd-aware, falls back to project skill
loading before CLI init, syncs sidebar session deletion with open tabs, and
aligns the empty-session composer styling so the pre-message session UI stays
consistent.
Constraint: Fresh desktop sessions need slash-command discovery before the CLI websocket emits system/init
Constraint: Only repository code should be committed; build artifacts, installs, and /tmp smoke fixtures stay out of git
Rejected: Rebuild slash-command listings from full getCommands() in the sessions API | introduced auth-gated command dependencies unrelated to local skill discovery
Rejected: Unify EmptySession and ChatInput into one full component now | higher regression risk for normal chat interactions than a visual-only hero variant
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep pre-message skill discovery keyed to session workDir so project-level .claude/skills remain visible before the first turn
Tested: bun test src/server/__tests__/sessions.test.ts; cd desktop && bun run test -- --run src/__tests__/skillsSettings.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx src/pages/ActiveSession.test.tsx; cd desktop && bun run lint
Not-tested: Manual desktop smoke test against the rebuilt .app bundle in the local GUI
Desktop sessions were missing a visible request_access approval path and could
mis-detect their own app window as an unapproved frontmost target, which caused
Computer Use clicks to fail even after opening the intended app. On macOS, text
entry was also split across inconsistent clipboard and keystroke paths, making
Electron inputs unreliable for Chinese and short strings.
This change adds a desktop approval bridge over the existing session websocket,
renders a dedicated desktop approval modal, threads the real desktop bundle id
into the Computer Use executor, and switches macOS clipboard typing onto the
native pasteboard plus system paste shortcut path. It also makes tool error
results expandable in the desktop chat UI so frontmost-gate failures are fully
visible during debugging.
Constraint: Desktop sessions run the CLI over the SDK websocket path, so Ink tool JSX dialogs are not visible there
Constraint: macOS IME and Electron text inputs are unreliable with pyautogui.write and generic hotkey synthesis
Rejected: Reuse CLI setToolJSX dialogs in desktop mode | no transport for mid-call Ink UI over the SDK bridge
Rejected: Keep shell pbcopy/pbpaste for clipboard typing | inconsistent with NSPasteboard path and less reliable for Chinese text
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep desktop Computer Use approvals and macOS text-entry behavior on a single bridge/path; avoid reintroducing separate CLI-only and desktop-only codepaths for the same action
Tested: python3 -m unittest runtime/test_helpers.py
Tested: bun test src/utils/computerUse/permissions.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test ComputerUsePermissionModal chatStore
Tested: cd desktop && bun run test chatBlocks
Tested: cd desktop && bun run lint
Not-tested: End-to-end manual Computer Use interaction against a live Electron target app on macOS
Windows desktop sessions were failing during early hook execution because the
runtime assumed Git Bash was always installed. That made a missing Git
installation look like a fatal CLI startup regression even though PowerShell is
usually available and sufficient for default hook execution.
This change turns Git Bash lookup into a best-effort probe, leaves SHELL unset
when Git Bash is missing, and falls back to PowerShell for default Windows hook
execution. Explicit bash hooks still fail with a direct installation hint, but
they no longer hard-exit the whole process.
Constraint: Windows users may have PowerShell but no Git for Windows installed
Rejected: Keep hard-exiting on missing Git Bash | makes first-run desktop chat fail for a recoverable missing dependency
Rejected: Bundle Git for Windows into the app | larger distribution and higher maintenance burden than a runtime fallback
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Treat Git Bash as an optional Windows capability unless a code path explicitly requires bash semantics
Tested: bun test src/server/__tests__/conversation-service.test.ts
Tested: bun -e "await import('./src/utils/windowsPaths.ts'); await import('./src/utils/hooks.ts'); console.log('module-import-ok')"
Not-tested: End-to-end desktop chat startup on a clean Windows Server host without Git installed
Not-tested: Explicit bash hook execution on Windows after the fallback path
Related: GitHub issue #62
Local main was already updated to origin/main at 8f0e46e, and this merge brings those buddy/docs/env changes onto feat/dev-desktop. The only conflict was .github/workflows/build-desktop-dev.yml, where both branches added the workflow; the resolved version keeps the mainline body and retains the libfuse2 Linux dependency needed for AppImage-oriented tooling.
Constraint: Merge was already in progress from local main at 8f0e46e
Rejected: Abort and re-run merge from scratch | existing index already isolated the single conflict cleanly
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep libfuse2 in the desktop dev build workflow unless Linux packaging is revalidated without it
Tested: git fetch origin main; git diff --check; manual resolution of build-desktop-dev workflow conflict
Not-tested: Root TypeScript typecheck because existing generated desktop/src-tauri/target and extracted-natives assets are included and fail independently of this merge
The desktop chat store was dropping the previous assistant draft when a new user turn began because streaming text was cleared before it was flushed into message history. At the same time, the desktop OAuth flow was using an unregistered /api/haha-oauth/callback redirect URI, which caused provider authorization failures. This change flushes visible assistant drafts before starting a new turn, restores the OAuth redirect to the registered localhost callback, and adds a root callback handler while keeping the legacy API callback path compatible.
Constraint: The OAuth provider only accepts the registered localhost /callback redirect URI
Rejected: Keep the desktop-specific /api/haha-oauth/callback path | provider rejects unsupported redirect URIs
Rejected: Rely on message_complete to persist visible assistant text | next user turn can begin before that event arrives
Confidence: high
Scope-risk: moderate
Directive: Any UI-visible assistant draft must be flushed into messages before a new user turn resets streaming state
Tested: bun test src/server/__tests__/haha-oauth-service.test.ts src/server/__tests__/haha-oauth-api.test.ts src/server/__tests__/conversation-service.test.ts
Tested: cd desktop && bun run test src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Not-tested: Manual end-to-end desktop OAuth login after reinstall
Desktop auth was reporting stale local token files as logged in, could inherit a parent OAuth token after logout, and kept polling even when browser launch failed. This change validates stored tokens through the refresh path, clears inherited OAuth env before spawning the CLI, fetches subscription metadata on initial login, and moves polling start until after the browser opens. Regression tests cover the server and desktop store paths.
Constraint: Desktop CLI must bypass macOS Keychain by injecting env OAuth when available
Rejected: Keep status endpoint file-based only | reports expired sessions as logged in
Confidence: high
Scope-risk: moderate
Directive: Official desktop auth state must be derived from ensureFreshTokens rather than oauth.json presence alone
Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/haha-oauth-service.test.ts src/server/__tests__/haha-oauth-api.test.ts
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test
Not-tested: Manual end-to-end desktop OAuth flow against the live provider
This bundles the pending desktop/server team-session fixes with the local adapter recovery changes already in the worktree. The team path now keeps teammate membership stable under concurrent spawns, surfaces real teammate identities in the desktop UI, and allows direct interaction with member transcripts. The adapter changes recover automatically when stale thinking signatures invalidate an existing session.
Constraint: Team config writes can happen concurrently while multiple reviewers spawn in parallel
Constraint: Desktop member views must follow mailbox/transcript semantics rather than hijacking teammate runtime sessions
Rejected: Keep relying on config.json alone for member discovery | in-process teammates can be lost after concurrent writes
Rejected: Open teammate sessionIds as normal desktop sessions | would attach a second CLI instead of the running teammate
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Preserve locked team-file mutation for any future teammate registration path and keep teammate labels sourced from member names before agent types
Tested: bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts
Tested: cd desktop && bun run test --run src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run build
Not-tested: Manual end-to-end validation against a live Agent Teams run in the desktop app
- security: XSS sanitization with DOMPurify in Markdown/Mermaid/PermissionDialog;
path whitelist in filesystem API; fake keys in test/config files
- perf: fine-grained Zustand selectors in Sidebar/StatusBar/ContentRouter;
50ms throttle on streaming deltas; React.memo + useMemo in MessageList;
useRef for frequent keyboard shortcut state; AbortController 30s timeout
- leaks: WS session TTL timers (5-min cleanup on close); batch splice for
sdkMessages/stderrLines; folderPath validation in cronScheduler
- quality: optimistic update rollback in settingsStore; error state in
providerStore/teamStore; i18n for all hardcoded English strings
- docs: desktop architecture and features docs updated; VitePress nav fixed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
- Fix WebSocket race condition: wait for connection to open before
sending first message, preventing silent message loss after pairing
or project selection
- Fix response text appearing above tool calls: finalize placeholder
before tool_use blocks so post-tool text gets a new message
- Remove noisy tool_use and tool_result messages from IM output;
thinking indicator is preserved, details visible in Desktop
- Fix /new command using default project dir instead of always showing
project picker
- Fix duplicate projects in list by deduping on realPath instead of
projectPath
- Improve formatToolUse with human-readable summaries for common tools
- Add send failure feedback to users
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>
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: 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>
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
Cron task metadata writes can overlap closely enough that a timestamp-only
$temp filename is not stable. Harden the atomic write path by using a
collision-resistant temp name and retrying once when rename reports ENOENT.
Constraint: Scheduled task persistence must keep atomic replace semantics in ~/.claude/scheduled_tasks.json
Rejected: Add explicit file locking around every scheduled task write | more coordination overhead than the observed transient rename race warrants
Confidence: high
Scope-risk: narrow
Directive: Keep scheduled task writes collision-resistant and retriable; do not revert to timestamp-only temp file names without reproducing concurrent writes
Tested: bun test src/server/__tests__/scheduled-tasks.test.ts
Not-tested: bun test src/server/__tests__/cron-scheduler.test.ts (existing timeouts and unrelated assertion failures in this checkout)
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>
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>
executeTask() lacked a runningTasks guard, so manual "Run Now" or
server restarts could spawn a second subprocess while one was already
running. Now checks runningTasks at entry and skips if already in flight.
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>
- Input @ in composer to open file search popup showing project root
- Filter files/directories by typing, navigate with keyboard or mouse
- Click directory to enter subdirectory, type / to navigate deeper
- Enter selects item and inserts path text at cursor, popup closes
- Parse filter path (e.g. @src/components/) to auto-navigate and search
- Desktop: filesystem browse/search API supports includeFiles and search
- Web: directory tree browser with real-time search filtering
The CLI subprocess runs in --print (non-interactive) mode, which disabled
V2 task tools (TaskCreate/TaskUpdate). Only TodoWrite was available, but
the frontend only tracked V2 tool names for task refresh — so the Tasks
bar never appeared for new WebApp sessions.
Changes:
- Set CLAUDE_CODE_ENABLE_TASKS=1 in CLI subprocess env to enable V2 tasks
- Add TodoWrite to TASK_TOOL_NAMES for backward compat with V1 sessions
- Parse TodoWrite input.todos directly into TaskBar state (no disk read)
- Extract last TodoWrite from history on session load for V1 sessions
- Inline completed task summary into message flow when user continues chat
(sticky TaskBar converts to scrollable inline summary)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>