Compare commits

...

16 Commits

Author SHA1 Message Date
Kacper
3ccea7a67b fix(docker): Address remaining PR #745 review comments
- Move Playwright install after node_modules copy to use pinned version
- Use local playwright binary instead of npx to avoid registry fetch
- Add --user automaker -w /app flags to docker exec commands
- Change bold text to proper heading in README (MD036 lint fix)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 16:07:53 +01:00
Kacper
b37a287c9c fix(docker): Address PR #745 review feedback
- Clean up npx cache after Playwright installation to reduce image size
- Clarify README: volume mounts persist cache across container lifecycles,
  not image rebuilds
- Add first-use warning: empty volume overrides pre-installed browsers,
  users must re-install with docker exec command

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 15:55:11 +01:00
Kacper
45f6f17eb0 fix(docker): Pre-install Playwright Chromium browsers for automated testing
Fixes #725

AI agents in automated testing mode require Playwright to verify implementations,
but Docker containers had only system dependencies installed, not browser binaries.
This caused verification failures with permissions errors.

Changes:
- Install Playwright Chromium in Dockerfile (~300MB increase)
- Update docker-compose.override.yml.example with clearer Playwright documentation
- Add "Playwright for Automated Testing" section to README
- Document optional volume mount for persisting browsers across rebuilds

Browsers are now pre-installed and work out of the box for Docker users.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 15:47:18 +01:00
Shirone
29b3eef500 Merge pull request #744 from AutoMaker-Org/fix/git-project-initial-branch
fix(server): Use 'main' as default branch for new git projects
2026-02-02 14:20:03 +00:00
Kacper
010e516b0e fix(server): Use 'main' as default branch for new git projects
Git initialization now explicitly specifies --initial-branch=main to match
GitHub's default branch standard (since October 2020). This prevents the
branch name mismatch that caused features to disappear from the UI when
pushing to GitHub.

Fixes #734

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 15:07:43 +01:00
Shirone
00e4712ae7 Merge pull request #743 from AutoMaker-Org/fix/broken-syslinks-on-server
fix(electron): Fix broken symlinks in server bundle preventing app startup
2026-02-02 13:50:39 +00:00
Kacper
4b4ae04fbe refactor: Address PR review feedback for symlink and directory handling
- Use lstatSync with try/catch for robust broken symlink detection
- Remove redundant existsSync check before mkdirSync with recursive: true

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 14:36:24 +01:00
Kacper
04775af561 fix(electron): Fix broken symlinks in server bundle preventing app startup
Fixes #742

This commit resolves two critical issues that prevented the Electron app from starting:

