From 6529446281a09b7008a4f5351625de3490089264 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 12:31:30 +0530 Subject: [PATCH 01/25] feat: add Fedora/RHEL RPM package support with comprehensive documentation Add native RPM package building for Fedora-based distributions: - Extend electron-builder configuration to include RPM target - Add rpm-build installation to GitHub Actions CI/CD workflow - Update artifact upload patterns to include .rpm files - Declare proper RPM dependencies (gtk3, libnotify, nss, etc.) - Use xz compression for optimal package size Documentation: - Update README.md with Fedora/RHEL installation instructions - Create comprehensive docs/install-fedora.md guide covering: - Installation methods (dnf/yum, direct URL) - System requirements and capabilities - Configuration and troubleshooting - SELinux handling and firewall rules - Performance tips and security considerations - Building from source - Support for Fedora 39+, RHEL 9+, Rocky Linux, AlmaLinux End-to-end support enables Fedora users to install Automaker via: sudo dnf install ./Automaker--x86_64.rpm Co-Authored-By: Claude Haiku 4.5 --- .github/workflows/release.yml | 7 +- README.md | 21 +- apps/ui/package.json | 25 +- docs/install-fedora.md | 479 ++++++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+), 3 deletions(-) create mode 100644 docs/install-fedora.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c71a884..d85d1425 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,6 +35,11 @@ jobs: with: check-lockfile: 'true' + - name: Install RPM build tools (Linux) + if: matrix.os == 'ubuntu-latest' + shell: bash + run: sudo apt-get update && sudo apt-get install -y rpm + - name: Build Electron app (macOS) if: matrix.os == 'macos-latest' shell: bash @@ -73,7 +78,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: linux-builds - path: apps/ui/release/*.{AppImage,deb} + path: apps/ui/release/*.{AppImage,deb,rpm} retention-days: 30 upload: diff --git a/README.md b/README.md index 8bfd2a0a..ef8c7bc4 100644 --- a/README.md +++ b/README.md @@ -197,11 +197,30 @@ npm run build:electron # Platform-specific builds npm run build:electron:mac # macOS (DMG + ZIP, x64 + arm64) npm run build:electron:win # Windows (NSIS installer, x64) -npm run build:electron:linux # Linux (AppImage + DEB, x64) +npm run build:electron:linux # Linux (AppImage + DEB + RPM, x64) # Output directory: apps/ui/release/ ``` +**Linux Distribution Packages:** + +- **AppImage**: Universal format, works on any Linux distribution +- **DEB**: Ubuntu, Debian, Linux Mint, Pop!\_OS +- **RPM**: Fedora, RHEL, Rocky Linux, AlmaLinux, openSUSE + +**Installing on Fedora/RHEL:** + +```bash +# Download the RPM package +wget https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm + +# Install with dnf (Fedora) +sudo dnf install ./Automaker--x86_64.rpm + +# Or with yum (RHEL/CentOS) +sudo yum localinstall ./Automaker--x86_64.rpm +``` + #### Docker Deployment Docker provides the most secure way to run Automaker by isolating it from your host filesystem. diff --git a/apps/ui/package.json b/apps/ui/package.json index b28ad8c7..5742fb07 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -204,12 +204,35 @@ "arch": [ "x64" ] + }, + { + "target": "rpm", + "arch": [ + "x64" + ] } ], "category": "Development", "icon": "public/logo_larger.png", "maintainer": "webdevcody@gmail.com", - "executableName": "automaker" + "executableName": "automaker", + "description": "An autonomous AI development studio that helps you build software faster using AI-powered agents", + "synopsis": "AI-powered autonomous development studio" + }, + "rpm": { + "depends": [ + "gtk3", + "libnotify", + "nss", + "libXScrnSaver", + "libXtst", + "xdg-utils", + "at-spi2-core", + "libuuid" + ], + "compression": "xz", + "vendor": "AutoMaker Team", + "license": "SEE LICENSE IN LICENSE" }, "nsis": { "oneClick": false, diff --git a/docs/install-fedora.md b/docs/install-fedora.md new file mode 100644 index 00000000..5877e8ea --- /dev/null +++ b/docs/install-fedora.md @@ -0,0 +1,479 @@ +# Installing Automaker on Fedora/RHEL + +This guide covers installation of Automaker on Fedora, RHEL, Rocky Linux, AlmaLinux, and other RPM-based distributions. + +## Prerequisites + +Automaker requires: + +- **64-bit x86_64 architecture** +- **Fedora 39+** or **RHEL 9+** (earlier versions may work but not officially supported) +- **4GB RAM minimum**, 8GB recommended +- **~300MB disk space** for installation +- **Internet connection** for installation and Claude API access + +### Authentication + +You'll need one of the following: + +- **Claude CLI** (recommended) - `claude login` +- **API key** - Set `ANTHROPIC_API_KEY` environment variable + +See main [README.md authentication section](../README.md#authentication) for details. + +## Installation + +### Option 1: Download and Install from GitHub + +1. Visit [GitHub Releases](https://github.com/AutoMaker-Org/automaker/releases) +2. Find the latest release and download the `.rpm` file: + - Download: `Automaker--x86_64.rpm` + +3. Install using dnf (Fedora): + + ```bash + sudo dnf install ./Automaker--x86_64.rpm + ``` + + Or using yum (RHEL/CentOS): + + ```bash + sudo yum localinstall ./Automaker--x86_64.rpm + ``` + +### Option 2: Install Directly from URL + +Install from GitHub releases URL without downloading first: + +**Fedora:** + +```bash +sudo dnf install https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm +``` + +**RHEL/CentOS:** + +```bash +sudo yum install https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm +``` + +## Running Automaker + +After successful installation, launch Automaker: + +### From Application Menu + +- Open Activities/Applications +- Search for "Automaker" +- Click to launch + +### From Terminal + +```bash +automaker +``` + +## System Requirements & Capabilities + +### Hardware Requirements + +| Component | Minimum | Recommended | +| ------------ | ----------------- | ----------- | +| CPU | Modern multi-core | 4+ cores | +| RAM | 4GB | 8GB+ | +| Disk | 300MB | 1GB+ | +| Architecture | x86_64 | x86_64 | + +### Required Dependencies + +The RPM package automatically installs these dependencies: + +``` +gtk3 - GTK+ GUI library +libnotify - Desktop notification library +nss - Network Security Services +libXScrnSaver - X11 screensaver library +libXtst - X11 testing library +xdg-utils - XDG standards utilities +at-spi2-core - Accessibility library +libuuid - UUID library +``` + +Most of these are pre-installed on typical Fedora/RHEL systems. + +### Optional Dependencies + +For development (source builds only): + +- Node.js 22+ +- npm 10+ + +The packaged application includes its own Electron runtime and does not require system Node.js. + +## Supported Distributions + +**Officially Tested:** + +- Fedora 39, 40 (latest) +- Rocky Linux 9 +- AlmaLinux 9 + +**Should Work:** + +- CentOS Stream 9+ +- openSUSE Leap/Tumbleweed (with compatibility layer) +- RHEL 9+ + +**Not Supported:** + +- RHEL 8 (glibc 2.28 too old, requires Node.js 22) +- CentOS 7 and earlier +- Fedora versions older than 39 + +## Configuration + +### Environment Variables + +Set authentication via environment variable: + +```bash +export ANTHROPIC_API_KEY=sk-ant-... +automaker +``` + +Or create `~/.config/automaker/.env`: + +``` +ANTHROPIC_API_KEY=sk-ant-... +``` + +### Configuration Directory + +Automaker stores configuration and cache in: + +``` +~/.automaker/ # Project-specific data +~/.config/automaker/ # Application configuration +~/.cache/automaker/ # Cache and temporary files +``` + +## Troubleshooting + +### Application Won't Start + +**Check installation:** + +```bash +rpm -qi automaker +rpm -V automaker +``` + +**Verify desktop file:** + +```bash +cat /usr/share/applications/automaker.desktop +``` + +**Run from terminal for error output:** + +```bash +automaker +``` + +### Missing Dependencies + +If dependencies fail to install automatically: + +**Fedora:** + +```bash +sudo dnf install gtk3 libnotify nss libXScrnSaver libXtst xdg-utils at-spi2-core libuuid +``` + +**RHEL/CentOS (enable EPEL first if needed):** + +```bash +sudo dnf install epel-release +sudo dnf install gtk3 libnotify nss libXScrnSaver libXtst xdg-utils at-spi2-core libuuid +``` + +### SELinux Denials + +If Automaker fails on SELinux-enforced systems: + +**Temporary workaround (testing):** + +```bash +# Set SELinux to permissive mode +sudo setenforce 0 + +# Run Automaker +automaker + +# Check for denials +sudo ausearch -m avc -ts recent | grep automaker + +# Re-enable SELinux +sudo setenforce 1 +``` + +**Permanent fix (not recommended for production):** +Create custom SELinux policy based on ausearch output. For support, see [GitHub Issues](https://github.com/AutoMaker-Org/automaker/issues). + +### Port Conflicts + +Automaker uses port 3008 for the internal server. If port is already in use: + +**Find process using port 3008:** + +```bash +sudo ss -tlnp | grep 3008 +# or +lsof -i :3008 +``` + +**Kill conflicting process (if safe):** + +```bash +sudo kill -9 +``` + +Or configure Automaker to use different port (see Configuration section). + +### Firewall Issues + +On Fedora with firewalld enabled: + +```bash +# Allow internal traffic (local development only) +sudo firewall-cmd --add-port=3008/tcp +sudo firewall-cmd --permanent --add-port=3008/tcp +``` + +### GPU/Acceleration + +Automaker uses Chromium for rendering. GPU acceleration should work automatically on supported systems. + +**Check acceleration:** + +- Look for "GPU acceleration" status in application settings +- Verify drivers: `lspci | grep VGA` + +**Disable acceleration if issues occur:** + +```bash +DISABLE_GPU_ACCELERATION=1 automaker +``` + +### Terminal/Worktree Issues + +If terminal emulator fails or git worktree operations hang: + +1. Check disk space: `df -h` +2. Verify git installation: `git --version` +3. Check /tmp permissions: `ls -la /tmp` +4. File a GitHub issue with error output + +### Unresponsive GUI + +If the application freezes: + +1. Wait 30 seconds (AI operations may be processing) +2. Check process: `ps aux | grep automaker` +3. Force quit if necessary: `killall automaker` +4. Check system resources: `free -h`, `top` + +### Network Issues + +If Claude API calls fail: + +```bash +# Test internet connectivity +ping claude.ai + +# Test API access +curl -I https://api.anthropic.com + +# Verify API key +echo $ANTHROPIC_API_KEY +``` + +## Uninstallation + +### Remove Application + +**Fedora:** + +```bash +sudo dnf remove automaker +``` + +**RHEL/CentOS:** + +```bash +sudo yum remove automaker +``` + +### Clean Configuration (Optional) + +Remove all user data and configuration: + +```bash +# Remove project-specific data +rm -rf ~/.automaker + +# Remove application configuration +rm -rf ~/.config/automaker + +# Remove cache +rm -rf ~/.cache/automaker +``` + +**Warning:** This removes all saved projects and settings. Ensure you have backups if needed. + +## Building from Source + +To build Automaker from source on Fedora/RHEL: + +**Prerequisites:** + +```bash +# Fedora +sudo dnf install nodejs npm git + +# RHEL (enable EPEL first) +sudo dnf install epel-release +sudo dnf install nodejs npm git +``` + +**Build steps:** + +```bash +# Clone repository +git clone https://github.com/AutoMaker-Org/automaker.git +cd automaker + +# Install dependencies +npm install + +# Build packages +npm run build:packages + +# Build Linux packages +npm run build:electron:linux + +# Packages in: apps/ui/release/ +ls apps/ui/release/*.rpm +``` + +See main [README.md](../README.md) for detailed build instructions. + +## Updating Automaker + +**Automatic Updates:** +Automaker checks for updates on startup. Install available updates through notifications. + +**Manual Update:** + +```bash +# Fedora +sudo dnf update automaker + +# RHEL/CentOS +sudo yum update automaker + +# Or reinstall latest release +sudo dnf remove automaker +sudo dnf install https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm +``` + +## Getting Help + +### Resources + +- [Main README](../README.md) - Project overview +- [CONTRIBUTING.md](../CONTRIBUTING.md) - Contributing guide +- [GitHub Issues](https://github.com/AutoMaker-Org/automaker/issues) - Bug reports & feature requests +- [Discussions](https://github.com/AutoMaker-Org/automaker/discussions) - Questions & community + +### Reporting Issues + +When reporting Fedora/RHEL issues, include: + +```bash +# System information +lsb_release -a +uname -m + +# Automaker version +rpm -qi automaker + +# Error output (run from terminal) +automaker 2>&1 | tee automaker.log + +# SELinux status +getenforce + +# Relevant system logs +sudo journalctl -xeu automaker.service (if systemd service exists) +``` + +## Performance Tips + +1. **Use SSD**: Faster than spinning disk, significantly improves performance +2. **Close unnecessary applications**: Free up RAM for AI agent processing +3. **Disable GPU acceleration if glitchy**: Set `DISABLE_GPU_ACCELERATION=1` +4. **Keep system updated**: `sudo dnf update` +5. **Use latest Fedora/RHEL**: Newer versions have better Electron support + +## Security Considerations + +### API Key Security + +Never commit API keys to version control: + +```bash +# Good: Use environment variable +export ANTHROPIC_API_KEY=sk-ant-... + +# Good: Use .env file (not in git) +echo "ANTHROPIC_API_KEY=sk-ant-..." > ~/.config/automaker/.env + +# Bad: Hardcoded in files +ANTHROPIC_API_KEY="sk-ant-..." (in any tracked file) +``` + +### SELinux Security + +Running with SELinux disabled (`setenforce 0`) reduces security. Create custom policy: + +1. Generate policy from audit logs: `ausearch -m avc -ts recent | grep automaker` +2. Use selinux-policy tools to create module +3. Install and test module +4. Keep SELinux enforcing + +### File Permissions + +Ensure configuration files are readable by user only: + +```bash +chmod 600 ~/.config/automaker/.env +chmod 700 ~/.automaker/ +chmod 700 ~/.config/automaker/ +``` + +## Known Limitations + +1. **Single display support**: Multi-monitor setups may have cursor synchronization issues +2. **X11 only**: Wayland support limited (runs under XWayland) +3. **No native systemd service**: Manual launcher or desktop file shortcut +4. **ARM/ARM64**: Not supported, x86_64 only + +## Contributing + +Found an issue or want to improve Fedora support? See [CONTRIBUTING.md](../CONTRIBUTING.md). + +--- + +**Last Updated**: 2026-01-16 +**Tested On**: Fedora 40, Rocky Linux 9, AlmaLinux 9 From 017ff3ca0a7bc9f76db3b0bb5fc9866a082d5e7c Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 12:33:45 +0530 Subject: [PATCH 02/25] fix: resolve TypeScript error in backlog plan loading Fix type mismatch in loadBacklogPlan where secureFs.readFile with 'utf-8' encoding returns union type string | Buffer, causing JSON.parse to fail type checking. Cast raw to string to satisfy TypeScript strict mode. Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/backlog-plan/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/routes/backlog-plan/common.ts b/apps/server/src/routes/backlog-plan/common.ts index 98142c30..254e8d40 100644 --- a/apps/server/src/routes/backlog-plan/common.ts +++ b/apps/server/src/routes/backlog-plan/common.ts @@ -77,7 +77,7 @@ export async function loadBacklogPlan(projectPath: string): Promise Date: Fri, 16 Jan 2026 12:47:29 +0530 Subject: [PATCH 03/25] fix: remove invalid license property from RPM configuration The 'license' property is not supported by electron-builder's RPM schema. Valid RPM properties are: afterInstall, afterRemove, appArmorProfile, artifactName, category, compression, depends, description, desktop, executableArgs, fpm, icon, maintainer, mimeTypes, packageCategory, packageName, publish, synopsis, vendor. This fix allows electron-builder to proceed to the RPM build stage. Co-Authored-By: Claude Haiku 4.5 --- apps/ui/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/ui/package.json b/apps/ui/package.json index 5742fb07..8d5a5ddb 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -231,8 +231,7 @@ "libuuid" ], "compression": "xz", - "vendor": "AutoMaker Team", - "license": "SEE LICENSE IN LICENSE" + "vendor": "AutoMaker Team" }, "nsis": { "oneClick": false, From 8b448b948132d0a05b6d4bfcf1bfdd18f794c240 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 12:54:45 +0530 Subject: [PATCH 04/25] fix: address CodeRabbit security and validation issues in Fedora docs and backlog plan Documentation improvements: - Fix GitHub URL placeholder issues in install-fedora.md - GitHub /latest/download/ endpoint doesn't support version substitution, use explicit download URL pattern instead - Improve security in network troubleshooting section: - Change ping target from claude.ai (marketing site) to api.anthropic.com (actual API) - Remove unsafe 'echo \$ANTHROPIC_API_KEY' command that exposes secrets in shell history - Use safe API key check with conditional output instead Code improvements: - apps/server/src/routes/backlog-plan/common.ts: Add Array.isArray() validation for stored plan shape before returning it. Ensures changes is actually an array, not just truthy, preventing downstream runtime errors. Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/backlog-plan/common.ts | 2 +- docs/install-fedora.md | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/server/src/routes/backlog-plan/common.ts b/apps/server/src/routes/backlog-plan/common.ts index 254e8d40..1fab1e2a 100644 --- a/apps/server/src/routes/backlog-plan/common.ts +++ b/apps/server/src/routes/backlog-plan/common.ts @@ -78,7 +78,7 @@ export async function loadBacklogPlan(projectPath: string): Promise-x86_64.rpm +# Replace v0.11.0 with the actual latest version +sudo dnf install https://github.com/AutoMaker-Org/automaker/releases/download/v0.11.0/Automaker-0.11.0-x86_64.rpm ``` **RHEL/CentOS:** ```bash -sudo yum install https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm +# Replace v0.11.0 with the actual latest version +sudo yum install https://github.com/AutoMaker-Org/automaker/releases/download/v0.11.0/Automaker-0.11.0-x86_64.rpm ``` ## Running Automaker @@ -289,13 +291,13 @@ If Claude API calls fail: ```bash # Test internet connectivity -ping claude.ai +ping -c 3 api.anthropic.com # Test API access curl -I https://api.anthropic.com -# Verify API key -echo $ANTHROPIC_API_KEY +# Verify API key is set (without exposing the value) +[ -n "$ANTHROPIC_API_KEY" ] && echo "API key is set" || echo "API key is NOT set" ``` ## Uninstallation @@ -384,7 +386,11 @@ sudo yum update automaker # Or reinstall latest release sudo dnf remove automaker -sudo dnf install https://github.com/AutoMaker-Org/automaker/releases/latest/download/Automaker--x86_64.rpm + +# Download the latest .rpm from releases page +# https://github.com/AutoMaker-Org/automaker/releases +# Then reinstall with: +# sudo dnf install ./Automaker--x86_64.rpm ``` ## Getting Help From c0ea1c736ad82b5446b928339de86f326ae479f9 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 13:11:29 +0530 Subject: [PATCH 05/25] fix(backlog-plan): clear running details and handle plan cleanup safely - Add setRunningDetails(null) in finally block of generate handler to prevent state leaks - Move clearBacklogPlan before response in apply handler and wrap in try-catch to prevent errors after headers sent Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/backlog-plan/routes/apply.ts | 13 +++++++++++-- .../src/routes/backlog-plan/routes/generate.ts | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/server/src/routes/backlog-plan/routes/apply.ts b/apps/server/src/routes/backlog-plan/routes/apply.ts index d2b45f40..9e0ae999 100644 --- a/apps/server/src/routes/backlog-plan/routes/apply.ts +++ b/apps/server/src/routes/backlog-plan/routes/apply.ts @@ -147,12 +147,21 @@ export function createApplyHandler() { } } + // Clear the plan before responding + try { + await clearBacklogPlan(projectPath); + } catch (error) { + logger.warn( + `[BacklogPlan] Failed to clear backlog plan after apply:`, + getErrorMessage(error) + ); + // Don't throw - operation succeeded, just cleanup failed + } + res.json({ success: true, appliedChanges, }); - - await clearBacklogPlan(projectPath); } catch (error) { logError(error, 'Apply backlog plan failed'); res.status(500).json({ success: false, error: getErrorMessage(error) }); diff --git a/apps/server/src/routes/backlog-plan/routes/generate.ts b/apps/server/src/routes/backlog-plan/routes/generate.ts index 59438538..0e9218e6 100644 --- a/apps/server/src/routes/backlog-plan/routes/generate.ts +++ b/apps/server/src/routes/backlog-plan/routes/generate.ts @@ -63,6 +63,7 @@ export function createGenerateHandler(events: EventEmitter, settingsService?: Se }) .finally(() => { setRunningState(false, null); + setRunningDetails(null); }); res.json({ success: true }); From bf93cdf0c4e939c4589ed725f8d85848b1078436 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 13:12:16 +0530 Subject: [PATCH 06/25] fix(backlog-plan): clear running details when stopping generation Add setRunningDetails(null) to stop handler to prevent state leaks when aborting operation. Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/backlog-plan/routes/stop.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/server/src/routes/backlog-plan/routes/stop.ts b/apps/server/src/routes/backlog-plan/routes/stop.ts index aa275c67..d969f1b1 100644 --- a/apps/server/src/routes/backlog-plan/routes/stop.ts +++ b/apps/server/src/routes/backlog-plan/routes/stop.ts @@ -3,7 +3,13 @@ */ import type { Request, Response } from 'express'; -import { getAbortController, setRunningState, getErrorMessage, logError } from '../common.js'; +import { + getAbortController, + setRunningState, + setRunningDetails, + getErrorMessage, + logError, +} from '../common.js'; export function createStopHandler() { return async (_req: Request, res: Response): Promise => { @@ -12,6 +18,7 @@ export function createStopHandler() { if (abortController) { abortController.abort(); setRunningState(false, null); + setRunningDetails(null); } res.json({ success: true }); } catch (error) { From 883ad2a04b18e3f3f37794b2afe6cea88c5033b7 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 13:13:48 +0530 Subject: [PATCH 07/25] fix(backlog-plan): clear running details in generate-plan finally block Ensure running details are cleared when generation completes or fails, preventing state leaks. Co-Authored-By: Claude Haiku 4.5 --- apps/server/src/routes/backlog-plan/generate-plan.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/server/src/routes/backlog-plan/generate-plan.ts b/apps/server/src/routes/backlog-plan/generate-plan.ts index bf99d2a4..e96ce8ea 100644 --- a/apps/server/src/routes/backlog-plan/generate-plan.ts +++ b/apps/server/src/routes/backlog-plan/generate-plan.ts @@ -17,7 +17,13 @@ import { resolvePhaseModel } from '@automaker/model-resolver'; import { FeatureLoader } from '../../services/feature-loader.js'; import { ProviderFactory } from '../../providers/provider-factory.js'; import { extractJsonWithArray } from '../../lib/json-extractor.js'; -import { logger, setRunningState, getErrorMessage, saveBacklogPlan } from './common.js'; +import { + logger, + setRunningState, + setRunningDetails, + getErrorMessage, + saveBacklogPlan, +} from './common.js'; import type { SettingsService } from '../../services/settings-service.js'; import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js'; @@ -225,5 +231,6 @@ ${userPrompt}`; throw error; } finally { setRunningState(false, null); + setRunningDetails(null); } } From becd79f1e3a004ef6a0831ff8d4ec5ef60bb0b2d Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 19:38:10 +0530 Subject: [PATCH 08/25] fix: add missing dmg-license dependency to fix release builds The release workflow was failing for all platforms because macOS DMG builder requires dmg-license. This single dependency was preventing AppImage, DEB, RPM, DMG, and EXE artifacts from being built and uploaded to any release since v0.7.3. Includes lockfile updates and conversion of git+ssh:// URLs to https:// to prevent SSH key requirement issues in CI/CD and across environments. Co-Authored-By: Claude Haiku 4.5 --- package-lock.json | 35 ++++++++++++++++------------------- package.json | 1 + 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd96e672..45fa6af8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "tree-kill": "1.2.2" }, "devDependencies": { + "dmg-license": "^1.0.11", "husky": "9.1.7", "lint-staged": "16.2.7", "prettier": "3.7.4", @@ -29,7 +30,7 @@ }, "apps/server": { "name": "@automaker/server", - "version": "0.10.0", + "version": "0.11.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@anthropic-ai/claude-agent-sdk": "0.1.76", @@ -80,7 +81,7 @@ }, "apps/ui": { "name": "@automaker/ui", - "version": "0.10.0", + "version": "0.11.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -6381,7 +6382,6 @@ "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" @@ -6462,8 +6462,7 @@ "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/@types/ws": { "version": "8.18.1", @@ -7488,7 +7487,6 @@ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.8" } @@ -7541,7 +7539,6 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=8" } @@ -8285,7 +8282,6 @@ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -8568,8 +8564,7 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/cors": { "version": "2.8.5", @@ -8590,7 +8585,6 @@ "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "buffer": "^5.1.0" } @@ -9062,7 +9056,6 @@ "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, "license": "MIT", - "optional": true, "os": [ "darwin" ], @@ -10006,8 +9999,7 @@ "engines": [ "node >=0.6.0" ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -10981,7 +10973,6 @@ "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, "license": "MIT", - "optional": true, "os": [ "darwin" ], @@ -11607,6 +11598,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11628,6 +11620,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11671,6 +11664,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11692,6 +11686,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11713,6 +11708,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11734,6 +11730,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11755,6 +11752,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11776,6 +11774,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11797,6 +11796,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13455,8 +13455,7 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/node-api-version": { "version": "0.2.1", @@ -15392,7 +15391,6 @@ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -16561,7 +16559,6 @@ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index a65e869c..27ef27bb 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "tree-kill": "1.2.2" }, "devDependencies": { + "dmg-license": "^1.0.11", "husky": "9.1.7", "lint-staged": "16.2.7", "prettier": "3.7.4", From 52a821d3bb2e6fc3abe47e497cbf5a654f23e965 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 19:43:09 +0530 Subject: [PATCH 09/25] fix: add --force flag to npm install in CI to allow platform-specific devDependencies dmg-license is a darwin-only package required for macOS DMG building. The CI runs on Linux, so npm install fails when trying to install a platform-specific devDependency. Using --force allows npm to skip platform mismatches instead of erroring out, allowing the build to proceed on non-darwin platforms where the darwin-only dependency will simply be skipped. Co-Authored-By: Claude Haiku 4.5 --- .github/actions/setup-project/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-project/action.yml b/.github/actions/setup-project/action.yml index 75030b90..0fcd1e4f 100644 --- a/.github/actions/setup-project/action.yml +++ b/.github/actions/setup-project/action.yml @@ -41,7 +41,8 @@ runs: # Use npm install instead of npm ci to correctly resolve platform-specific # optional dependencies (e.g., @tailwindcss/oxide, lightningcss binaries) # Skip scripts to avoid electron-builder install-app-deps which uses too much memory - run: npm install --ignore-scripts + # Use --force to allow platform-specific dev dependencies like dmg-license on non-darwin platforms + run: npm install --ignore-scripts --force - name: Install Linux native bindings shell: bash From e02fd889c293c3557d9cbb0a6987facf72ad0d01 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Fri, 16 Jan 2026 19:51:23 +0530 Subject: [PATCH 10/25] fix: add --force flag to npm install in format-check workflow Ensures dmg-license can be installed on Linux CI runners even though it's a darwin-only package. The --force flag allows npm to skip platform mismatches. Co-Authored-By: Claude Haiku 4.5 --- .github/workflows/format-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 39d5a257..d6904979 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -25,7 +25,7 @@ jobs: cache-dependency-path: package-lock.json - name: Install dependencies - run: npm install --ignore-scripts + run: npm install --ignore-scripts --force - name: Check formatting run: npm run format:check From 16ef026b384a117ead5956f9bc048a06aad32c98 Mon Sep 17 00:00:00 2001 From: Shirone Date: Fri, 16 Jan 2026 23:49:36 +0100 Subject: [PATCH 11/25] refactor: Centralize UUID generation with fallback support --- .../ui/src/components/views/analysis-view.tsx | 4 +-- .../components/views/github-issues-view.tsx | 4 +-- .../src/components/views/interview-view.tsx | 4 +-- .../event-hooks/event-hook-dialog.tsx | 3 +- apps/ui/src/lib/utils.ts | 36 +++++++++++++++++++ 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/apps/ui/src/components/views/analysis-view.tsx b/apps/ui/src/components/views/analysis-view.tsx index f551e0a8..e235a9e9 100644 --- a/apps/ui/src/components/views/analysis-view.tsx +++ b/apps/ui/src/components/views/analysis-view.tsx @@ -20,7 +20,7 @@ import { AlertCircle, ListChecks, } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn, generateUUID } from '@/lib/utils'; const logger = createLogger('AnalysisView'); @@ -638,7 +638,7 @@ ${Object.entries(projectAnalysis.filesByExtension) for (const detectedFeature of detectedFeatures) { await api.features.create(currentProject.path, { - id: crypto.randomUUID(), + id: generateUUID(), category: detectedFeature.category, description: detectedFeature.description, status: 'backlog', diff --git a/apps/ui/src/components/views/github-issues-view.tsx b/apps/ui/src/components/views/github-issues-view.tsx index 03275b02..0ae6e1e8 100644 --- a/apps/ui/src/components/views/github-issues-view.tsx +++ b/apps/ui/src/components/views/github-issues-view.tsx @@ -8,7 +8,7 @@ import { Button } from '@/components/ui/button'; import { ConfirmDialog } from '@/components/ui/confirm-dialog'; import { LoadingState } from '@/components/ui/loading-state'; import { ErrorState } from '@/components/ui/error-state'; -import { cn, pathsEqual } from '@/lib/utils'; +import { cn, pathsEqual, generateUUID } from '@/lib/utils'; import { toast } from 'sonner'; import { useGithubIssues, useIssueValidation, useIssuesFilter } from './github-issues-view/hooks'; import { IssueRow, IssueDetailPanel, IssuesListHeader } from './github-issues-view/components'; @@ -137,7 +137,7 @@ export function GitHubIssuesView() { .join('\n'); const feature = { - id: `issue-${issue.number}-${crypto.randomUUID()}`, + id: `issue-${issue.number}-${generateUUID()}`, title: issue.title, description, category: 'From GitHub', diff --git a/apps/ui/src/components/views/interview-view.tsx b/apps/ui/src/components/views/interview-view.tsx index 75d8ab27..b9b9997e 100644 --- a/apps/ui/src/components/views/interview-view.tsx +++ b/apps/ui/src/components/views/interview-view.tsx @@ -6,7 +6,7 @@ import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Bot, Send, User, Loader2, Sparkles, FileText, ArrowLeft, CheckCircle } from 'lucide-react'; -import { cn } from '@/lib/utils'; +import { cn, generateUUID } from '@/lib/utils'; import { getElectronAPI } from '@/lib/electron'; import { Markdown } from '@/components/ui/markdown'; import { useFileBrowser } from '@/contexts/file-browser-context'; @@ -345,7 +345,7 @@ export function InterviewView() { // Create initial feature in the features folder const initialFeature: Feature = { - id: crypto.randomUUID(), + id: generateUUID(), category: 'Core', description: 'Initial project setup', status: 'backlog' as const, diff --git a/apps/ui/src/components/views/settings-view/event-hooks/event-hook-dialog.tsx b/apps/ui/src/components/views/settings-view/event-hooks/event-hook-dialog.tsx index 68233b5a..3997d424 100644 --- a/apps/ui/src/components/views/settings-view/event-hooks/event-hook-dialog.tsx +++ b/apps/ui/src/components/views/settings-view/event-hooks/event-hook-dialog.tsx @@ -28,6 +28,7 @@ import type { EventHookHttpAction, } from '@automaker/types'; import { EVENT_HOOK_TRIGGER_LABELS } from '@automaker/types'; +import { generateUUID } from '@/lib/utils'; interface EventHookDialogProps { open: boolean; @@ -108,7 +109,7 @@ export function EventHookDialog({ open, onOpenChange, editingHook, onSave }: Eve const handleSave = () => { const hook: EventHook = { - id: editingHook?.id || crypto.randomUUID(), + id: editingHook?.id || generateUUID(), name: name.trim() || undefined, trigger, enabled: editingHook?.enabled ?? true, diff --git a/apps/ui/src/lib/utils.ts b/apps/ui/src/lib/utils.ts index e96d587c..27da4859 100644 --- a/apps/ui/src/lib/utils.ts +++ b/apps/ui/src/lib/utils.ts @@ -124,3 +124,39 @@ export const isMac = : typeof navigator !== 'undefined' && (/Mac/.test(navigator.userAgent) || (navigator.platform ? navigator.platform.toLowerCase().includes('mac') : false)); + +/** + * Generate a UUID v4 string. + * + * Uses crypto.randomUUID() when available (secure contexts: HTTPS or localhost). + * Falls back to crypto.getRandomValues() for non-secure contexts (e.g., Docker via HTTP). + * + * @returns A RFC 4122 compliant UUID v4 string (e.g., "550e8400-e29b-41d4-a716-446655440000") + */ +export function generateUUID(): string { + // Use native randomUUID if available (secure contexts: HTTPS or localhost) + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + return crypto.randomUUID(); + } + + // Fallback using crypto.getRandomValues() (works in all modern browsers, including non-secure contexts) + if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') { + const bytes = new Uint8Array(16); + crypto.getRandomValues(bytes); + + // Set version (4) and variant (RFC 4122) bits + bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4 + bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant RFC 4122 + + // Convert to hex string with proper UUID format + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; + } + + // Last resort fallback using Math.random() - less secure but ensures functionality + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} From bc09a22e1fbc4bc3455d2a12e89a8d1ab78a21b7 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 14:19:12 +0530 Subject: [PATCH 12/25] fix: extract app version from apps/ui/package.json instead of monorepo root The start-automaker.sh script now correctly sources the app version (0.12.0) from apps/ui/package.json instead of the monorepo version (1.0.0) from the root package.json. This ensures the launcher displays the correct Automaker application version. --- start-automaker.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/start-automaker.sh b/start-automaker.sh index cff17b87..a9b19e17 100755 --- a/start-automaker.sh +++ b/start-automaker.sh @@ -37,11 +37,11 @@ DEFAULT_SERVER_PORT=3008 WEB_PORT=$DEFAULT_WEB_PORT SERVER_PORT=$DEFAULT_SERVER_PORT -# Extract VERSION from package.json (using node for reliable JSON parsing) +# Extract VERSION from apps/ui/package.json (the actual app version, not monorepo version) if command -v node &> /dev/null; then - VERSION="v$(node -p "require('$SCRIPT_DIR/package.json').version" 2>/dev/null || echo "0.0.0")" + VERSION="v$(node -p "require('$SCRIPT_DIR/apps/ui/package.json').version" 2>/dev/null || echo "0.11.0")" else - VERSION=$(grep '"version"' "$SCRIPT_DIR/package.json" | head -1 | sed 's/.*"version"[^"]*"\([^"]*\)".*/v\1/') + VERSION=$(grep '"version"' "$SCRIPT_DIR/apps/ui/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[^"]*"\([^"]*\)".*/v\1/' || echo "v0.11.0") fi # ANSI Color codes (256-color palette) From 33fa138d21100f8ac0474bc680e04b4f8c6b9150 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 14:40:08 +0530 Subject: [PATCH 13/25] feat: add docker group support with sg docker command Improve Docker access handling by detecting and using 'sg docker' command when the user is in the docker group but hasn't logged out yet. This allows running docker commands without requiring a full session restart after `usermod -aG docker $USER`. Changes: - Detect docker group access and fall back to sg docker -c when needed - Export DOCKER_CMD variable for use throughout the script - Update all docker compose and docker ps commands to use DOCKER_CMD - Improve error messages to guide users on fixing docker access issues --- start-automaker.sh | 90 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/start-automaker.sh b/start-automaker.sh index a9b19e17..a2d3e54c 100755 --- a/start-automaker.sh +++ b/start-automaker.sh @@ -200,6 +200,8 @@ check_required_commands() { fi } +DOCKER_CMD="docker" + check_docker() { if ! command -v docker &> /dev/null; then echo "${C_RED}Error:${RESET} Docker is not installed or not in PATH" @@ -207,12 +209,22 @@ check_docker() { return 1 fi - if ! docker info &> /dev/null; then - echo "${C_RED}Error:${RESET} Docker daemon is not running" - echo "Please start Docker and try again" - return 1 + if ! docker info &> /dev/null 2>&1; then + if sg docker -c "docker info" &> /dev/null 2>&1; then + DOCKER_CMD="sg docker -c" + else + echo "${C_RED}Error:${RESET} Docker daemon is not running or not accessible" + echo "" + echo "To fix, run:" + echo " sudo usermod -aG docker \$USER" + echo "" + echo "Then either log out and back in, or run:" + echo " newgrp docker" + return 1 + fi fi + export DOCKER_CMD return 0 } @@ -291,7 +303,11 @@ check_running_containers() { local running_containers="" # Get list of running automaker containers - running_containers=$(docker ps --filter "name=automaker-dev" --format "{{.Names}}" 2>/dev/null | tr '\n' ' ') + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + running_containers=$(sg docker -c "docker ps --filter 'name=automaker-dev' --format '{{{{Names}}}}'" 2>/dev/null | tr '\n' ' ' || true) + else + running_containers=$($DOCKER_CMD ps --filter "name=automaker-dev" --format "{{.Names}}" 2>/dev/null | tr '\n' ' ' || true) + fi if [ -n "$running_containers" ] && [ "$running_containers" != " " ]; then get_term_size @@ -319,9 +335,13 @@ check_running_containers() { [sS]|[sS][tT][oO][pP]) echo "" center_print "Stopping existing containers..." "$C_YELLOW" - docker compose -f "$compose_file" down 2>/dev/null || true - # Also try stopping any orphaned containers - docker ps --filter "name=automaker-dev" -q 2>/dev/null | xargs -r docker stop 2>/dev/null || true + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + sg docker -c "docker compose -f '$compose_file' down" 2>/dev/null || true + sg docker -c "docker ps --filter 'name=automaker-dev' -q" 2>/dev/null | xargs -r sg docker -c "docker stop" 2>/dev/null || true + else + $DOCKER_CMD compose -f "$compose_file" down 2>/dev/null || true + $DOCKER_CMD ps --filter "name=automaker-dev" -q 2>/dev/null | xargs -r $DOCKER_CMD stop 2>/dev/null || true + fi center_print "✓ Containers stopped" "$C_GREEN" echo "" return 0 # Continue with fresh start @@ -329,7 +349,11 @@ check_running_containers() { [rR]|[rR][eE][sS][tT][aA][rR][tT]) echo "" center_print "Stopping and rebuilding containers..." "$C_YELLOW" - docker compose -f "$compose_file" down 2>/dev/null || true + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + sg docker -c "docker compose -f '$compose_file' down" 2>/dev/null || true + else + $DOCKER_CMD compose -f "$compose_file" down 2>/dev/null || true + fi center_print "✓ Ready to rebuild" "$C_GREEN" echo "" return 0 # Continue with rebuild @@ -1170,10 +1194,18 @@ case $MODE in center_print "API: http://localhost:$DEFAULT_SERVER_PORT" "$C_GREEN" center_print "Press Ctrl+C to detach" "$C_MUTE" echo "" - if [ -f "docker-compose.override.yml" ]; then - docker compose -f docker-compose.dev.yml -f docker-compose.override.yml logs -f + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + if [ -f "docker-compose.override.yml" ]; then + sg docker -c "docker compose -f 'docker-compose.dev.yml' -f 'docker-compose.override.yml' logs -f" + else + sg docker -c "docker compose -f 'docker-compose.dev.yml' logs -f" + fi else - docker compose -f docker-compose.dev.yml logs -f + if [ -f "docker-compose.override.yml" ]; then + $DOCKER_CMD compose -f docker-compose.dev.yml -f docker-compose.override.yml logs -f + else + $DOCKER_CMD compose -f docker-compose.dev.yml logs -f + fi fi else echo "" @@ -1192,10 +1224,18 @@ case $MODE in echo "" center_print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" "$C_GRAY" echo "" - if [ -f "docker-compose.override.yml" ]; then - docker compose -f docker-compose.dev.yml -f docker-compose.override.yml up --build + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + if [ -f "docker-compose.override.yml" ]; then + sg docker -c "docker compose -f 'docker-compose.dev.yml' -f 'docker-compose.override.yml' up --build" + else + sg docker -c "docker compose -f 'docker-compose.dev.yml' up --build" + fi else - docker compose -f docker-compose.dev.yml up --build + if [ -f "docker-compose.override.yml" ]; then + $DOCKER_CMD compose -f docker-compose.dev.yml -f docker-compose.override.yml up --build + else + $DOCKER_CMD compose -f docker-compose.dev.yml up --build + fi fi fi ;; @@ -1235,10 +1275,18 @@ case $MODE in else center_print "Starting Docker server container..." "$C_MUTE" echo "" - if [ -f "docker-compose.override.yml" ]; then - docker compose -f docker-compose.dev-server.yml -f docker-compose.override.yml up --build & + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + if [ -f "docker-compose.override.yml" ]; then + sg docker -c "docker compose -f 'docker-compose.dev-server.yml' -f 'docker-compose.override.yml' up --build" & + else + sg docker -c "docker compose -f 'docker-compose.dev-server.yml' up --build" & + fi else - docker compose -f docker-compose.dev-server.yml up --build & + if [ -f "docker-compose.override.yml" ]; then + $DOCKER_CMD compose -f docker-compose.dev-server.yml -f docker-compose.override.yml up --build & + else + $DOCKER_CMD compose -f docker-compose.dev-server.yml up --build & + fi fi DOCKER_PID=$! fi @@ -1284,7 +1332,11 @@ case $MODE in echo "" center_print "Shutting down Docker container..." "$C_MUTE" [ -n "$DOCKER_PID" ] && kill $DOCKER_PID 2>/dev/null || true - docker compose -f docker-compose.dev-server.yml down 2>/dev/null || true + if [ "$DOCKER_CMD" = "sg docker -c" ]; then + sg docker -c "docker compose -f 'docker-compose.dev-server.yml' down" 2>/dev/null || true + else + $DOCKER_CMD compose -f docker-compose.dev-server.yml down 2>/dev/null || true + fi center_print "Done!" "$C_GREEN" ;; esac From a0471098faa7eb87f5ceb7d4647bc1fc69cbda06 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 14:53:06 +0530 Subject: [PATCH 14/25] fix: use specific data-testid selectors in project switcher assertions Replace generic getByRole('button', { name: /.../ }) selectors with specific getByTestId('project-switcher-project-') to avoid strict mode violations where the selector resolves to multiple elements (project switcher button and sidebar button). Fixes failing E2E tests: - feature-manual-review-flow.spec.ts - new-project-creation.spec.ts - open-existing-project.spec.ts --- apps/ui/tests/features/feature-manual-review-flow.spec.ts | 4 ++-- apps/ui/tests/projects/new-project-creation.spec.ts | 4 ++-- apps/ui/tests/projects/open-existing-project.spec.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/ui/tests/features/feature-manual-review-flow.spec.ts b/apps/ui/tests/features/feature-manual-review-flow.spec.ts index 9456b215..42ee7c31 100644 --- a/apps/ui/tests/features/feature-manual-review-flow.spec.ts +++ b/apps/ui/tests/features/feature-manual-review-flow.spec.ts @@ -130,8 +130,8 @@ test.describe('Feature Manual Review Flow', () => { await page.waitForTimeout(300); } - // Verify we're on the correct project (project name appears in sidebar button) - await expect(page.getByRole('button', { name: new RegExp(projectName) })).toBeVisible({ + // Verify we're on the correct project (project switcher button shows project name) + await expect(page.getByTestId(`project-switcher-project-${projectName}`)).toBeVisible({ timeout: 10000, }); diff --git a/apps/ui/tests/projects/new-project-creation.spec.ts b/apps/ui/tests/projects/new-project-creation.spec.ts index ca95bd92..9d2f3362 100644 --- a/apps/ui/tests/projects/new-project-creation.spec.ts +++ b/apps/ui/tests/projects/new-project-creation.spec.ts @@ -77,8 +77,8 @@ test.describe('Project Creation', () => { } // Wait for project to be set as current and visible on the page - // The project name appears in the sidebar project selector button - await expect(page.getByRole('button', { name: new RegExp(projectName) })).toBeVisible({ + // The project name appears in the project switcher button + await expect(page.getByTestId(`project-switcher-project-${projectName}`)).toBeVisible({ timeout: 15000, }); diff --git a/apps/ui/tests/projects/open-existing-project.spec.ts b/apps/ui/tests/projects/open-existing-project.spec.ts index 7111d119..3f4a8a36 100644 --- a/apps/ui/tests/projects/open-existing-project.spec.ts +++ b/apps/ui/tests/projects/open-existing-project.spec.ts @@ -156,9 +156,9 @@ test.describe('Open Project', () => { } // Wait for a project to be set as current and visible on the page - // The project name appears in the sidebar project selector button + // The project name appears in the project switcher button if (targetProjectName) { - await expect(page.getByRole('button', { name: new RegExp(targetProjectName) })).toBeVisible({ + await expect(page.getByTestId(`project-switcher-project-${targetProjectName}`)).toBeVisible({ timeout: 15000, }); } From 545bf2045d8a20f45fcb55c522bdeefe08aed91a Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 15:00:48 +0530 Subject: [PATCH 15/25] fix: add --force flag to npm install in docker-compose files Allow npm to install platform-specific devDependencies (like dmg-license which is macOS-only) by skipping platform checks in Linux Docker containers. This matches the behavior already used in CI workflows. Fixes Docker container startup failure: - docker-compose.dev.yml (full stack development) - docker-compose.dev-server.yml (server-only with local Electron) The --force flag allows npm to proceed with installation even when some optional/platform-specific dependencies can't be installed on the current platform. --- docker-compose.dev-server.yml | 2 +- docker-compose.dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.dev-server.yml b/docker-compose.dev-server.yml index aad8fcef..5d0bae7b 100644 --- a/docker-compose.dev-server.yml +++ b/docker-compose.dev-server.yml @@ -81,7 +81,7 @@ services: # Run the rest as automaker user exec gosu automaker sh -c " echo 'Installing dependencies...' && - npm install && + npm install --force && echo 'Building shared packages...' && npm run build:packages && echo 'Starting server in development mode...' && diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 087bb095..cc273eaf 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -82,7 +82,7 @@ services: # Run the rest as automaker user exec gosu automaker sh -c " echo 'Installing dependencies...' && - npm install && + npm install --force && echo 'Building shared packages...' && npm run build:packages && echo 'Starting server in development mode...' && From bbdc11ce4793b1062ab70e08d73a5bce474dfc3d Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 15:30:21 +0530 Subject: [PATCH 16/25] fix: improve docker-compose npm install permissions and use npm ci 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. --- docker-compose.dev-server.yml | 8 ++++++-- docker-compose.dev.yml | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docker-compose.dev-server.yml b/docker-compose.dev-server.yml index 5d0bae7b..5c8efcbd 100644 --- a/docker-compose.dev-server.yml +++ b/docker-compose.dev-server.yml @@ -76,12 +76,16 @@ services: - | # Fix permissions on node_modules (created as root by Docker volume) echo 'Fixing node_modules permissions...' - chown -R automaker:automaker /app/node_modules 2>/dev/null || true + rm -rf /app/node_modules 2>/dev/null || true + rm -rf /app/apps/ui/node_modules 2>/dev/null || true + mkdir -p /app/node_modules + chown -R automaker:automaker /app/node_modules + chmod -R u+rwX /app/node_modules # Run the rest as automaker user exec gosu automaker sh -c " echo 'Installing dependencies...' && - npm install --force && + npm ci --force && echo 'Building shared packages...' && npm run build:packages && echo 'Starting server in development mode...' && diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index cc273eaf..61f578f2 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -77,12 +77,16 @@ services: - | # Fix permissions on node_modules (created as root by Docker volume) echo 'Fixing node_modules permissions...' - chown -R automaker:automaker /app/node_modules 2>/dev/null || true + rm -rf /app/node_modules 2>/dev/null || true + rm -rf /app/apps/ui/node_modules 2>/dev/null || true + mkdir -p /app/node_modules + chown -R automaker:automaker /app/node_modules + chmod -R u+rwX /app/node_modules # Run the rest as automaker user exec gosu automaker sh -c " echo 'Installing dependencies...' && - npm install --force && + npm ci --force && echo 'Building shared packages...' && npm run build:packages && echo 'Starting server in development mode...' && From 92afbeb6bdaf5162a9d5754eff65621e9f5f4065 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 15:36:50 +0530 Subject: [PATCH 17/25] fix: run npm install as root to avoid permission issues 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. --- docker-compose.dev-server.yml | 25 +++++++++---------------- docker-compose.dev.yml | 25 +++++++++---------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/docker-compose.dev-server.yml b/docker-compose.dev-server.yml index 5c8efcbd..106ed39f 100644 --- a/docker-compose.dev-server.yml +++ b/docker-compose.dev-server.yml @@ -74,23 +74,16 @@ services: command: - -c - | - # Fix permissions on node_modules (created as root by Docker volume) - echo 'Fixing node_modules permissions...' - rm -rf /app/node_modules 2>/dev/null || true - rm -rf /app/apps/ui/node_modules 2>/dev/null || true - mkdir -p /app/node_modules - chown -R automaker:automaker /app/node_modules - chmod -R u+rwX /app/node_modules + # Install as root to avoid permission issues with named volumes + echo 'Installing dependencies...' && + npm ci --legacy-peer-deps && + echo 'Building shared packages...' && + npm run build:packages && - # Run the rest as automaker user - exec gosu automaker sh -c " - echo 'Installing dependencies...' && - npm ci --force && - echo 'Building shared packages...' && - npm run build:packages && - echo 'Starting server in development mode...' && - npm run _dev:server - " + # Fix permissions and start server as automaker user + chown -R automaker:automaker /app/node_modules && + echo 'Starting server in development mode...' && + exec gosu automaker npm run _dev:server healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3008/api/health'] interval: 10s diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 61f578f2..7c6c6225 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -75,23 +75,16 @@ services: command: - -c - | - # Fix permissions on node_modules (created as root by Docker volume) - echo 'Fixing node_modules permissions...' - rm -rf /app/node_modules 2>/dev/null || true - rm -rf /app/apps/ui/node_modules 2>/dev/null || true - mkdir -p /app/node_modules - chown -R automaker:automaker /app/node_modules - chmod -R u+rwX /app/node_modules + # Install as root to avoid permission issues with named volumes + echo 'Installing dependencies...' && + npm ci --legacy-peer-deps && + echo 'Building shared packages...' && + npm run build:packages && - # Run the rest as automaker user - exec gosu automaker sh -c " - echo 'Installing dependencies...' && - npm ci --force && - echo 'Building shared packages...' && - npm run build:packages && - echo 'Starting server in development mode...' && - npm run _dev:server - " + # Fix permissions and start server as automaker user + chown -R automaker:automaker /app/node_modules && + echo 'Starting server in development mode...' && + exec gosu automaker npm run _dev:server healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3008/api/health'] interval: 10s From ef6b9ac2d23860b98108008107beec0542e2e533 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 15:53:31 +0530 Subject: [PATCH 18/25] fix: add --force flag to npm ci for platform-specific dependencies 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. --- docker-compose.dev-server.yml | 3 ++- docker-compose.dev.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.dev-server.yml b/docker-compose.dev-server.yml index 106ed39f..9ff0972e 100644 --- a/docker-compose.dev-server.yml +++ b/docker-compose.dev-server.yml @@ -75,8 +75,9 @@ services: - -c - | # Install as root to avoid permission issues with named volumes + # Use --force to skip platform-specific devDependencies (dmg-license is macOS-only) echo 'Installing dependencies...' && - npm ci --legacy-peer-deps && + npm ci --legacy-peer-deps --force && echo 'Building shared packages...' && npm run build:packages && diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7c6c6225..de4ebb11 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -76,8 +76,9 @@ services: - -c - | # Install as root to avoid permission issues with named volumes + # Use --force to skip platform-specific devDependencies (dmg-license is macOS-only) echo 'Installing dependencies...' && - npm ci --legacy-peer-deps && + npm ci --legacy-peer-deps --force && echo 'Building shared packages...' && npm run build:packages && From 2d9e38ad99b88d5a6af39b671e7de3b3f71fad89 Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Sat, 17 Jan 2026 12:32:42 +0100 Subject: [PATCH 19/25] fix: stop repeated GitHub PR fetch warnings for non-GitHub repos 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. --- .../server/src/routes/worktree/routes/list.ts | 74 +++++++++++++++++-- .../worktree-panel/hooks/use-worktrees.ts | 5 +- apps/ui/src/lib/electron.ts | 7 +- apps/ui/src/lib/http-api-client.ts | 4 +- apps/ui/src/types/electron.d.ts | 3 +- 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/apps/server/src/routes/worktree/routes/list.ts b/apps/server/src/routes/worktree/routes/list.ts index a7c12f98..4d623414 100644 --- a/apps/server/src/routes/worktree/routes/list.ts +++ b/apps/server/src/routes/worktree/routes/list.ts @@ -16,10 +16,27 @@ import { isGitRepo } from '@automaker/git-utils'; import { getErrorMessage, logError, normalizePath, execEnv, isGhCliAvailable } from '../common.js'; import { readAllWorktreeMetadata, type WorktreePRInfo } from '../../../lib/worktree-metadata.js'; import { createLogger } from '@automaker/utils'; +import { + checkGitHubRemote, + type GitHubRemoteStatus, +} from '../../github/routes/check-github-remote.js'; const execAsync = promisify(exec); const logger = createLogger('Worktree'); +/** + * Cache for GitHub remote status per project path. + * This prevents repeated "no git remotes found" warnings when polling + * projects that don't have a GitHub remote configured. + */ +interface GitHubRemoteCacheEntry { + status: GitHubRemoteStatus; + checkedAt: number; +} + +const githubRemoteCache = new Map(); +const GITHUB_REMOTE_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes + interface WorktreeInfo { path: string; branch: string; @@ -121,23 +138,63 @@ async function scanWorktreesDirectory( return discovered; } +/** + * Get cached GitHub remote status for a project, or check and cache it. + * Returns null if gh CLI is not available. + */ +async function getGitHubRemoteStatus(projectPath: string): Promise { + // Check if gh CLI is available first + const ghAvailable = await isGhCliAvailable(); + if (!ghAvailable) { + return null; + } + + const now = Date.now(); + const cached = githubRemoteCache.get(projectPath); + + // Return cached result if still valid + if (cached && now - cached.checkedAt < GITHUB_REMOTE_CACHE_TTL_MS) { + return cached.status; + } + + // Check GitHub remote and cache the result + const status = await checkGitHubRemote(projectPath); + githubRemoteCache.set(projectPath, { + status, + checkedAt: now, + }); + + return status; +} + /** * Fetch open PRs from GitHub and create a map of branch name to PR info. * This allows detecting PRs that were created outside the app. + * + * Uses cached GitHub remote status to avoid repeated warnings when the + * project doesn't have a GitHub remote configured. */ async function fetchGitHubPRs(projectPath: string): Promise> { const prMap = new Map(); try { - // Check if gh CLI is available - const ghAvailable = await isGhCliAvailable(); - if (!ghAvailable) { + // Check GitHub remote status (uses cache to avoid repeated warnings) + const remoteStatus = await getGitHubRemoteStatus(projectPath); + + // If gh CLI not available or no GitHub remote, return empty silently + if (!remoteStatus || !remoteStatus.hasGitHubRemote) { return prMap; } + // Use -R flag with owner/repo for more reliable PR fetching + const repoFlag = + remoteStatus.owner && remoteStatus.repo + ? `-R ${remoteStatus.owner}/${remoteStatus.repo}` + : ''; + // Fetch open PRs from GitHub const { stdout } = await execAsync( - 'gh pr list --state open --json number,title,url,state,headRefName,createdAt --limit 1000', + `gh pr list ${repoFlag} --state open --json number,title,url,state,headRefName,createdAt --limit 1000`, { cwd: projectPath, env: execEnv, timeout: 15000 } ); @@ -170,9 +227,10 @@ async function fetchGitHubPRs(projectPath: string): Promise => { try { - const { projectPath, includeDetails } = req.body as { + const { projectPath, includeDetails, forceRefreshGitHub } = req.body as { projectPath: string; includeDetails?: boolean; + forceRefreshGitHub?: boolean; }; if (!projectPath) { @@ -180,6 +238,12 @@ export function createListHandler() { return; } + // Clear GitHub remote cache if force refresh requested + // This allows users to re-check for GitHub remote after adding one + if (forceRefreshGitHub) { + githubRemoteCache.delete(projectPath); + } + if (!(await isGitRepo(projectPath))) { res.json({ success: true, worktrees: [] }); return; diff --git a/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktrees.ts b/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktrees.ts index 1575f38a..95589f4b 100644 --- a/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktrees.ts +++ b/apps/ui/src/components/views/board-view/worktree-panel/hooks/use-worktrees.ts @@ -39,7 +39,10 @@ export function useWorktrees({ logger.warn('Worktree API not available'); return; } - const result = await api.worktree.listAll(projectPath, true); + // Pass forceRefreshGitHub when this is a manual refresh (not silent polling) + // This clears the GitHub remote cache so users can re-detect after adding a remote + const forceRefreshGitHub = !silent; + const result = await api.worktree.listAll(projectPath, true, forceRefreshGitHub); if (result.success && result.worktrees) { setWorktrees(result.worktrees); setWorktreesInStore(projectPath, result.worktrees); diff --git a/apps/ui/src/lib/electron.ts b/apps/ui/src/lib/electron.ts index 97167e89..66bbd537 100644 --- a/apps/ui/src/lib/electron.ts +++ b/apps/ui/src/lib/electron.ts @@ -1596,10 +1596,15 @@ function createMockWorktreeAPI(): WorktreeAPI { return { success: true, worktrees: [] }; }, - listAll: async (projectPath: string, includeDetails?: boolean) => { + listAll: async ( + projectPath: string, + includeDetails?: boolean, + forceRefreshGitHub?: boolean + ) => { console.log('[Mock] Listing all worktrees:', { projectPath, includeDetails, + forceRefreshGitHub, }); return { success: true, diff --git a/apps/ui/src/lib/http-api-client.ts b/apps/ui/src/lib/http-api-client.ts index 547fee7f..d7ac5280 100644 --- a/apps/ui/src/lib/http-api-client.ts +++ b/apps/ui/src/lib/http-api-client.ts @@ -1724,8 +1724,8 @@ export class HttpApiClient implements ElectronAPI { getStatus: (projectPath: string, featureId: string) => this.post('/api/worktree/status', { projectPath, featureId }), list: (projectPath: string) => this.post('/api/worktree/list', { projectPath }), - listAll: (projectPath: string, includeDetails?: boolean) => - this.post('/api/worktree/list', { projectPath, includeDetails }), + listAll: (projectPath: string, includeDetails?: boolean, forceRefreshGitHub?: boolean) => + this.post('/api/worktree/list', { projectPath, includeDetails, forceRefreshGitHub }), create: (projectPath: string, branchName: string, baseBranch?: string) => this.post('/api/worktree/create', { projectPath, diff --git a/apps/ui/src/types/electron.d.ts b/apps/ui/src/types/electron.d.ts index 42e4200d..49c1c4ad 100644 --- a/apps/ui/src/types/electron.d.ts +++ b/apps/ui/src/types/electron.d.ts @@ -705,7 +705,8 @@ export interface WorktreeAPI { // List all worktrees with details (for worktree selector) listAll: ( projectPath: string, - includeDetails?: boolean + includeDetails?: boolean, + forceRefreshGitHub?: boolean ) => Promise<{ success: boolean; worktrees?: Array<{ From 5cd4183a7b567dffee36c8067781d065a287cce6 Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Sat, 17 Jan 2026 12:36:33 +0100 Subject: [PATCH 20/25] fix: use fresh timestamp when setting cache entry Use Date.now() after checkGitHubRemote() completes instead of the pre-captured timestamp to ensure accurate 5-minute TTL. --- apps/server/src/routes/worktree/routes/list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/routes/worktree/routes/list.ts b/apps/server/src/routes/worktree/routes/list.ts index 4d623414..96782d64 100644 --- a/apps/server/src/routes/worktree/routes/list.ts +++ b/apps/server/src/routes/worktree/routes/list.ts @@ -161,7 +161,7 @@ async function getGitHubRemoteStatus(projectPath: string): Promise Date: Sat, 17 Jan 2026 13:53:34 +0100 Subject: [PATCH 21/25] docs: add hint about AUTOMAKER_API_KEY env var to API key banner 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. --- apps/server/src/lib/auth.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server/src/lib/auth.ts b/apps/server/src/lib/auth.ts index 0a4b5389..4626ed25 100644 --- a/apps/server/src/lib/auth.ts +++ b/apps/server/src/lib/auth.ts @@ -142,6 +142,8 @@ if (process.env.AUTOMAKER_HIDE_API_KEY !== 'true') { ║ ${API_KEY} ║ ║ ║ In Electron mode, authentication is handled automatically. ║ +║ ║ +║ 💡 Tip: Set AUTOMAKER_API_KEY env var to use a fixed key for dev ║ ╚═══════════════════════════════════════════════════════════════════════╝ `); } else { From 466c34afd45ec41c92f6665d87dc38d46c56d8d4 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 19:18:15 +0530 Subject: [PATCH 22/25] ci: improve release workflow artifact uploads - 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 --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d85d1425..50023666 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,8 +109,8 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - artifacts/macos-builds/* - artifacts/windows-builds/* - artifacts/linux-builds/* + artifacts/macos-builds/*.{dmg,zip,blockmap} + artifacts/windows-builds/*.{exe,blockmap} + artifacts/linux-builds/*.{AppImage,deb,rpm,blockmap} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9c3b3a41047b74d4dc0e63de48e9180191748947 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 20:32:10 +0530 Subject: [PATCH 23/25] fix: make dynamic models select-all checkbox respect search filters 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 --- .../providers/opencode-model-configuration.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx b/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx index a2b11381..3d2d0fb6 100644 --- a/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx +++ b/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx @@ -611,16 +611,16 @@ export function OpencodeModelConfiguration({ Dynamic - {models.length > 0 && ( + {filteredModels.length > 0 && (
model.id), + filteredModels.map((model) => model.id), enabledDynamicModelIds )} onCheckedChange={(checked) => toggleProviderDynamicModels( - models.map((model) => model.id), + filteredModels.map((model) => model.id), checked ) } From 24042d20c2554b2648e7f76c3faabb00d363e5c0 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 20:55:04 +0530 Subject: [PATCH 24/25] fix: filter dynamic OpenCode models by enabled status in model selector 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 --- .../model-defaults/phase-model-selector.tsx | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx index ddd48b46..392445e0 100644 --- a/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx +++ b/apps/ui/src/components/views/settings-view/model-defaults/phase-model-selector.tsx @@ -166,6 +166,7 @@ export function PhaseModelSelector({ codexModelsLoading, fetchCodexModels, dynamicOpencodeModels, + enabledDynamicModelIds, opencodeModelsLoading, fetchOpencodeModels, disabledProviders, @@ -383,13 +384,16 @@ export function PhaseModelSelector({ const staticModels = [...OPENCODE_MODELS]; // Add dynamic models (convert ModelDefinition to ModelOption) - const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels.map((model) => ({ - id: model.id, - label: model.name, - description: model.description, - badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined, - provider: 'opencode' as const, - })); + // Only include dynamic models that are enabled by the user + const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels + .filter((model) => enabledDynamicModelIds.includes(model.id)) + .map((model) => ({ + id: model.id, + label: model.name, + description: model.description, + badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined, + provider: 'opencode' as const, + })); // Merge, avoiding duplicates (static models take precedence for same ID) // In practice, static and dynamic IDs don't overlap @@ -397,7 +401,7 @@ export function PhaseModelSelector({ const uniqueDynamic = dynamicModelOptions.filter((m) => !staticIds.has(m.id)); return [...staticModels, ...uniqueDynamic]; - }, [dynamicOpencodeModels]); + }, [dynamicOpencodeModels, enabledDynamicModelIds]); // Group models (filtering out disabled providers) const { favorites, claude, cursor, codex, opencode } = useMemo(() => { From 0480f6ccd68b76a88f6d9b39b5adfeb01523bf57 Mon Sep 17 00:00:00 2001 From: DhanushSantosh Date: Sat, 17 Jan 2026 21:13:47 +0530 Subject: [PATCH 25/25] fix: handle dynamic model IDs with slashes in the model name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- libs/types/src/provider-utils.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libs/types/src/provider-utils.ts b/libs/types/src/provider-utils.ts index 3143b336..4e4501fe 100644 --- a/libs/types/src/provider-utils.ts +++ b/libs/types/src/provider-utils.ts @@ -125,11 +125,14 @@ export function isOpencodeModel(model: string | undefined | null): boolean { // - github-copilot/gpt-4o // - google/gemini-2.5-pro // - xai/grok-3 - // Pattern: provider-id/model-name (must have exactly one / and not be a URL) + // - openrouter/qwen/qwen3-14b:free (model names can contain / or :) + // Pattern: provider-id/model-name (at least one /, not a URL) if (model.includes('/') && !model.includes('://')) { - const parts = model.split('/'); - // Valid dynamic model format: provider/model-name (exactly 2 parts) - if (parts.length === 2 && parts[0].length > 0 && parts[1].length > 0) { + const slashIndex = model.indexOf('/'); + const providerId = model.substring(0, slashIndex); + const modelName = model.substring(slashIndex + 1); + // Valid dynamic model format: provider-id/model-name (both parts non-empty) + if (providerId.length > 0 && modelName.length > 0) { return true; } }