Closed
Conversation
During app exit, session.close() uses unref'd timers to schedule subprocess termination, which means app.exit(0) tears down NAPI modules while the CLI subprocess is still alive and sending callbacks through its NAPI threadsafe function — causing a fatal napi_ref_threadsafe_function crash during node::FreeEnvironment. Fix by directly SIGTERM-ing the subprocess and awaiting its exit before calling session.close(), ensuring no NAPI callbacks are in-flight when the native module is destroyed. Also stop @parcel/watcher file watchers early in the will-quit handler to prevent its NAPI threadsafe functions from firing during teardown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement persistent scheduled tasks that survive app restarts, matching Claude Desktop's local scheduled task capability. Features include: - Backend ScheduledTaskService with disk persistence, CRUD operations, interval/daily/weekly scheduling, missed-run catch-up (7-day window), deterministic jitter, and auto-approve permissions - Gateway WebSocket API (6 request types + 3 notification types) - Frontend sidebar section with countdown timer, action buttons, and collapsible animation using CSS grid-template-rows trick - Create/edit modal with frequency presets (5m-12h intervals, daily, weekly multi-day select) - Graceful shutdown with running task wait (5s timeout) and autoApproveSessions size-limit fallback to prevent memory leak - Notification toast repositioned to bottom-right to avoid title bar - Projects section title for clear visual separation in sidebar - i18n support for English, Chinese, and Russian Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tent before approval 1. Skip group name update when session.updated carries no title (e.g. Claude engine emitting engineMeta/ccSessionId sync), preventing meaningful titles from being overwritten with "New Session". 2. Flush streaming text buffer before showing ExitPlanMode approval question, so users can review the full plan content before deciding to approve or reject. 3. Add StreamingController.flushAsIntermediateReply() for updating the streaming message with accumulated text without marking completion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix i18n: use formatMessage() for validation errors and task failure messages instead of string concatenation - Fix types: replace `any` with proper types in Modal and Chat handler - Fix NotificationToast stacking order (flex-col-reverse → flex-col) - Remove unused `loading` field from scheduled-task store - Relax ScheduledTask.name comment (kebab-case → display name) - Always show create button in collapsed sidebar mode - Add missing i18n keys to all locales (directoryPlaceholder, lessThanOneMinute, fieldRequired, daysRequired) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add explicit type annotations to scheduled task callbacks in Chat.tsx to fix TS7006 implicit-any errors - Use type assertions for createScheduledTask/updateScheduledTask to fix TS2345 union-type incompatibility - Add aria-label to icon-only buttons in ScheduledTaskSection for screen-reader accessibility - Add unit tests for computeNextRun() covering interval, daily, weekly frequencies, jitter capping, default values, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mprove message loading merge - Refactor SessionSidebar to render default workspace above project divider - Add collapsed mode icon and search filtering for default workspace - Move add-project and refresh buttons into section headers - Add scheduled tasks enabled/disabled toggle in Settings - Persist scheduledTasksEnabled setting and sync with store - Improve loadSessionMessages to merge disk data with streaming state - Preserve store-only messages (unflushed placeholders) during disk load - Add i18n keys for scheduled tasks feature toggle (en/zh/ru) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: support Lark in Feishu channel Add platform selection to the existing Feishu channel, pass the selected SDK domain into the Lark SDK clients, improve startup readiness for long connections, and document the Feishu/Lark permission templates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address PR feedback for Feishu startup Avoid unnecessary adapter restarts on unchanged config saves, suppress noisy WS info logging while still detecting readiness, and clean up the startup monitor on early failures to prevent dangling timeout/rejection state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: polish Lark platform copy Use platform-aware runtime log labels for the shared Feishu/Lark adapter and align the Russian locale strings with the new platform selector copy.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Lark log scope for Lark channel Switch the shared Feishu/Lark channel logger scope dynamically so runtime log prefixes use lark when the configured platform is Lark, and thread that logger through the adapter helpers with test coverage.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address websocket startup review feedback Refine the Feishu/Lark WS startup monitor to keep SDK diagnostics visible, add a weaker start-resolution fallback if the SDK log markers change, simplify config merging without reintroducing undefined overwrites, and make the channel name platform-aware with targeted unit coverage.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Active section above Default Workspace/Projects showing non-idle sessions - Pin/Unpin any session to force it into/out of Active section - 5s delayed fade-out when viewing completed/error/cancelled sessions - Pin/Unpin button added to all session hover actions - Pin icon indicator on pinned sessions - Collapsed sidebar shows activity count badge - Search filters Active section like other groups - i18n: en/zh/ru translations for activeSection/pinSession/unpinSession - 15 unit tests for filtering, pin lifecycle, delayed removal, search Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Active section now uses collapsible-grid pattern matching Default Workspace - Chevron toggle with default expanded state - Add hr dividers between Active/Default Workspace/Projects sections - Active section defaults to expanded (unlike projects which default collapsed) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix race condition: compute session status before clearing unread/dismissed - Add isEngineEnabled filter to activeSessions computation - Fix Active section toggle needing 2 clicks (dedicated toggle logic) - Extract computeActiveSessions and filterSessionsBySearch into shared module - Tests now import production logic instead of re-implementing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: bump version to 1.5.2 Changes since v1.5.1: - feat: add Active Sessions section in sidebar - feat: default showDefaultWorkspace to true - feat: separate default workspace group, add scheduled tasks toggle - feat: add desktop-level scheduled tasks with full lifecycle management - feat: intercept ExitPlanMode to require user plan approval (realDuang#67) - feat: enhance CI/CD pipelines, add ESLint, and create project website (realDuang#64) - feat: Support Lark in the Feishu channel (realDuang#69) - refactor: centralize port config & graceful Claude engine shutdown (realDuang#66) - fix: kill CLI subprocesses before shutdown to prevent NAPI crash - fix(feishu): prevent title overwrite and flush plan content before approval - fix: address PR review feedback for scheduled task feature - style: make Active section collapsible with dividers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: update electron-builder linux desktop config and add linux cloudflared support - Move linux.desktop properties under 'entry' key for electron-builder 26.8.1 compatibility - Add linux-x64 platform to cloudflared download script - Add linux extraResources for cloudflared binary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add linux maintainer for deb package build Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove ELECTRON_RUN_AS_NODE from Copilot CLI subprocess env In packaged builds, ELECTRON_RUN_AS_NODE leaks into the Copilot CLI subprocess environment, causing the CLI to malfunction and its stdin stream to be destroyed prematurely ('Cannot call write after a stream was destroyed'). Claude and OpenCode adapters already clean this up; apply the same fix to the Copilot adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: resolve copilot CLI binary from asar.unpacked in packaged builds In packaged Electron builds, import.meta.resolve returns paths inside app.asar. Electron's patched fs.existsSync reports these files exist, but the OS cannot execute binaries from inside an asar archive. This caused the Copilot CLI process to fail immediately on spawn, resulting in 'Cannot call write after a stream was destroyed'. Fix: convert app.asar paths to app.asar.unpacked paths, where electron-builder extracts the copilot binaries via asarUnpack config. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ADMEs Sync project structure diagrams with actual codebase — add missing directories (shared/, tests/, docs/, website/, homebrew/, locales/, channels/streaming/, utils/) and link contributing section to CONTRIBUTING.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add engine-agnostic git worktree management to CodeMux, allowing users
to create isolated worktrees per project for parallel development.
Core infrastructure:
- Slug generator (adjective-noun naming, e.g. 'brave-cabin')
- WorktreeStore for persistent worktree metadata
- WorktreeManager for git worktree CRUD and merge operations
- Worktrees stored in {appData}/codemux/worktrees/{projectId}/
Gateway protocol:
- 5 new request types: worktree.create/list/remove/merge/listBranches
- 3 new notification types for worktree lifecycle events
- Session creation supports worktreeId parameter
Frontend:
Session
- WorktreeModal for create/delete/merge operations
- Settings toggle (worktreeEnabled, default off)
- Full i18n support (en/zh/ru)
Branch naming: codemux/{slug}, base branch selectable at creation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Adjust trafficLightPosition from (16,16) to (13,14) for vertical centering - Increase MACOS_TITLE_BAR_HEIGHT from 38px to 40px to match traffic light center - Increase left padding from 72px to 78px for better horizontal spacing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 'Manage Worktree' button (git-branch icon) in project group header - Wire WorktreeModal to sidebar via worktreeModalDir signal - Update handleNewSession in Chat.tsx to pass worktreeId parameter - Import gateway in sidebar for worktree list loading on mount - Auto-load project worktrees when feature is enabled Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move WorktreeModal rendering from SessionSidebar (overflow:hidden trap) to Chat.tsx at the same level as other modals (full-screen overlay) - Add onManageWorktrees callback prop to SessionSidebar - Lazy-init WorktreeManager on first use (ensureInit) to fix ERR_INVALID_ARG_TYPE when worktreeBase was undefined Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add parentDirectory to ConversationMeta for worktree session→project binding - deriveProjects() skips worktree sessions (no spurious projects after restart) - convToSession() resolves projectId from parentDirectory for worktree sessions - Merge uses updateTargetBranch() helper: detects if target is checked out and uses git merge instead of git fetch (fixes 'refusing to fetch' error) - Support merge/squash/rebase modes with custom commit message - Worktree deletion cleans up all linked sessions via engineManager.deleteSession() - Lazy-init WorktreeManager (ensureInit) to avoid ERR_INVALID_ARG_TYPE - resolveProjectId uses directory name instead of commit hash Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Simplify WorktreeModal to create-only (auto-close on success) - Add MergeWorktreeModal: target branch selector, merge/squash/rebase mode radio cards, custom commit message, inline result display - Add DeleteWorktreeModal: impact summary (files, branch, sessions), irreversible warning, styled like HideProjectModal - Unify sidebar hover buttons: project and worktree rows use absolute overlay with backdrop blur (same pattern as session rows) - Restore project path subtitle alongside session count badge - Filter worktree sessions from sidebar when feature is disabled - Load worktrees reactively on project list changes - Refresh worktrees on session list refresh Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 12 new i18n keys across en/zh/ru for: - Merge modal: mergeMode, modeMergeDesc, modeSquashDesc, modeRebaseDesc, mergeMessage, merging - Delete modal: deleteConfirmTitle, deleteWarningTitle, deleteImpactFiles, deleteImpactBranch, deleteImpactSessions, deleteIrreversible Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: multiple bug fixes - fix: update OpenCode install URL from install.sh to install - fix: always show Projects section in sidebar so Add Project button is accessible when no projects exist yet; show 'No projects yet' placeholder - fix: refresh engine list on engine status change to avoid stale engine info in configStore - fix: re-throw error in handleAddProject so AddProjectModal can surface failure to the user Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: preserve caught error cause to satisfy preserve-caught-error lint rule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ubuntu <xinyul@openclaw.ik1yadxmgfeunbvontudimgtwd.lx.internal.cloudapp.net> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: add slash command engine passthrough
Implement transparent slash command invocation across all three engines
(Claude Code, GitHub Copilot, OpenCode), allowing users to type engine-
native commands directly in the chat input.
- Types: Add EngineCommand, CommandListRequest, CommandInvokeRequest,
CommandInvokeResult to unified type system; extend EngineCapabilities
with slashCommands flag; add COMMAND_LIST/COMMAND_INVOKE request types
and COMMANDS_CHANGED notification type
- Engine adapters: Add listCommands() and invokeCommand() to base
EngineAdapter with safe defaults; implement per-engine discovery and
invocation (Claude via embedded text, Copilot via skills API, OpenCode
via command REST API with sendMessage fallback)
- Gateway: Route COMMAND_LIST and COMMAND_INVOKE through EngineManager;
broadcast commands.changed events to all clients
- Client API: Add listCommands() and invokeCommand() RPC methods to
gateway-client and gateway-api layers
- Frontend: Add autocomplete dropdown to PromptInput triggered on '/'
with keyboard navigation (ArrowUp/Down/Tab/Enter/Escape); add command
state management to Chat.tsx with availableCommands signal, engine-
change fetch, and handleCommandInvoke handler with optimistic UI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct SDK API usage in slash command adapters
Claude Code:
- Fix init message field: use `slash_commands: string[]` (correct SDK
type) instead of `msg.commands` which doesn't exist
- Enrich command names with descriptions from well-known commands list
since the init message only provides names without metadata
- Extract getWellKnownCommands() for reuse between init and fallback
GitHub Copilot:
- Add command.execute event handler that acknowledges commands via
session.rpc.commands.handlePendingCommand() — required by the SDK
protocol to unblock the CLI after command dispatch
- Add command.completed event handler (logging)
- Add commands.changed event handler to dynamically refresh the cached
command list when the CLI updates available commands
- Document the command flow in invokeCommand() comments
OpenCode:
- Remove unnecessary `(client.session as any).command()` cast — the SDK
v2 types already expose `session.command()` with full type safety
- Remove `cmd: any` cast in fetchCommands() since SDK types flow through
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve slash command issues in Claude and Copilot adapters
Claude Code fixes:
- Handle local_command_output system messages so /help, /cost etc.
actually display their output instead of silently dropping it
- Extract user-installed skills from init message's skills[] array
alongside built-in slash_commands[]
- Improve listCommands() to check session state before falling back
to the well-known commands list
Copilot fixes:
- Await fetchSkills() in createSession() instead of fire-and-forget,
ensuring cachedCommands is populated before frontend requests
- Retry fetchSkills in listCommands() when cache is empty
- Add session.skills_loaded event handler for real-time skill updates
- Format handlePendingCommand() call correctly
Frontend (Chat.tsx):
- Pass sessionId to gateway.listCommands() so adapters can look up
the correct engine session for command discovery
- Re-fetch commands when active session changes (not just engine type)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve three slash command bugs (output, skills, Copilot)
1. Claude /help and other slash commands producing no output:
- Handle string content in handleAssistantMessage (SDK wraps
slash command output as plain string, not content block array)
- Handle text content in handleUserMessage (synthetic user messages
from CLI with XML-stripped slash command output)
- Use result.result text as fallback in handleResultMessage when
buffer is empty (for local-jsx commands like /help)
- Keep local_command_output handler for future-proofing
2. Claude skills not showing: Added debug logging to init message
handler to trace slash_commands and skills arrays from SDK.
3. Copilot slash commands completely non-functional:
- Root cause: listCommands called before engine session is active
(lazy creation). activeSessions lookup returns undefined.
- Fix: activate session via ensureActiveSession in listCommands
when no active session found but directory is known.
- Enhanced fetchSkills with comprehensive debug logging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: load slash commands at session creation instead of first message
- Use SDK's supportedCommands() API via sdkQuery for Claude warmup,
replacing init-message parsing (gets full name + description)
- Trigger warmup in createSession() and await it in listCommands()
so commands are available before user sends first message
- Inject cached user skill names into system prompt via append
- Pass directory from conversationStore to Copilot listCommands()
so sessions can resume after app restart
- Create engine session eagerly in engine-manager createSession()
instead of lazily on first sendMessage
- Track only engineType (not sessionId) in Chat.tsx createEffect
to avoid re-fetching commands on every session switch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review feedback
- Downgrade verbose debug logs from info to debug level (Claude SDK messages,
system messages, Copilot session events, fetchSkills)
- Remove JSON.stringify of full message payloads in logs to prevent leaking
user data; log only type/subtype
- Fix OpenCode invokeCommand to return handledAsCommand:false so gateway
falls back to sendMessage correctly
- Guard Cron RPC client methods with clear rejection until backend handlers
are implemented
- Add zero-length guard for modulo in command menu keyboard navigation
to prevent NaN
- Clean up orphaned conversation if engine session creation fails in
EngineManager.createSession
- Reduce local_command_output logging to debug level, remove content preview
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add new features to README and website (scheduled tasks, worktree, file explorer, slash commands)
- Add 'Developer Workflow Tools' section to all 5 README languages
- Scheduled Tasks (interval/daily/weekly automation)
- Git Worktree Parallel Sessions (isolated branches with merge/squash/rebase)
- File Explorer & Git Monitoring (real-time change tracking, inline diff)
- Slash Commands & Engine Skills (unified autocomplete for all engines)
- Add Token usage tracking to 'And More' section
- Website: add 4 new feature cards, remove 'Modern Tech Stack' card
- Website: add i18n translations (en/zh) for new feature cards
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The SDK's `unstable_v2_createSession` / `unstable_v2_resumeSession` silently drops the `cwd` field from options — the internal `rQ` constructor never forwards it to `ProcessTransport`. This causes the spawned CC CLI to inherit the Electron process's own cwd, writing session JSONL files under the wrong project directory. When the codemux installation directory changes (e.g. codemux → codemux-2), previously created sessions become unfindable because the JSONL path is derived from process.cwd() at spawn time. Work around this by temporarily calling process.chdir(directory) before creating/resuming the V2 session and restoring it in a finally block. The rQ → ProcessTransport → spawn chain is fully synchronous, so this is safe in single-threaded Node.js. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: preserve runtime channel startup state Keep runtime gateway options available for first-run channel setup in packaged builds, and avoid init-time config overwrite or duplicate auto-start when a channel starts before initFromConfig completes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: guard missing channel config options Handle persisted or hand-edited channel configs that omit the options object, so runtime config merges in ChannelManager do not throw during startup or manual channel actions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: harden channel adapter restarts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- add shared settings sync between Electron and web clients - subscribe renderer and browser to host-backed settings updates - harden settings validation and optimistic sync behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📦 Preview Build — v1.5.1 → v1.5.2All builds succeeded. View workflow run
📥 DownloadGo to the Actions run artifacts section to download installers. |
…ns lint error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move onCleanup before awaits in Settings.tsx onMount to prevent listener leak - Guard pending shared setting overrides behind isSettingsSyncEnabled() in getSetting - Clear pendingSharedSettingOverrides when sync is disabled - Reject prototype-pollution keys (__proto__, constructor, prototype) in engineModels validation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add host-backed settings sync so the web client and Electron app can share key preferences consistently, and harden the sync path for cache refreshes, optimistic updates, and validation.
Changes
Related Issues
Test Plan
bun run test:unit)bun run test:e2e)npm run typecheck)Manual notes:
Screenshots