1. **Broken symlinks in server bundle**
   - After npm install, local @automaker/* packages were symlinked in node_modules
   - These symlinks broke after electron-builder packaging since relative paths no longer existed
   - Solution: Added Step 6b in prepare-server.mjs to replace symlinks with real directory copies
   - Added lstatSync and resolve imports to support symlink detection and replacement

2. **electronUserDataWriteFileSync fails on first launch**
   - The userData directory doesn't exist on first app launch
   - Writing .api-key file would fail with ENOENT error
   - Solution: Added directory existence check and creation with { recursive: true } before writing

Files modified:
- apps/ui/scripts/prepare-server.mjs: Added symlink replacement logic after npm install
- libs/platform/src/system-paths.ts: Added parent directory creation in electronUserDataWriteFileSync

Verification: After these fixes, npm run build:electron produces a working app that starts without ERR_MODULE_NOT_FOUND errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 14:26:59 +01:00
Shirone
b8fa7fc579 Merge pull request #732 from AutoMaker-Org/fix/icon-posiition-on-mac
fix(ui): adjust padding for logo for mac
2026-01-31 12:01:49 +00:00
Shirone
7fb0d0f2ca refactor(ui): Integrate macOS Electron padding logic into ProjectSwitcher
Updated the ProjectSwitcher component to conditionally apply top padding based on the operating system and Electron environment. This change utilizes the newly created MACOS_ELECTRON_TOP_PADDING_CLASS for improved maintainability and consistency across the UI.
2026-01-31 12:54:36 +01:00
Kacper
f15725f28a refactor(ui): Extract macOS Electron padding into shared constant
Extract the hardcoded 'pt-[38px]' magic number into a shared constant
MACOS_ELECTRON_TOP_PADDING_CLASS for better maintainability. This
addresses the PR #732 review feedback from Gemini Code Assist.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 20:43:28 +01:00
Kacper
7d7d152d4e fix(ui): Adjust sidebar padding for macOS Electron compatibility
Updated the sidebar header and navigation components to increase top padding for macOS Electron users from 10px to 38px, ensuring better layout and avoiding overlap with the traffic light controls. This change enhances the user experience on macOS platforms.
2026-01-30 20:36:33 +01:00
DhanushSantosh
1a460c301a fix(test): Set HOSTNAME in dev server tests for consistent behavior
Dev server test was failing on non-localhost hostnames (e.g., 'fedora')
because it expected 'localhost' in the URL. Now sets HOSTNAME env var
in test setup and restores it in teardown for consistent test behavior
across all environments.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 19:55:23 +05:30
DhanushSantosh
c1f480fe49 fix(ui): Make GitHub Copilot icon theme-aware for light mode visibility
The Copilot icon had a hardcoded white fill that made it invisible on
light theme backgrounds. Changed to use currentColor so it adapts to
theme and respects CSS text color classes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 19:55:08 +05:30
Shirone
ef3f8de33b Merge pull request #715 from OG-Ken/fix/opencode-dynamic-models-404-endpoint
fix: Correct OpenCode dynamic models API endpoint URL
2026-01-27 12:02:33 +00:00
Ken Lopez
d379bf412a fix: Correct OpenCode dynamic models API endpoint URL
The fetchOpencodeModels function was calling '/api/opencode/models' which
returns 404. Changed to '/api/setup/opencode/models' which correctly
returns the dynamic models.

This fixes an issue where enabled OpenCode dynamic models (e.g., local
Ollama models) were not appearing in the Model Defaults dropdown selectors
despite being visible and enabled in the OpenCode Settings page.
2026-01-27 03:06:28 -05:00
15 changed files with 138 additions and 26 deletions

View File

@@ -118,6 +118,7 @@ RUN curl -fsSL https://opencode.ai/install | bash && \
echo "=== Checking OpenCode CLI installation ===" && \
ls -la /home/automaker/.local/bin/ && \
(which opencode && opencode --version) || echo "opencode installed (may need auth setup)"
USER root
# Add PATH to profile so it's available in all interactive shells (for login shells)
@@ -147,6 +148,15 @@ COPY --from=server-builder /app/apps/server/package*.json ./apps/server/
# Copy node_modules (includes symlinks to libs)
COPY --from=server-builder /app/node_modules ./node_modules
# Install Playwright Chromium browser for AI agent verification tests
# This adds ~300MB to the image but enables automated testing mode out of the box
# Using the locally installed playwright ensures we use the pinned version from package-lock.json
USER automaker
RUN ./node_modules/.bin/playwright install chromium && \
echo "=== Playwright Chromium installed ===" && \
ls -la /home/automaker/.cache/ms-playwright/ || echo "Playwright browsers installed"
USER root
# Create data and projects directories
RUN mkdir -p /data /projects && chown automaker:automaker /data /projects

View File

@@ -338,6 +338,42 @@ services:
The Docker image supports both AMD64 and ARM64 architectures. The GitHub CLI and Claude CLI are automatically downloaded for the correct architecture during build.
##### Playwright for Automated Testing
The Docker image includes **Playwright Chromium pre-installed** for AI agent verification tests. When agents implement features in automated testing mode, they use Playwright to verify the implementation works correctly.
**No additional setup required** - Playwright verification works out of the box.
#### Optional: Persist browsers for manual updates
By default, Playwright Chromium is pre-installed in the Docker image. If you need to manually update browsers or want to persist browser installations across container restarts (not image rebuilds), you can mount a volume.
**Important:** When you first add this volume mount to an existing setup, the empty volume will override the pre-installed browsers. You must re-install them:
```bash
# After adding the volume mount for the first time
docker exec --user automaker -w /app automaker-server npx playwright install chromium
```
Add this to your `docker-compose.override.yml`:
```yaml
services:
server:
volumes:
- playwright-cache:/home/automaker/.cache/ms-playwright
volumes:
playwright-cache:
name: automaker-playwright-cache
```
**Updating browsers manually:**
```bash
docker exec --user automaker -w /app automaker-server npx playwright install chromium
```
### Testing
#### End-to-End Tests (Playwright)

View File

@@ -43,10 +43,14 @@ export function createInitGitHandler() {
// .git doesn't exist, continue with initialization
}
// Initialize git and create an initial empty commit
await execAsync(`git init && git commit --allow-empty -m "Initial commit"`, {
cwd: projectPath,
});
// Initialize git with 'main' as the default branch (matching GitHub's standard since 2020)
// and create an initial empty commit
await execAsync(
`git init --initial-branch=main && git commit --allow-empty -m "Initial commit"`,
{
cwd: projectPath,
}
);
res.json({
success: true,

View File

@@ -20,8 +20,8 @@ export interface TestRepo {
export async function createTestGitRepo(): Promise<TestRepo> {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'automaker-test-'));
// Initialize git repo
await execAsync('git init', { cwd: tmpDir });
// Initialize git repo with 'main' as the default branch (matching GitHub's standard)
await execAsync('git init --initial-branch=main', { cwd: tmpDir });
// Use environment variables instead of git config to avoid affecting user's git config
// These env vars override git config without modifying it
@@ -38,9 +38,6 @@ export async function createTestGitRepo(): Promise<TestRepo> {
await execAsync('git add .', { cwd: tmpDir, env: gitEnv });
await execAsync('git commit -m "Initial commit"', { cwd: tmpDir, env: gitEnv });
// Create main branch explicitly
await execAsync('git branch -M main', { cwd: tmpDir });
return {
path: tmpDir,
cleanup: async () => {

View File

@@ -14,7 +14,8 @@ describe('worktree create route - repositories without commits', () => {
async function initRepoWithoutCommit() {
repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'automaker-no-commit-'));
await execAsync('git init', { cwd: repoPath });
// Initialize with 'main' as the default branch (matching GitHub's standard)
await execAsync('git init --initial-branch=main', { cwd: repoPath });
// Don't set git config - use environment variables in commit operations instead
// to avoid affecting user's git config
// Intentionally skip creating an initial commit

View File

@@ -30,11 +30,16 @@ import net from 'net';
describe('dev-server-service.ts', () => {
let testDir: string;
let originalHostname: string | undefined;
beforeEach(async () => {
vi.clearAllMocks();
vi.resetModules();
// Store and set HOSTNAME for consistent test behavior
originalHostname = process.env.HOSTNAME;
process.env.HOSTNAME = 'localhost';
testDir = path.join(os.tmpdir(), `dev-server-test-${Date.now()}`);
await fs.mkdir(testDir, { recursive: true });
@@ -56,6 +61,13 @@ describe('dev-server-service.ts', () => {
});
afterEach(async () => {
// Restore original HOSTNAME
if (originalHostname === undefined) {
delete process.env.HOSTNAME;
} else {
process.env.HOSTNAME = originalHostname;
}
try {
await fs.rm(testDir, { recursive: true, force: true });
} catch {

View File

@@ -7,8 +7,8 @@
*/
import { execSync } from 'child_process';
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'fs';
import { join, dirname } from 'path';
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, lstatSync } from 'fs';
import { join, dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
@@ -112,6 +112,29 @@ execSync('npm install --omit=dev', {
},
});
// Step 6b: Replace symlinks for local packages with real copies
// npm install creates symlinks for file: references, but these break when packaged by electron-builder
console.log('🔗 Replacing symlinks with real directory copies...');
const nodeModulesAutomaker = join(BUNDLE_DIR, 'node_modules', '@automaker');
for (const pkgName of LOCAL_PACKAGES) {
const pkgDir = pkgName.replace('@automaker/', '');
const nmPkgPath = join(nodeModulesAutomaker, pkgDir);
try {
// lstatSync does not follow symlinks, allowing us to check for broken ones
if (lstatSync(nmPkgPath).isSymbolicLink()) {
const realPath = resolve(BUNDLE_DIR, 'libs', pkgDir);
rmSync(nmPkgPath);
cpSync(realPath, nmPkgPath, { recursive: true });
console.log(` ✓ Replaced symlink: ${pkgName}`);
}
} catch (error) {
// If the path doesn't exist, lstatSync throws ENOENT. We can safely ignore this.
if (error.code !== 'ENOENT') {
throw error;
}
}
}
// Step 7: Rebuild native modules for current architecture
// This is critical for modules like node-pty that have native bindings
console.log('🔨 Rebuilding native modules for current architecture...');

