Assistant Workspace V2:
- Markdown indexing engine (incremental manifest + chunks JSONL)
- Keyword retrieval with CJK bigram support and hotset boosting
- Directory taxonomy inference and evolution suggestions
- Workspace config management (.assistant/config.json)
- Organize API: capture, classify, move, archive, suggest-evolution
Workspace switching:
- Inspect API (GET /api/workspace/inspect) for pre-switch validation
- Two-phase switching with confirmation dialogs (5 workspace statuses)
- Atomic save: init/reset runs before setSetting(), rollback on failure
- Auto-navigate to new/reused session after switch
- Mismatch banner in ChatView (only for former assistant sessions)
- Debounced path validation with visual indicators
Security hardening:
- Completion fence (onboarding-complete/checkin-complete) only processes
when session.workingDirectory === assistant_workspace_path (frontend)
AND session.working_directory === workspacePath (backend double-check)
- assertContained() in workspace-organizer: rejects absolute paths, ~/,
../ traversal, and symlink escapes for captureNote/moveFile
- organize API validates all path params at entry (validateRelativePath)
- captureDefault sanitized to relative path on onboarding write
- R_OK + W_OK validation on GET settings, session creation, and PUT
Bug fixes:
- hookTriggeredSessionId now read before auto-trigger; allows retry if
session has no messages (previous trigger failed silently)
- workspace-switched event listener matches oldPath (not just != newPath)
so normal project chats never show the mismatch banner
- Daily check-in card hidden when onboarding not complete
- Refresh docs now calls generateRootDocs() + generateDirectoryDocs()
- Banner "Open" button reuses latest session (checkin mode) instead of
always creating new onboarding session
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add "Assistant Workspace" feature that lets users designate a directory
(e.g. Obsidian vault) containing 4 Markdown files (soul.md, user.md,
memory.md, claude.md) to define AI personality, user profile, long-term
memory, and execution rules. These files are automatically injected into
the system prompt for assistant project sessions.
Core library (src/lib/assistant-workspace.ts):
- Workspace validation, initialization with template files
- File loading with 8000-char truncation, XML prompt assembly (40k limit)
- State management via .assistant/state.json
- Directory docs generation (README.ai.md + PATH.ai.md)
- Case-insensitive file name fallback (soul.md → SOUL.md)
System prompt injection (src/app/api/chat/route.ts):
- Scoped to sessions where working_directory === workspace path
- Workspace prompt prepended before session-level system prompt
- Onboarding/check-in instructions injected as <assistant-project-task>
- autoTrigger flag skips user message save and title update
Chat-based onboarding (10 questions) & daily check-in (3 questions):
- AI conducts conversation, outputs structured completion code blocks
- Frontend detects onboarding-complete/checkin-complete blocks
- Backend calls Claude API to generate file content from answers
- Fallback to raw answers if API call fails
Auto-trigger pipeline:
- Mount trigger (500ms delay) + focus fallback in MessageInput
- assistantTriggerFiredRef prevents duplicate triggers per mount
- hookTriggeredSessionId in state.json prevents cross-reload duplicates
- Daily check-in only triggers in the most recent workspace session
- hookTriggeredSessionId cleared via __clear__ sentinel after completion
API endpoints:
- GET/PUT /api/settings/workspace — path CRUD + file status
- POST /api/workspace/session — create (onboarding) or reuse (check-in)
- POST /api/workspace/onboarding — AI-generate soul.md + user.md
- POST /api/workspace/checkin — AI-update memory.md + user.md
- POST /api/workspace/hook-triggered — set/clear trigger guard
- GET /api/workspace/latest-session — latest session lookup
- POST /api/workspace/docs — refresh directory documentation
UI:
- Settings "Assistant" tab with workspace path, file status, controls
- Violet badge on workspace project group in ChatListPanel
- useAssistantWorkspace hook for state queries
Also includes:
- 102 passing tests (16 new regression tests for workspace logic)
- i18n keys for en.ts and zh.ts
- Handover documentation and execution plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add "View Plan" button with fullscreen modal to ExitPlanMode UI
When Claude writes a plan in plan mode, the plan content is written to a
file on disk but the ExitPlanMode UI only shows Approve/Reject buttons —
the user never sees the actual plan without manually finding the file.
Add a "View Plan" button that opens a fullscreen Dialog modal showing
the plan content rendered as markdown. The plan content is extracted from
the tool uses array (the Write tool's input for the plan file), so no
new API endpoints are needed.
Changes in StreamingMessage.tsx:
- extractPlanContent(): walks toolUses backwards to find the last Write
to a plans/*.md file and returns its content
- ExitPlanModeUI: accepts toolUses prop, renders "View Plan" button
(hidden if no plan content found), opens Radix Dialog modal with
MessageResponse markdown renderer
- Modal is 80vh tall, max-w-4xl, scrollable, with Close button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add "Do this instead" feedback textbox to plan approval UI
When rejecting a plan, users can now type feedback and click "Do this
instead" (or press Enter) to send a custom deny message to the SDK.
This matches the Claude Code CLI behavior where users can provide
alternative instructions when rejecting a plan.
Previously, the deny message was hardcoded to "User denied permission"
— the API already supported custom messages but the client never sent
them.
Changes:
- StreamingMessage.tsx: add feedback textbox + "Do this instead" button
to ExitPlanModeUI, wire onDenyWithMessage callback
- ChatView.tsx: pass denyMessage through handlePermissionResponse
- MessageList.tsx: update onPermissionResponse type signature
- stream-session-manager.ts: add denyMessage param to
respondToPermission(), use it in the deny decision body
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: fetch plan content from disk so View Plan always shows latest version
Previously extractPlanContent read the Write tool's input.content, which
showed stale content when Claude later edited the plan via the Edit tool.
Now extractPlanFilePath finds the plan file path (matching both Write and
Edit tools, with Windows backslash support), and the View Plan button
fetches the file from disk on each click via /api/files/preview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Skills.sh Marketplace:
- Add marketplace browser with search, detail panel, and install/uninstall
- Search API proxying skills.sh with lock file integration for installed state
- Install/remove APIs using `npx skills` CLI with SSE streaming progress
- SKILL.md content fetching via GitHub Git Trees API with in-memory caching
- MarketplaceSkillCard, MarketplaceSkillDetail, MarketplaceBrowser components
- InstallProgressDialog with real-time SSE log output
- SkillsManager gains "My Skills" / "Marketplace" segmented control
- Type definitions: MarketplaceSkill, SkillLockFile, SkillLockEntry
Navigation restructure:
- Split Extensions page into standalone /skills and /mcp top-level pages
- NavRail: replace single Extensions icon with separate Skills (ZapIcon) and MCP (Plug01Icon) entries
- /extensions page now redirects to /skills or /mcp based on ?tab param
- /plugins redirects updated to point to new routes
- Remove redundant source badges from SkillListItem (section headers suffice)
Other:
- Add WeChat user group QR code to README.md and README_CN.md
- i18n: add 19 marketplace-related keys in en.ts and zh.ts
- Bump version to 0.18.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
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: 生图)
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>
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>
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
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