P1: tool_result dedup changed from "first wins" to "last wins"
- claude-client.ts: remove emittedToolResultIds Set, both PostToolUse and
user message handler emit freely. PostToolUse provides immediate UI feedback;
user handler's potentially more complete result arrives second.
- route.ts: collectStreamResponse replaces existing contentBlock entry when
duplicate tool_use_id arrives (last wins for DB persistence).
- ChatView.tsx: onToolResult replaces existing state entry by tool_use_id
instead of skipping, ensuring most complete result is displayed.
P2: SDK task ID collision risk eliminated
- db.ts: syncSdkTasks now uses full sessionId in task ID
(`sdk-{fullSessionId}-{index}`) instead of truncated 8-char prefix,
eliminating cross-session primary key collisions.
P2: Todo ordering stabilized
- db.ts: add sort_order column migration, syncSdkTasks writes array index
as sort_order. getTasksBySession sorts by sort_order ASC, created_at ASC
so tasks with identical timestamps maintain insertion order.
P3: Documentation field mapping corrected
- agent-tooling-todo-bridge.md: fix field mapping table — actual implementation
maps activeForm (not priority) to description column. Updated dedup strategy
docs from "three-layer skip" to "two-layer last-wins replace".
Lint: fix prefer-const for finalPrompt in claude-client.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Session runtime mutex:
- Add session_runtime_locks table with TTL-based locking via SQLite
single-writer guarantee (transaction: DELETE expired + INSERT, PK
conflict = already locked → 409 SESSION_BUSY)
- POST /api/chat acquires lock before processing, releases in
collectStreamResponse finally block and error catch
- Add runtime_status/runtime_updated_at/runtime_error columns to
chat_sessions for tracking session state
Permission request persistence:
- Add permission_requests table with full lifecycle tracking
(pending → allow/deny/timeout/aborted)
- Dual-write in permission-registry: in-memory Map for real-time
resolution + DB for audit and recovery
- claude-client canUseTool now persists to DB before SSE event
- permission/route.ts validates against DB before resolving:
404 (not found), 409 (already resolved), 410 (waiter gone)
Immutable sdk_cwd:
- Add sdk_cwd column, snapshot from working_directory at session
creation time, never changes afterward
- route.ts uses sdk_cwd for SDK cwd to prevent resume breakage
when user modifies working_directory
- Backfill existing sessions via migration
Startup recovery (migrateDb):
- Reset running/waiting_permission sessions to idle with error msg
- Clear all runtime locks (stale from previous process)
- Abort pending permission requests (no in-memory waiter exists)
All DB writes are try/catch guarded — failure degrades gracefully
without breaking the in-memory hot path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: assistant messages use temporary IDs (temp-assistant-{timestamp})
in the frontend, which don't match real DB message IDs. The PUT update by
message_id always found 0 rows, so results were never persisted.
Fix — fallback search by session + prompt:
- db.ts: updateMessageContent now returns changes count; new
updateMessageBySessionAndHint() finds the latest assistant message
containing image-gen-request with matching prompt text and updates it
- /api/chat/messages PUT: tries message_id first, falls back to
session_id + prompt_hint search; returns 404 if neither finds a match;
response includes updated_message_id and fallback_used for debugging
- ImageGenConfirmation: always sends session_id and prompt_hint in PUT
request, regardless of whether messageId is a real or temp ID
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: after image generation, the message content in DB stayed as
`image-gen-request` (the options form). On app restart, MessageItem re-parsed
this as a generation form instead of showing the completed result. The result
only lived in React state and localStorage.
Fix:
- db.ts: add updateMessageContent() to update a message's content by ID
- /api/chat/messages: add PUT handler for message content updates
- ImageGenConfirmation: accept messageId prop; on successful generation,
replace message content from image-gen-request to image-gen-result format
(with localPath references) via PUT API call
- MessageItem: pass message.id to ImageGenConfirmation
Now on restart, parseImageGenResult() matches the persisted image-gen-result
block and renders ImageGenCard directly with the saved image paths.
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>
Gallery UI overhaul:
- GalleryGrid: replace equal-size grid with CSS columns masonry layout (6 columns),
pure image cards without text/date info, hover gray ring highlight,
favorited items show red heart badge
- GalleryDetail: left-right layout dialog (70% image / 30% info),
nearly full-screen size with minimal margins, black background for image area,
absolute-positioned image container for correct sizing across all aspect ratios,
favorite toggle button replaces tag manager, removed outer border
- Gallery page: added "favorites only" filter button, replaced load-more button
with IntersectionObserver infinite scroll, removed tag system references
Favorites system:
- db.ts: added `favorited` column to media_generations with migration
- New API: PUT /api/media/[id]/favorite for toggle
- gallery/route.ts: support favoritesOnly=1 filter, return favorited field
- i18n: added favorites-related translations (en/zh)
Other:
- Default image model changed from Nano Banana Pro to Nano Banana 2
- providers/models/route.ts: skip media provider types in chat model selector
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Image Generation Refactor:
- MessageInput: add onImageGenerate callback, remove skill fetch dependency
- ChatView: implement handleImageGenerate that creates image-gen-request
message for ImageGenConfirmation UI, then user selects params and generates
- New API route POST /api/chat/messages for DB-only message persistence
- image-gen-completed event now only persists notice to DB, no longer triggers model
- Reference images passed via module-level Map (imageGenRefStore) to avoid
DB bloat from base64 data; user message DB content stores metadata only
Bug Fixes:
- Fix useCallback dependency missing imageGen causing stale closure reads
- Fix React Hooks order violation in MessageItem (early return before hooks)
- Fix localStorage cache collision across sessions (key now includes sessionId)
- Fix empty src attribute in FileAttachmentDisplay for DB-restored messages
- Add JSON parse fallback for malformed image-gen-request blocks
Gallery Reference Images:
- /api/media/generate saves reference images to disk, stores paths in metadata
- /api/media/gallery extracts referenceImages from metadata in mapRow
- GalleryDetail displays reference image thumbnails
- ImageGenCard supports referenceImages prop for chat display
UI Changes:
- ImageGenToggle: replace paint brush icon with text label (en: Image, zh: 生图)
- Model selector button: remove provider name display to save space
- ImageGenConfirmation: support referenceImagesData prop for base64 preview
i18n:
- Add imageGen.toggleLabel key (en: Image, zh: 生图)
Foundation for the image generation feature:
- Install @google/genai SDK (Google Gemini API client for Nano Banana Pro)
- Add media_generations table to store image generation metadata
(id, type, status, provider, model, prompt, aspect_ratio, image_size,
local_path, tags, session_id, message_id, etc.)
- Add media_tags table for tag-based filtering in gallery
- Add DB migration for existing databases
- Add indexes for created_at, session_id, status on media_generations
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>
ErrorBoundary (PR #49 follow-up):
- Replace inline SVG with lucide-react CircleAlert icon for consistency
- Add i18n support: extract fallback UI into functional ErrorFallback
component so it can use useTranslation hook
- Remove unused withErrorBoundary HOC
- Add error.* translation keys to en.ts and zh.ts
ProviderManager:
- Add Claude Code default config entry in connected providers section
with cc switch hint explaining configurations may not be readable
- Update GLM preset descriptions: "GLM" → "GLM Code Plan / 编程套餐"
- Update MiniMax preset descriptions: "MiniMax API" → "MiniMax Code Plan / 编程套餐"
- Add provider.ccSwitchHint translation key to en.ts and zh.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Completely rewrites the Provider management page in Settings to follow
the OpenCode reference design with two clear sections.
ProviderManager.tsx — full rewrite:
- Top section "Connected Providers": shows all configured providers as a
clean list with brand icons from @lobehub/icons, name, badge, edit and
disconnect buttons. Environment variables also shown here.
- Bottom section "Add Provider": preset list with icon + name + description
+ Connect button. Clicking Connect opens a dialog with only the fields
needed for that preset.
- First 3 presets: Custom API (all fields), Anthropic Third-party API
(name + URL + key), Anthropic Official (key only).
- Other presets (GLM, Kimi, Moonshot, MiniMax, etc.) simplified to key-only.
- Bedrock/Vertex show environment variable config.
- Removed the top-right "Add Provider" button entirely.
- Added PresetConnectDialog component: shows brand icon in title, only
relevant fields, advanced options fold for extra_env, Cancel/Connect footer.
package.json:
- Add @lobehub/icons dependency for AI brand icons
- Bump version to 0.15.0
i18n (en.ts, zh.ts):
- Add translations: connectedProviders, noConnected, connect, disconnect,
disconnectProvider, disconnectConfirm, addProviderSection, addProviderDesc,
disconnecting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactors the provider/model selection system so users can switch providers
directly from the chat input model selector, instead of having to go to
Settings to activate/deactivate providers.
types/index.ts:
- Add ProviderModelGroup interface for grouped provider+model data
- Extend SendMessageRequest, ClaudeStreamOptions, ChatSession with provider_id
db.ts:
- Migration: add provider_id column to chat_sessions
- Migration: convert is_active=1 provider to default_provider_id setting
- New helpers: updateSessionProviderId, getDefaultProviderId, setDefaultProviderId
providers/models/route.ts:
- Rewrite to return { groups, default_provider_id } instead of flat model list
- Each configured provider becomes a group; env vars become "Environment" group
- Deduplicate models within a group (e.g. GLM sonnet/opus both → "GLM-4.7" once)
chat/route.ts:
- Accept provider_id from request body
- Resolution chain: request → session → default_provider_id → env vars
- provider_id='env' is sentinel for environment variable fallback
- Pass resolved provider object to streamClaude()
claude-client.ts:
- Use options.provider ?? getActiveProvider() so explicit provider takes priority
ChatView.tsx:
- Track currentProviderId state, pass provider_id in fetch body
- New handleProviderModelChange callback
MessageInput.tsx:
- New props: providerId, onProviderModelChange
- Replace dynamicModels/activeProviderName with providerGroups/defaultProviderId
- Model selector UI: grouped by provider with section headers, ✓ for active item
- Persist both model and provider ID to localStorage
chat/page.tsx, chat/[id]/page.tsx:
- Track and pass provider_id through the component chain
ProviderManager.tsx:
- Remove handleActivate/handleDeactivate, activatingId, Apply/Disable buttons
- Remove Active badge and green border styling
- Simplify env variable banner
- Add provider-changed event dispatch to save/delete handlers
providers/route.ts:
- GET response now includes default_provider_id field
i18n (en.ts, zh.ts):
- Add provider.default, provider.setDefault, provider.environment translations
Backward compatible: old sessions without provider_id default to '' which
triggers the fallback chain. is_active column preserved but no longer written.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add useEffect cleanup to abort in-flight fetch when ChatView unmounts
(e.g., session switch via key={id} remount). This triggers the existing
server-side cleanup chain: saves partial message to DB, auto-denies
pending permissions. Also resets streamingSessionId and
pendingApprovalSessionId global state to prevent stale UI.
- Add beforeunload handler during streaming to show browser confirmation
dialog when user attempts to close the window/tab, preventing accidental
data loss. Listener is automatically removed when streaming ends.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New API route: src/app/api/skills/search/route.ts
- POST endpoint receives query + skill list + model alias
- resolveApiConfig() resolves credentials: active provider → settings → env
- Supports Anthropic direct, OpenRouter (Bearer auth), custom base URLs
- Bedrock/Vertex silently return empty results (unsupported)
- 5s hard timeout, all errors return { suggestions: [] }
- LLM returns JSON array of matching skill names, validated against input
- Modified: src/components/chat/MessageInput.tsx
- Extend substring matching to search both label and description fields
- Add debounced AI search (500ms) when skill mode has < 2 non-built-in
substring matches and filter length >= 2 characters
- AbortController cancellation on new input or popover close
- "AI Suggested" section with BrainIcon and spinner in popover UI
- Unified keyboard navigation across filteredItems + aiSuggestions
using allDisplayedItems for ArrowUp/Down, Enter/Tab, and visibility
- Modified: src/i18n/en.ts, src/i18n/zh.ts
- Added 'messageInput.aiSuggested' translation key (EN/ZH)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `electron:dev` script requires `dist-electron/main.js` to exist,
but the README does not mention running `node scripts/build-electron.mjs`
beforehand. Without this step, `electron .` fails with:
"Cannot find module '/path/to/dist-electron/main.js'"
Add the build command to both the Quick Start and Development sections
in all three README files (EN, CN, JA).
New "Usage" tab in Settings showing token consumption analytics:
- **Data cards**: Total Tokens, Total Cost, Sessions, Cache Hit Rate
- **Bar chart**: Daily token usage with model/provider color grouping
using recharts v3, with custom tooltip and continuous date x-axis
- **Provider tracking**: Each session now records the active provider
name (e.g. "Kimi Coding Plan") so the chart distinguishes usage
across different providers, not just model names
Backend changes (db.ts):
- Added `provider_name` column to chat_sessions with auto-migration
- Added `getTokenUsageStats(days)` with summary + daily aggregation
- SQL uses `json_valid()` guard to prevent malformed JSON crashes
- Fixed off-by-one: 7D now shows exactly 7 calendar days, not 8
- Provider-qualified grouping: shows provider name when set,
plain model name when using direct Anthropic
API changes (route.ts):
- Persists effectiveModel and activeProvider name on every message
- Captures model from SDK init event for accurate tracking
- New GET /api/usage/stats endpoint
Frontend fixes:
- SettingsLayout: replaced useEffect+setState with useSyncExternalStore
to fix react-hooks/set-state-in-effect lint error
- UsageStatsSection: AbortController prevents request race on rapid
range switching; cache hit rate formula corrected to
cache_read/(cache_read+input); date gaps filled with zeros for
continuous x-axis
- Full i18n support (en + zh)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Resume pre-check and degradation (claude-client.ts):
- Check working_directory exists on disk before attempting resume;
if missing, skip resume, clear sdk_session_id, notify frontend
- On resume peek failure, clear sdk_session_id to prevent repeated
retries on the same broken session
- Change iterator wrap condition from sdkSessionId to shouldResume
so skipped resumes don't enter the peek path
2. Per-request independent permission timeout (permission-registry.ts):
- Replace lazy cleanup (cleanupExpired on next register) with a
dedicated setTimeout per permission request
- Auto-deny after 5 minutes with clearTimeout on resolve/abort
- Extract denyAndRemove helper for abort and timeout paths
3. Permission wait visibility (StreamingMessage.tsx):
- Always show StreamingStatusBar during streaming (remove
!pendingPermission guard)
- Display "Waiting for authorization: <tool>" with elapsed timer
when a permission request is pending, instead of hiding the bar
4. Stream idle timeout (ChatView.tsx):
- Add 330s idle timeout (slightly above 5-min permission timeout
to avoid races) using setInterval that checks lastEventTime
- Every SSE callback calls markActive() to reset the timer
- On idle timeout, abort the stream and show a readable error
- Clean up interval in both catch and finally blocks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The "exited with code 1" error was hiding the real cause. Now:
- Logs full error object to console (message, stack, cause, stderr)
- Extracts stderr/cause from SDK error and includes in user-facing message
- Adds "Details:" section to the error when extra info is available
This will help diagnose why old sessions fail on some machines while
new sessions work fine with the same provider config.
- src/lib/claude-client.ts: enhanced catch block with console.error
logging and stderr/cause extraction for error messages
When a resumed SDK session crashes mid-stream (after outputting some
content), the sdk_session_id remains in the database. Every subsequent
message in that chat session attempts to resume the same broken SDK
session, causing repeated failures.
Root cause: the SDK process starts successfully with resume, outputs
some content, then crashes (exit code 1). The sdk_session_id was
already stored from a previous successful turn and never gets cleared
on error.
Fix: in the catch block of streamClaude(), when we were resuming a
session (sdkSessionId is set), clear the sdk_session_id in the
database. This ensures the next message starts a fresh SDK session
instead of repeatedly hitting the same broken resume.
Combined with the earlier peek-based fallback (which handles failures
at startup), this covers both failure modes:
1. Resume fails immediately → fallback retries without resume in the
same request
2. Resume fails mid-stream → clears sdk_session_id so the NEXT
request starts fresh
- src/lib/claude-client.ts: import updateSdkSessionId, add
sdk_session_id reset in catch block when sdkSessionId was set
When continuing an old chat session, CodePilot passes the stored
sdk_session_id to the SDK with resume=true. If the local SDK session
file is stale, corrupt, or incompatible (e.g. after a CLI version
update), the SDK process exits with code 1 and the user sees a
misleading "Invalid or missing API Key" error.
This fix wraps the initial query() call with a try-catch that detects
resume failures by peeking at the first async message. On failure, it
automatically retries without the resume option, starting a fresh SDK
session while preserving the chat history in CodePilot.
- src/lib/claude-client.ts: added resume-failure detection and
automatic fallback logic around the query() call
Title bar alignment (AppShell.tsx, page.tsx, DocPreview.tsx):
- Reduce AppShell draggable spacer from h-11 to h-5 to match side panels' mt-5
- Chat title bar uses normal flow h-12 instead of absolute positioning hack
- DocPreview header changed from h-10 to h-12 mt-5
- All three columns now align at the same vertical position
macOS Dock icon (build/icon.png, build/icon.icns):
- Add 100px transparent padding on all sides (content 824x824 in 1024x1024 canvas)
- Follows Apple Human Interface Guidelines for macOS app icons
- Regenerated icon.icns with all required sizes
- Original icon backed up as icon-original.png
Remove start execution button (ChatView.tsx):
- Remove showStartExecution logic and handleStartExecution callback
- Mode switching is now handled automatically by SDK
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)
Wrap main content area and chat list panel with ErrorBoundary
components to gracefully handle runtime errors instead of
showing a blank white screen.
Closes#32
Add 'Commit 信息规范' section to Development Rules requiring:
- Conventional commits format in title
- Detailed per-file/per-feature change descriptions in body
- Root cause explanation for bug fixes
- Architectural decision rationale when applicable
Also remove v0.11.0 changelog that was mistakenly added to CLAUDE.md
(changelogs belong in commit messages and GitHub Releases, not here).
Core fixes:
- Add settingSources to SDK options so CodePilot loads user/project settings
like the CLI (fixes WebFetch timeouts, tool permission issues)
- Delete CLAUDECODE env var to prevent nested session detection when
CodePilot runs alongside Claude Code CLI (fixes 'exited with code 1')
- Remove manual MCP config reading (SDK handles it via settingSources)
- Remove hardcoded timeout env vars (SDK loads from user settings)
UX improvements:
- Remember last selected model across sessions (localStorage)
- Same-project new chat skips directory picker
- File tree auto-refreshes on project switch and after each tool completes
- Left sidebar projects default collapsed (only most recent expanded)
- Right-side file tree defaults collapsed
- Fix file tree icon alignment (spacer size-4 → size-5)
- Dynamic model list from /api/providers/models endpoint
- Provider change dispatches event to refresh model selector
- Improved error messages with provider name and troubleshooting hints
- Fix context button file type matching for extension patterns
- Default tool timeout 120s → 300s
FilePreview and DocPreview components were not passing baseDir to
the preview API, causing 403 "File is outside the allowed scope"
on Windows when the project is on a different drive than home.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- Add overflow-hidden to dialog to constrain child components
- Change path display from truncate to horizontal scroll so long
Windows paths (e.g., D:\projects\very\deep\path) remain readable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FileTree component was not passing baseDir to the file API, causing
it to fall back to restricting access to the home directory. On
Windows, projects on a different drive (e.g., D:\) were blocked
with 403.
Changes:
- FileTree now passes workingDirectory as baseDir parameter
- File API routes no longer require baseDir to be under homeDir
(allows cross-drive access on Windows)
- Still prevents using filesystem root as baseDir for safety
- Add isRootPath() utility for root path detection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The chat route and claude-client both fell back to process.cwd() when
working_directory was empty, causing Claude to operate in the server's
directory instead of the user's project. Since session creation now
requires a valid working_directory, these fallbacks are unnecessary
and misleading. Fall back to os.homedir() as a safer last resort.
Also update CLAUDE.md: require explicit user confirmation before
publishing releases (no auto push+tag).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.10.7 skipped .cmd wrappers entirely, but the SDK's internal
fallback can't find the CLI either. Now we read the .cmd file,
extract the actual .js path (e.g. node_modules/@anthropic-ai/
claude-code/cli.js), resolve %~dp0 to the wrapper's directory,
and pass the resolved script path to the SDK.
Regex patterns are scoped to paths containing "claude" to avoid
matching unrelated .js files in the wrapper.
Bump version to 0.10.8.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two root causes of spawn EINVAL on Windows:
1. process.env spread can include undefined values. Windows
child_process.spawn requires all env values to be strings.
sanitizeEnv now builds a clean object, dropping non-string entries.
2. Claude CLI installed via npm creates .cmd wrappers that cannot be
spawned without shell:true. Detect .cmd/.bat extensions and fall
back to SDK's internal resolution instead.
Bump version to 0.10.7.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip null bytes and control characters from environment variable values
before passing them to child_process.spawn via the SDK. Third-party
Anthropic API providers could introduce invalid characters through
api_key, base_url, or extra_env, causing spawn EINVAL errors.
Bump version to 0.10.6.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>