View File

@@ -1,7 +1,7 @@
import { useState, useCallback, useEffect } from 'react';
import { Plus, Bug, FolderOpen, BookOpen } from 'lucide-react';
import { useNavigate, useLocation } from '@tanstack/react-router';
import { cn } from '@/lib/utils';
import { cn, isMac } from '@/lib/utils';
import { useAppStore } from '@/store/app-store';
import { useOSDetection } from '@/hooks/use-os-detection';
import { ProjectSwitcherItem } from './components/project-switcher-item';
@@ -11,9 +11,12 @@ import { NotificationBell } from './components/notification-bell';
import { NewProjectModal } from '@/components/dialogs/new-project-modal';
import { OnboardingDialog } from '@/components/layout/sidebar/dialogs';
import { useProjectCreation } from '@/components/layout/sidebar/hooks';
import { SIDEBAR_FEATURE_FLAGS } from '@/components/layout/sidebar/constants';
import {
MACOS_ELECTRON_TOP_PADDING_CLASS,
SIDEBAR_FEATURE_FLAGS,
} from '@/components/layout/sidebar/constants';
import type { Project } from '@/lib/electron';
import { getElectronAPI } from '@/lib/electron';
import { getElectronAPI, isElectron } from '@/lib/electron';
import { initializeProject, hasAppSpec, hasAutomakerDir } from '@/lib/project-init';
import { toast } from 'sonner';
import { CreateSpecDialog } from '@/components/views/spec-view/dialogs';
@@ -279,7 +282,12 @@ export function ProjectSwitcher() {
data-testid="project-switcher"
>
{/* Automaker Logo and Version */}
<div className="flex flex-col items-center pt-3 pb-2 px-2">
<div
className={cn(
'flex flex-col items-center pb-2 px-2',
isMac && isElectron() ? MACOS_ELECTRON_TOP_PADDING_CLASS : 'pt-3'
)}
>
<button
onClick={() => navigate({ to: '/dashboard' })}
className="group flex flex-col items-center gap-0.5"

View File

@@ -6,6 +6,7 @@ import type { LucideIcon } from 'lucide-react';
import { cn, isMac } from '@/lib/utils';
import { formatShortcut } from '@/store/app-store';
import { isElectron, type Project } from '@/lib/electron';
import { MACOS_ELECTRON_TOP_PADDING_CLASS } from '../constants';
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
import { useAppStore } from '@/store/app-store';
import {
@@ -89,7 +90,7 @@ export function SidebarHeader({
<div
className={cn(
'shrink-0 flex flex-col items-center relative px-2 pt-3 pb-2',
isMac && isElectron() && 'pt-[10px]'
isMac && isElectron() && MACOS_ELECTRON_TOP_PADDING_CLASS
)}
>
<Tooltip>
@@ -240,7 +241,7 @@ export function SidebarHeader({
<div
className={cn(
'shrink-0 flex flex-col relative px-3 pt-3 pb-2',
isMac && isElectron() && 'pt-[10px]'
isMac && isElectron() && MACOS_ELECTRON_TOP_PADDING_CLASS
)}
>
{/* Header with logo and project dropdown */}

View File

@@ -3,7 +3,9 @@ import type { NavigateOptions } from '@tanstack/react-router';
import { ChevronDown, Wrench, Github, Folder } from 'lucide-react';
import * as LucideIcons from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
import { cn, isMac } from '@/lib/utils';
import { isElectron } from '@/lib/electron';
import { MACOS_ELECTRON_TOP_PADDING_CLASS } from '../constants';
import { formatShortcut, useAppStore } from '@/store/app-store';
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
import type { NavSection } from '../types';
@@ -117,7 +119,12 @@ export function SidebarNavigation({
className={cn(
'flex-1 overflow-y-auto scrollbar-hide px-3 pb-2',
// Add top padding in discord mode since there's no header
sidebarStyle === 'discord' ? 'pt-3' : 'mt-1'
// Extra padding for macOS Electron to avoid traffic light overlap
sidebarStyle === 'discord'
? isMac && isElectron()
? MACOS_ELECTRON_TOP_PADDING_CLASS
: 'pt-3'
: 'mt-1'
)}
>
{/* Project name display for classic/discord mode */}

View File

@@ -1,5 +1,11 @@
import { darkThemes, lightThemes } from '@/config/theme-options';
/**
* Tailwind class for top padding on macOS Electron to avoid overlapping with traffic light window controls.
* This padding is applied conditionally when running on macOS in Electron.
*/
export const MACOS_ELECTRON_TOP_PADDING_CLASS = 'pt-[38px]';
/**
* Shared constants for theme submenu positioning and layout.
* Used across project-context-menu and project-selector-with-options components

View File

@@ -116,9 +116,8 @@ const PROVIDER_ICON_DEFINITIONS: Record<ProviderIconKey, ProviderIconDefinition>
},
copilot: {
viewBox: '0 0 98 96',
// Official GitHub Octocat logo mark
// Official GitHub Octocat logo mark (theme-aware via currentColor)
path: 'M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z',
fill: '#ffffff',
},
};

View File

@@ -2512,7 +2512,7 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
authMethod?: string;
}>;
error?: string;
}>('/api/opencode/models');
}>('/api/setup/opencode/models');
if (data.success && data.models) {
// Filter out Bedrock models

View File

@@ -21,9 +21,13 @@ services:
# - ~/.local/share/opencode:/home/automaker/.local/share/opencode
# - ~/.config/opencode:/home/automaker/.config/opencode
# Playwright browser cache - persists installed browsers across container restarts
# Run 'npx playwright install --with-deps chromium' once, and it will persist
# ===== Playwright Browser Cache (Optional) =====
# Playwright Chromium is PRE-INSTALLED in the Docker image for automated testing.
# Uncomment below to persist browser cache across container rebuilds (saves ~300MB download):
# - playwright-cache:/home/automaker/.cache/ms-playwright
#
# To update Playwright browsers manually:
# docker exec --user automaker -w /app automaker-server npx playwright install chromium
environment:
# Set root directory for all projects and file operations
# Users can only create/open projects within this directory
@@ -37,6 +41,7 @@ services:
# - CURSOR_API_KEY=${CURSOR_API_KEY:-}
volumes:
# Playwright cache volume (persists Chromium installs)
# Playwright cache volume - optional, persists browser updates across container rebuilds
# Uncomment if you mounted the playwright-cache volume above
# playwright-cache:
# name: automaker-playwright-cache

View File

@@ -750,6 +750,9 @@ export function electronUserDataWriteFileSync(
throw new Error('[SystemPaths] Electron userData path not initialized');
}
const fullPath = path.join(electronUserDataPath, relativePath);
// Ensure parent directory exists (may not exist on first launch)
const dir = path.dirname(fullPath);
fsSync.mkdirSync(dir, { recursive: true });
fsSync.writeFileSync(fullPath, data, options);
}