- Rewrite install-check-prerequisites with multi-path Claude CLI detection
(native, bun, homebrew, npm) and structured result reporting
- Improve InstallWizard with step-by-step progress and better error handling
- Enhance ConnectionStatus with detailed status display and invalidation
- Add /api/claude-status/invalidate route for cache-busting
- Expand platform.ts with richer detection utilities
- Add onInitMeta handler in stream-session-manager
- Update electron.d.ts types for new IPC channels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ChatView.tsx (P2): snapshot consumption now calls detectAssistantCompletion()
so assistant workspace onboarding/check-in state updates even when the
stream completes while ChatView is unmounted.
ChatView.tsx (P2): replace local temp-message append with a DB re-fetch
on snapshot consumption to avoid duplicate messages (backend already
persists the reply in route.ts).
page.tsx (P3): add updatedInput and denyMessage parameters to
handlePermissionResponse so AskUserQuestion answers and ExitPlanMode
custom deny messages are forwarded to the permission API on the
first-message page.
electron/main.ts: fix Windows startup timeout by forcing IPv4 (family:4)
in health check HTTP request and improving error diagnostics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: preserve paragraph breaks between text blocks in bridge responses
When Claude's response includes tool calls (Read, Edit, Bash, etc.),
the response text is split into multiple content blocks — one before
each tool call and one after. These blocks were joined with an empty
string (.join('')), causing all paragraphs to concatenate without
any spacing in the final Telegram message.
Changed .join('') to .join('\n\n') so paragraph breaks between text
blocks are preserved, matching how they appear in the desktop UI.
* fix: sanitize __NEXT_PRIVATE_* env vars from child processes
The bundled Next.js standalone server sets __NEXT_PRIVATE_STANDALONE_CONFIG
and __NEXT_PRIVATE_ORIGIN in process.env at runtime. These leak into every
child process spawned by the Electron main process (Claude Code CLI,
node version checks, package installs, etc.), which in turn pollutes
tmux/terminal sessions.
When other Next.js projects detect __NEXT_PRIVATE_STANDALONE_CONFIG in their
environment, they skip normal next.config loading and use CodePilot's baked-in
config instead — silently breaking builds and dev servers system-wide.
Fix: strip all __NEXT_PRIVATE_* variables when constructing env objects for
child processes. The standalone server.js already sets these internally, so
they don't need to be passed from the parent.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
- electron/main.ts: check-prerequisites now returns hasHomebrew + platform
so the frontend can distinguish macOS-without-Homebrew from other scenarios
- electron/main.ts: remove untested tarball fallback (hardcoded Node version,
custom ~/.codepilot/node/bin PATH), replace with clean Homebrew-only path
that returns a clear error message when Homebrew is absent
- electron/main.ts: remove ~/.codepilot/node/bin from getExpandedShellPath()
since the tarball install target no longer exists
- InstallWizard.tsx: when Node.js is missing AND Homebrew is absent on macOS,
show a guided UI with the Homebrew install command, copy button, and
step-by-step instructions instead of a broken "Install" button
- InstallWizard.tsx: footer shows "Recheck" button (re-runs checkPrereqs)
instead of "Install" in the no-Homebrew case
- en.ts/zh.ts: add 8 new i18n keys for the Homebrew guidance UI
Root cause: the previous tarball fallback was untested, pinned to a specific
Node version (v22.12.0), and required maintaining a custom PATH entry. Guiding
users to install Homebrew first is more reliable and maintainable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Problem:
- Install wizard on macOS failed with "Homebrew not found" when user
didn't have Homebrew installed, blocking Claude Code installation entirely
Fix (electron/main.ts):
- Add fallback: when Homebrew is not found, download official Node.js v22.12.0
binary tarball from nodejs.org and extract to ~/.codepilot/node/
- No Homebrew or sudo required — curl + tar only
- Detects architecture (arm64/x64) via process.arch
- Add ~/.codepilot/node/bin to getExpandedShellPath() so the downloaded
Node.js is found by subsequent install steps and server launcher
Install flow now: Homebrew found → brew install node (existing)
Homebrew missing → download tarball to ~/.codepilot/node/ (new)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-update flow rework:
- updater.ts: disable autoDownload, add manual `updater:download` IPC handler
so user explicitly triggers download via "Download & Install" button
- updater.ts: resolve system proxy via session.defaultSession.resolveProxy()
and inject into HTTPS_PROXY env var, fixing zero download speed behind VPN/proxy
- preload.ts + electron.d.ts: expose `downloadUpdate` method to renderer
UI improvements:
- UpdateDialog: add "Download & Install" button when update available but not
yet downloading; show progress bar during download; "Restart to Update" when ready
- GeneralSection UpdateCard: show "Download & Install" / "Restart to Update" button
next to "Check for Updates"; show download progress bar inline; add "View Details"
link to reopen update dialog
- AppShell: dismissUpdate no longer permanently hides update for that version
(removed localStorage persistence), just closes the dialog
- useUpdate: add downloadUpdate to context
i18n:
- Added `update.installUpdate` key (en: "Download & Install", zh: "下载并安装")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the manual "jump to GitHub Release" update flow with native
in-app auto-update powered by electron-updater. The app now silently
downloads updates in the background and prompts the user to restart
when ready. Browser mode (`npm run dev`) retains the existing polling
fallback via `/api/app/updates`.
Main process (electron/updater.ts — new):
- Wrap electron-updater's autoUpdater, forward lifecycle events
(checking / available / downloading / downloaded / error) to the
renderer via IPC channel `updater:status`
- Register `updater:check` and `updater:quit-and-install` IPC handlers
- Auto-check 10s after launch, then every 4 hours
- Only initialized in packaged mode (!isDev)
Preload bridge (electron/preload.ts):
- Expose `updater` namespace with checkForUpdates, quitAndInstall,
and onStatus callback (same pattern as install.onProgress)
Type declarations (src/types/electron.d.ts):
- Add UpdateStatusEvent, ElectronUpdaterAPI interfaces
- Add optional `updater` field to ElectronAPI
Update hook (src/hooks/useUpdate.ts):
- Extend UpdateInfo with downloadProgress, readyToInstall, isNativeUpdate
- Add quitAndInstall to UpdateContextValue
Renderer — dual-mode logic (src/components/layout/AppShell.tsx):
- Detect `window.electronAPI?.updater` to choose between native IPC
subscription (Electron) and HTTP polling (browser)
- Map IPC events to UpdateInfo state, including download progress
UI components:
- UpdateDialog: three footer states — View Release (browser), progress
bar + disabled button (downloading), Restart to Update (ready)
- UpdateBanner (new): persistent top banner when update is downloaded,
shown even after dialog is dismissed
- NavRail: settings dot turns green + pulse when readyToInstall
- GeneralSection: UpdateCard shows download %, restart button, or
View Release depending on mode
Build & CI:
- electron-builder.yml: add `publish` config (github provider) to
generate latest-mac.yml / latest.yml metadata + blockmap files
- build.yml: change --publish never to --publish always with GH_TOKEN;
release job now uses `gh release edit` instead of create
i18n:
- Add 6 translation keys (en + zh) for update UI strings
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Streaming tool UI fix (ChatView.tsx):
- Add toolUsesRef/toolResultsRef to reliably track tool data across closures
- Serialize tool_use/tool_result into JSON content-blocks array when building
assistant message after stream ends, matching backend storage format
- MessageItem.parseToolBlocks() now renders tool UI from history messages
- Clean up refs in finally block and timeout auto-retry path
File tree UX (FileTree.tsx, file-tree.tsx):
- Remove folder path header from right panel, merge search + refresh into one row
- Remove file icon spacer (size-5) so file icons align flush with search box
- Change folder icon color from blue to muted-foreground
Chat list panel (ChatListPanel.tsx):
- Folder header: expanded uses FolderOpenIcon, collapsed uses Folder01Icon
- Add tooltip showing full working directory path on folder hover
- Double-click folder header opens in system file manager
Title bar folder name (chat/[id]/page.tsx):
- Add tooltip with full path on project name hover
- Click opens folder in Finder/Explorer via Electron IPC or API fallback
Open folder support:
- electron/main.ts: add shell:open-path IPC handler via shell.openPath
- electron/preload.ts: expose electronAPI.shell.openPath to renderer
- api/files/open/route.ts: fallback API route using open/explorer/xdg-open
Unify icon library — migrate app-level components from Lucide to Hugeicons:
- file-tree.tsx: folder, file, chevron, plus icons
- tool-actions-group.tsx: chevron expand icon
- MessageItem.tsx: copy, check, expand/collapse icons
- MessageInput.tsx: stop button (SquareIcon → StopIcon)
- FileCard.tsx: file icon
- ImageLightbox.tsx: left/right navigation arrows
- InstallWizard.tsx: status icons (check, x, minus, loader, circle, copy, download)
- Keep Lucide in ui/ and ai-elements/ base layers (shadcn standard)
Two fixes for critical Windows issues (#81):
1. NSIS installer: disable allowToChangeInstallationDirectory to prevent
users from installing into shared directories where uninstall would
delete all their files. Also set deleteAppDataOnUninstall: false to
preserve user data.
2. Windows process cleanup: killServer() used SIGTERM which Windows
doesn't support, leaving zombie processes that block updates.
Now uses taskkill /T /F to kill the entire process tree on Windows,
ensuring no residual processes remain after app exit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0: Add "confirm" phase — wizard checks prereqs then shows Install button,
user must explicitly click before any brew/winget/npm command runs.
P1: Closing the wizard dialog while install is running now auto-calls
install:cancel to stop the backend process.
P2: On Windows, use taskkill /T /F /PID to kill the entire process tree
instead of just the shell wrapper, ensuring npm/winget actually stops.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P1: When Node.js is missing, auto-install via brew (macOS) or winget (Windows)
instead of stopping and asking user to install manually.
P1: Auto-open install wizard on first disconnect detection in Electron,
with localStorage flag to avoid re-prompting after dismissal.
P2: IPC install:start now throws on concurrent calls instead of returning
silent { error } object. Removed unused return values from handlers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add an auto-install wizard accessible from the ConnectionStatus dialog
when Claude Code CLI is not detected (Electron only).
- Electron main: install orchestrator with IPC handlers for check/start/cancel
- Preload: expose install API via contextBridge
- InstallWizard: multi-step dialog with real-time log streaming
- ConnectionStatus: add "Install Automatically" button when disconnected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add killServer() that sends SIGTERM via UtilityProcess.kill(), waits
up to 3s, then escalates to SIGKILL via process.kill(pid). Fixes
window-all-closed to await shutdown before quitting, and uses a
persistent isQuitting flag in before-quit to prevent infinite loops.
Closes#51
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace child_process.spawn with Electron's utilityProcess.fork() for
the Next.js server subprocess. utilityProcess runs as a Chromium
service process which doesn't register as a GUI app, eliminating the
extra Dock icon bounce on macOS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Linux as a supported platform with AppImage, deb, and rpm targets
for both x64 and arm64 architectures.
Changes:
- electron-builder.yml: add linux section with AppImage/deb/rpm targets,
icon, category, and desktop entry configuration
- package.json: add electron:pack:linux script
- electron/main.ts: fix getIconPath() to return .png on Linux instead
of .icns (macOS format); enable loadUserShellEnv() on Linux since GUI
apps launched from desktop also have limited env
- README.md + README_CN.md: update supported platforms section
The README previously stated 'Linux builds are planned. Contributions
welcome.' — this delivers on that.
Root cause: electron-builder's @electron/rebuild only rebuilds native
modules found in the `files` config. Since better-sqlite3 enters the
app through extraResources (via .next/standalone/), it was skipped.
The afterPack hook was copying the system Node.js ABI (127) instead
of the required Electron ABI (143).
Fix:
- afterPack now explicitly calls `npx electron-rebuild` targeting the
exact Electron version and architecture from the build context
- Falls back to @electron/rebuild programmatic API if CLI fails
- Fails the build if rebuild cannot complete (no silent failures)
- Adds file size logging for verification
- Excludes `release/` from extraResources to prevent recursive nesting
- Adds startup ABI compatibility check in Electron main process with
friendly error dialog instead of cryptic crash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add src/lib/platform.ts: cross-platform Claude CLI discovery
(candidate paths, .cmd/.bat shell handling, where/which fallback)
- Refactor claude-status route and claude-client to use shared platform utils
- Electron main: Windows process spawning (windowsHide), PATH construction
with AppData/npm dirs, titleBarOverlay, .ico icon support
- electron-builder.yml: add win/nsis build targets
- package.json: add electron:pack:win script
- FolderPicker: drive switcher dropdown for navigating between drives
- SkillsManager: fix duplicate key warning with filePath fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the database path depended on app.getPath('userData') which
varies with app name changes (CodePilot vs codepilot vs Claude GUI).
Now uses a fixed ~/.codepilot/ directory for both dev and production.
Includes automatic migration: on first launch, if the DB doesn't exist
at the new path, it checks old locations (Application Support variants
and the old dev-mode data/ folder) and copies the database over.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Explicitly pass process.env to the Claude Agent SDK's env option,
ensuring user shell env vars (ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL,
etc.) reach the claude subprocess in packaged builds.
2. Spawn the standalone server via /bin/sh with exec to prevent the
Electron binary from appearing as a separate Dock icon on macOS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When launched from Dock/Finder, Electron's process.env is very limited
and misses vars from .zshrc/.bashrc (API keys, custom PATH, etc.).
Added loadUserShellEnv() to run a login shell and capture the full
environment, then pass it to the standalone server process. This fixes
"organization does not have access" errors for users with custom API
configurations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
electron-builder's @electron/rebuild compiles better-sqlite3 for
Electron's Node ABI. The standalone server must use Electron's
built-in Node (ELECTRON_RUN_AS_NODE=1) to match, otherwise the
native module fails with NODE_MODULE_VERSION mismatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Find system Node.js binary instead of using Electron's Node
(avoids native module ABI mismatch with better-sqlite3)
- Detect server process crash and fail fast instead of waiting 30s
- Include server stdout/stderr in error dialog for debugging
- Expand PATH to include homebrew directories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace fork() with spawn() + ELECTRON_RUN_AS_NODE=1 so the
standalone server runs correctly inside the packaged app
- Set cwd to standalone directory for proper dependency resolution
- Show error dialog on startup failure instead of silent quit
- Add Chinese README (README_CN.md)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-featured desktop application built with Next.js + Electron:
- Streaming chat with Claude Code via Agent SDK
- Session management with SQLite persistence
- Project file tree and file preview
- MCP server and custom skills management
- Permission controls with Code/Plan/Ask modes
- Dark/light theme, token tracking
- macOS DMG packaging with electron-builder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>