Address PR review comments by:
- Creating shared sanitizeForTestId utility in apps/ui/src/lib/utils.ts
- Updating ProjectSwitcherItem to use the shared utility
- Adding matching helper to test utils for E2E tests
- Updating all E2E tests to use the sanitization helper
This ensures the component and tests use identical sanitization logic,
making tests robust against project names with special characters.
- Replace direct localStorage.setItem() with setItem helper in use-settings-migration.ts (line 472) for consistent storage-availability checks and error handling
- Replace brittle attribute selector with Playwright's getByRole in open-existing-project.spec.ts (line 162) to handle names containing special characters
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The data-testid generation was using only the sanitized project name which
could produce collisions and didn't handle special characters properly.
Changes:
- Combine stable project.id with sanitized name: project-switcher-{id}-{name}
- Expand sanitization to remove non-alphanumeric chars (except hyphens)
- Collapse multiple hyphens and trim leading/trailing hyphens
- Update E2E tests to use ends-with selector for matching
This ensures test IDs are deterministic, unique, and safe for CSS selectors.
- Expanded docstrings in use-settings-migration.ts for parseLocalStorageSettings, localStorageHasMoreData, mergeSettings, and performSettingsMigration
- Expanded docstrings in use-settings-sync.ts for getSettingsFieldValue and hasSettingsFieldChanged helper functions
- Added detailed parameter and return value documentation
- Improved clarity on migration flow and settings merging logic
This brings docstring coverage from 77.78% to 80%+ to satisfy CodeRabbit checks.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit fixes bidirectional data synchronization between Electron and Web
modes by addressing multiple interconnected issues:
**Core Fixes:**
1. **Electron userData Path (main.ts)**
- Explicitly set userData path in development using app.setPath()
- Navigate from __dirname to project root instead of relying on process.cwd()
- Ensures Electron reads from /data instead of ~/.config/Automaker
2. **Server DataDir Path (main.ts, start-automaker.sh)**
- Fixed startServer() to use __dirname for reliable path calculation
- Export DATA_DIR environment variable in start-automaker.sh
- Server now consistently uses shared /data directory
3. **Settings Sync Protection (settings-service.ts)**
- Modified wipe protection to distinguish legitimate removals from accidents
- Allow empty projects array if trashedProjects has items
- Prevent false-positive wipe detection when removing projects
4. **Diagnostics & Logging**
- Enhanced cache loading logging in use-settings-migration.ts
- Detailed migration decision logs for troubleshooting
- Track project counts from both cache and server
**Impact:**
- Projects created in Electron now appear in Web mode after restart
- Projects removed in Web mode stay removed in Electron after restart
- Settings changes sync bidirectionally across mode switches
- No more data loss or project duplication issues
**Testing:**
- Verified Electron uses /home/dhanush/Projects/automaker/data
- Confirmed server startup logs show correct DATA_DIR
- Tested project persistence across mode restarts
- Validated no writes to ~/.config/Automaker in dev mode
Fixes: Data persistence between Electron and Web modes
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
CRITICAL: Electron was using ~/.config/@automaker/app/data/ while web mode
used ./data/, causing projects to never sync between modes.
In development mode, both now use the shared project root ./data directory.
In production, Electron uses its isolated userData directory for app portability.
This ensures:
- Electron projects sync to the same server data directory as web mode
- Projects opened in Electron immediately appear in web mode
- Server restart doesn't lose projects from either mode
The issue was on line 487 where DATA_DIR was set to app.getPath('userData')
instead of the shared project ./data directory.
Fixes the fundamental problem where projects never appeared in web mode
even though they were in the server's settings file.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Updated the PR state handling to use a consistent uppercase format ('OPEN', 'MERGED', 'CLOSED') throughout the codebase. This includes changes to the worktree metadata interface, PR creation logic, and related tests to ensure uniformity and prevent potential mismatches in state representation.
Additionally, modified the GitHub PR fetching logic to retrieve all PR states, allowing for better detection of state changes.
This refactor enhances clarity and consistency in how PR states are managed and displayed.
This update standardizes the loading indicators by replacing all instances of Loader2 with the new Spinner component. The Spinner component provides a consistent look and feel for loading states throughout the UI, enhancing the user experience.
Changes include:
- Updated loading indicators in various components such as popovers, modals, and views.
- Ensured that the Spinner component is used with appropriate sizes for different contexts.
No functional changes were made; this is purely a visual and structural improvement.
CRITICAL FIX: Electron and web mode were using DIFFERENT data directories:
- Electron: Docker volume 'automaker-data' (isolated from host)
- Web: Local ./data directory (host filesystem)
This caused projects opened in Electron to never appear in web mode because
they were synced to a completely separate Docker volume.
Solution: Mount the host's ./data directory into both containers
This ensures Electron and web mode always share the same data directory
and all projects are immediately visible across modes.
Now when you:
1. Open projects in Electron → synced to ./data
2. Switch to web mode → loads from same ./data
3. Restart server → both see the same projects
Fixes issue where projects opened in Electron don't appear in web mode.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
When switching between Electron and web modes or when the server temporarily
stops, web mode was falling back to stale localStorage data instead of fresh
server data.
This fix:
1. Updates localStorage cache whenever fresh server settings are fetched
2. Updates localStorage cache whenever settings are synced to server
3. Prioritizes fresh settings cache over old Zustand persisted storage
This ensures that:
- Web mode always sees the latest projects even after mode switches
- Switching from Electron to web mode immediately shows new projects
- Server restarts don't cause web mode to use stale cached data
Fixes issue where projects opened in Electron didn't appear in web mode
after stopping and restarting the server.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Projects are critical data that must persist across mode switches (Electron/web).
Previously, project changes were debounced by 1 second, which could cause data
loss if:
1. User switched from Electron to web mode quickly
2. App closed before debounce timer fired
3. Network temporarily unavailable during debounce window
This change makes project array changes sync immediately (syncNow) instead of
using the 1-second debounce, ensuring projects are always persisted to the
server right away and visible in both Electron and web modes.
Fixes issue where projects opened in Electron didn't appear in web mode.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
In web mode, image loads may not send session cookies due to proxy/CORS
restrictions. This adds the session token as a query parameter to ensure
images load correctly with proper authentication in web mode.
Fixes custom project icons and images not loading in web mode.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
When a project fails to initialize because the directory no longer exists
(e.g., test artifacts, deleted folders), automatically remove it from the
project list instead of showing the error repeatedly on every reload.
This prevents users from being stuck with broken project references in their
settings after testing or when project directories are moved/deleted.
The user is notified with a toast message explaining the removal.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Web mode sessions were being lost on page reload because the session token was
stored only in memory (cachedSessionToken). When the page reloaded, the token
was cleared and verifySession() would fail, redirecting users to login.
This commit adds localStorage persistence for the session token, ensuring:
1. Token survives page reloads in web mode
2. verifySession() can use the persisted token from localStorage
3. Token is cleared properly on logout
4. Graceful fallback if localStorage is unavailable (SSR, disabled storage)
The HTTP-only cookie alone isn't sufficient for web mode due to SameSite cookie
restrictions and potential proxy issues with credentials forwarding.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Enables ws: true for /api proxy to properly forward WebSocket connections through the development server in web mode. This ensures real-time features work correctly when developing in browser mode.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The web mode launcher was setting CORS_ORIGIN to only include the system
hostname and 127.0.0.1, but users access via http://localhost:3007 which
wasn't in the allowed list.
Now includes:
- http://localhost:3007 (primary dev URL)
- http://$HOSTNAME:3007 (system hostname if needed)
- http://127.0.0.1:3007 (loopback IP)
Also cleaned up debug logging from CORS check since root cause is now clear.
Fixes: Persistent "Not allowed by CORS" errors in web mode
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Added detailed logging to see:
- What origin is being sent
- How the hostname is parsed
- Why origins are being accepted/rejected
This will help us understand why CORS is still failing in web mode.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The CORS check was too strict for local development. Changed to:
- Parse origin URL properly to extract hostname
- Allow all localhost origins (any port)
- Allow all 127.0.0.1 origins (loopback IP)
- Allow all private network IPs (192.168.x.x, 10.x.x.x, 172.x.x.x)
- Keep security by rejecting unknown origins
This fixes CORS errors when accessing from http://localhost:3007
or other local addresses during web mode development.
Fixes: "Not allowed by CORS" errors in web mode
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
In web mode, the API client was hardcoding localhost:3008, which bypassed
the Vite proxy and caused CORS errors. Now it uses relative URLs (just /api)
in web mode, allowing the proxy to handle routing and making requests appear
same-origin.
- Web mode: Use relative URLs for proxy routing (no CORS issues)
- Electron mode: Continue using hardcoded localhost:3008
This allows the Vite proxy configuration to actually work in web mode.
Fixes: Persistent CORS errors in web mode development
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
When running in web mode (npm run dev:web), the frontend on localhost:3007
was making cross-origin requests to the backend on localhost:3008, causing
CORS errors.
Added Vite proxy configuration to forward /api requests from the dev server
to the backend. This makes all API calls appear same-origin to the browser,
eliminating CORS blocks during development.
Now web mode users can access http://localhost:3007 without CORS errors.
Fixes: CORS "Not allowed by CORS" errors in web mode
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
dmg-license is a macOS-only package used for building DMG installers.
Moving it from devDependencies to optionalDependencies allows npm ci
to succeed on Linux and Windows without failing on platform checks.
macOS developers will still get the package when available.
Linux/Windows developers can now run npm ci without errors.
Fixes: npm ci failing on Linux with "EBADPLATFORM" error
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The initial terminalState.fontFamily was set to a raw font string
that didn't match any option in TERMINAL_FONT_OPTIONS, causing the
dropdown to appear empty. Changed to use DEFAULT_FONT_VALUE sentinel.
Address CodeRabbit review feedback:
- Create getEffectiveFont helper to deduplicate getEffectiveFontSans/Mono
- Extract getSettingsFieldValue and hasSettingsFieldChanged helpers
- Create reusable FontSelector component for font selection UI
- Refactor project-theme-section and appearance-section to use FontSelector
- Fix ThemeMode type casting in __root.tsx
- Use specRegeneration.create() instead of non-existent generateAppSpec
- Add missing keyboard shortcut entries for projectSettings and notifications
- Fix lucide-react type casts with intermediate unknown cast
- Remove unused pipelineConfig prop from ListRow component
- Align SettingsProject interface with Project type
- Fix defaultDeleteBranchWithWorktree property name
isOpencodeModel was rejecting valid dynamic model IDs like
'openrouter/qwen/qwen3-14b:free' because it was splitting on all slashes
and expecting exactly 2 parts. This caused valid OpenCode models to be
treated as unknown, falling back to Claude.
Now correctly splits on the FIRST slash only, allowing model names
like 'qwen/qwen3-14b:free' to be recognized as valid.
Fixes: User selects openrouter/qwen/qwen3-14b:free → server falls back to Claude
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The phase model selector was showing ALL discovered dynamic models regardless
of whether they were enabled in settings. Now it filters dynamic models by
enabledDynamicModelIds, matching the behavior of Cursor models and making
the enable/disable setting meaningful.
Users can now:
- Disable models in settings they don't want to use
- See only enabled dynamic models in the model selector dropdown
- Have the "Select all" checkbox properly control which models appear
This ensures consistency: enabling/disabling models in settings affects
which models are available for feature execution.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The "Select all" checkbox for dynamic models was using the unfiltered models list,
causing the checkbox state to not reflect what users see when searching. Now it
correctly operates on the filtered models list so:
- Checkbox state matches the visible filtered models
- "Select all" only toggles models the user can see
- Indeterminate state shows if some filtered models are selected
This ensures the checkbox has a meaningful purpose when filtering/searching models.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fix race condition where sandbox warning appeared on every refresh
even after checking "Do not show again". The issue was that the
sandbox check effect ran before settings were hydrated from the
server, so skipSandboxWarning was always false (the default).
Changes:
- Add settingsLoaded to sandbox check dependencies to ensure the
user's preference is loaded before checking
- Add AUTOMAKER_SKIP_SANDBOX_WARNING env var option to skip the
warning entirely (useful for dev/CI environments)
- Use explicit file patterns to exclude builder config/debug files (builder-*.yml, *.yaml)
- Include blockmap files for efficient delta updates in auto-update scenarios
- Ensure only production-ready artifacts are uploaded to GitHub releases
This prevents accidental inclusion of builder configuration files in the release assets.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
When the dev server restarts, developers need to re-enter the API key
in the browser. While the key is persisted to ./data/.api-key, this
file may be missing in clean dev scenarios.
This adds a helpful tip to the API key banner informing developers
they can set AUTOMAKER_API_KEY environment variable for a persistent
API key during development, avoiding the need to re-enter it after
server restarts.
When opening a git repository without a GitHub remote, the server logs
were spammed with warnings every 5 seconds during worktree polling:
WARN [Worktree] Failed to fetch GitHub PRs: Command failed: gh pr list
... no git remotes found
This happened because fetchGitHubPRs() ran `gh pr list` without first
checking if the project has a GitHub remote configured.
Changes:
- Add per-project cache for GitHub remote status with 5-minute TTL
- Check cache before attempting to fetch PRs, skip silently if no remote
- Add forceRefreshGitHub parameter to clear cache on manual refresh
- Pass forceRefreshGitHub when user clicks the refresh worktrees button
This allows users to add a GitHub remote and immediately detect it by
clicking the refresh button, while preventing log spam during normal
polling for projects without GitHub remotes.
The Agent Output modal and LogViewer component had hardcoded dark zinc
colors that didn't adapt to light mode themes. Replaced all hardcoded
colors with semantic Tailwind classes (bg-popover, text-foreground,
text-muted-foreground, bg-muted, border-border) that automatically
respect the active theme.
npm ci without --force rejects platform-specific packages like dmg-license
which is macOS-only. The --force flag tells npm to proceed even when
platform constraints are violated.
This allows Linux containers to skip dmg-license and continue with the
install, matching the behavior we want for Docker development.
The named Docker volume for node_modules is created with root ownership,
causing EACCES errors when npm tries to write as the automaker user.
Solution:
- Run npm ci as root (installation phase)
- Use --legacy-peer-deps to properly handle optional dependencies
- Fix permissions after install
- Run server process as automaker user for security
This eliminates permission denied errors during npm install in dev containers.
Fixes permission denied errors when installing dependencies in Docker containers:
Changes:
- Remove stale node_modules directories before installing (fresh start)
- Use 'npm ci --force' instead of 'npm install --force' for deterministic installs
- Add chmod to ensure writable permissions on node_modules
- Properly fix directory ownership and permissions before install
This prevents EACCES errors when multiple processes try to write to node_modules
and handles lingering permission issues from previous failed container runs.