Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/actions/setup-cmux/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,14 @@ runs:
sudo apt-get install -y --no-install-recommends imagemagick
fi
convert --version | head -1
- name: Install ImageMagick (Windows)
if: inputs.install-imagemagick == 'true' && runner.os == 'Windows'
shell: powershell
run: |
if (Get-Command magick -ErrorAction SilentlyContinue) {
Write-Host "✅ ImageMagick already available"
} else {
Write-Host "📦 Installing ImageMagick..."
choco install -y imagemagick
}
magick --version | Select-Object -First 1
31 changes: 31 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,34 @@ jobs:
run: bun x electron-builder --linux --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build-windows:
name: Build and Release Windows
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for git describe to find tags

- uses: ./.github/actions/setup-cmux
with:
install-imagemagick: true

- name: Install GNU Make (for build)
run: choco install -y make

- name: Verify tools
shell: bash
run: |
make --version
bun --version
magick --version | head -1

- name: Build application
run: bun run build

- name: Package and publish for Windows (.exe)
run: bun x electron-builder --win --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 changes: 18 additions & 19 deletions .github/workflows/terminal-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ on:
workflow_call:
inputs:
model_name:
description: 'Model to use (e.g., anthropic:claude-sonnet-4-5)'
description: "Model to use (e.g., anthropic:claude-sonnet-4-5)"
required: false
type: string
thinking_level:
description: 'Thinking level (off, low, medium, high)'
description: "Thinking level (off, low, medium, high)"
required: false
type: string
dataset:
description: 'Terminal-Bench dataset to use'
description: "Terminal-Bench dataset to use"
required: false
type: string
default: 'terminal-bench-core==0.1.1'
default: "terminal-bench-core==0.1.1"
concurrency:
description: 'Number of concurrent tasks (--n-concurrent)'
description: "Number of concurrent tasks (--n-concurrent)"
required: false
type: string
default: '4'
default: "4"
livestream:
description: 'Enable livestream mode (verbose output to console)'
description: "Enable livestream mode (verbose output to console)"
required: false
type: boolean
default: false
sample_size:
description: 'Number of random tasks to run (empty = all tasks)'
description: "Number of random tasks to run (empty = all tasks)"
required: false
type: string
extra_args:
description: 'Additional arguments to pass to terminal-bench'
description: "Additional arguments to pass to terminal-bench"
required: false
type: string
secrets:
Expand All @@ -42,34 +42,34 @@ on:
workflow_dispatch:
inputs:
dataset:
description: 'Terminal-Bench dataset to use'
description: "Terminal-Bench dataset to use"
required: false
default: 'terminal-bench-core==0.1.1'
default: "terminal-bench-core==0.1.1"
type: string
concurrency:
description: 'Number of concurrent tasks (--n-concurrent)'
description: "Number of concurrent tasks (--n-concurrent)"
required: false
default: '4'
default: "4"
type: string
livestream:
description: 'Enable livestream mode (verbose output to console)'
description: "Enable livestream mode (verbose output to console)"
required: false
default: false
type: boolean
sample_size:
description: 'Number of random tasks to run (empty = all tasks)'
description: "Number of random tasks to run (empty = all tasks)"
required: false
type: string
model_name:
description: 'Model to use (e.g., anthropic:claude-sonnet-4-5, openai:gpt-5-codex)'
description: "Model to use (e.g., anthropic:claude-sonnet-4-5, openai:gpt-5-codex)"
required: false
type: string
thinking_level:
description: 'Thinking level (off, low, medium, high)'
description: "Thinking level (off, low, medium, high)"
required: false
type: string
extra_args:
description: 'Additional arguments to pass to terminal-bench'
description: "Additional arguments to pass to terminal-bench"
required: false
type: string

Expand Down Expand Up @@ -147,4 +147,3 @@ jobs:
benchmark.log
if-no-files-found: warn
retention-days: 30

91 changes: 56 additions & 35 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
# Branches reduce reproducibility - builds should fail fast with clear errors
# if dependencies are missing, not silently fall back to different behavior.

# Use PATH-resolved bash on Windows to avoid hardcoded /usr/bin/bash which doesn't
# exist in Chocolatey's make environment or on GitHub Actions windows-latest.
ifeq ($(OS),Windows_NT)
SHELL := bash
# Windows: Use npm/npx because bun x doesn't correctly pass arguments on Windows
RUNNER := npx
else
SHELL := /bin/bash
# Non-Windows: Use bun x for better performance
RUNNER := bun x
endif
.SHELLFLAGS := -eu -o pipefail -c

# Enable parallel execution by default (only if user didn't specify -j)
ifeq (,$(filter -j%,$(MAKEFLAGS)))
MAKEFLAGS += -j
Expand All @@ -32,7 +45,7 @@ endif
# Include formatting rules
include fmt.mk

.PHONY: all build dev start clean help
.PHONY: all build dev start clean clean-cache help
.PHONY: build-renderer version build-icons build-static
.PHONY: lint lint-fix typecheck static-check
.PHONY: test test-unit test-integration test-watch test-coverage test-e2e
Expand Down Expand Up @@ -91,26 +104,33 @@ help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'

## Development
dev: node_modules/.installed build-main ## Start development server (Vite + tsgo watcher for 10x faster type checking)
@bun x concurrently -k \
"bun x concurrently \"$(TSGO) -w -p tsconfig.main.json\" \"bun x tsc-alias -w -p tsconfig.main.json\"" \
dev: node_modules/.installed build-main clean-cache ## Start development server (Vite + nodemon watcher for Windows compatibility)
@echo "Starting dev mode (2 watchers: nodemon for main process, vite for renderer)..."
@# On Windows, use npm run because bun x doesn't correctly pass arguments
@NODE_OPTIONS="--max-old-space-size=4096" $(RUNNER) concurrently -k --raw \
"$(RUNNER) nodemon --exec node scripts/build-main-watch.js" \
"vite"
Comment on lines 106 to 112

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Configure nodemon to watch TypeScript sources

The new dev workflow replaces the previous tsgo -w watcher with nodemon ($(RUNNER) nodemon --exec node scripts/build-main-watch.js) but doesn’t tell nodemon which file extensions to monitor. Nodemon’s default set only includes .js, .mjs, and .json, so edits to the main process .ts files will no longer trigger a rebuild or restart, leaving the Electron backend stale. The same pattern appears in dev-server below. Please add --ext ts,tsx --watch src (or similar) so main TypeScript sources are actually rebuilt during development.

Useful? React with 👍 / 👎.


clean-cache: ## Clean Vite cache (helps with EMFILE errors on Windows)
@echo "Cleaning Vite cache..."
@rm -rf node_modules/.vite

dev-server: node_modules/.installed build-main ## Start server mode with hot reload (backend :3000 + frontend :5173). Use VITE_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0 for remote access
@echo "Starting dev-server..."
@echo " Backend (IPC/WebSocket): http://$(or $(BACKEND_HOST),localhost):$(or $(BACKEND_PORT),3000)"
@echo " Frontend (with HMR): http://$(or $(VITE_HOST),localhost):$(or $(VITE_PORT),5173)"
@echo ""
@echo "For remote access: make dev-server VITE_HOST=0.0.0.0 BACKEND_HOST=0.0.0.0"
@bun x concurrently -k \
"bun x concurrently \"$(TSGO) -w -p tsconfig.main.json\" \"bun x tsc-alias -w -p tsconfig.main.json\"" \
"bun x nodemon --watch dist/main.js --watch dist/main-server.js --delay 500ms --exec 'node dist/main.js server --host $(or $(BACKEND_HOST),localhost) --port $(or $(BACKEND_PORT),3000)'" \
"CMUX_VITE_HOST=$(or $(VITE_HOST),127.0.0.1) CMUX_VITE_PORT=$(or $(VITE_PORT),5173) VITE_BACKEND_URL=http://$(or $(BACKEND_HOST),localhost):$(or $(BACKEND_PORT),3000) vite"
@# On Windows, use npm run because bun x doesn't correctly pass arguments
@$(RUNNER) concurrently -k \
"$(RUNNER) nodemon --exec node scripts/build-main-watch.js" \
"$(RUNNER) nodemon --watch dist/main.js --watch dist/main-server.js --delay 500ms --exec \"node dist/main.js server --host $(or $(BACKEND_HOST),localhost) --port $(or $(BACKEND_PORT),3000)\"" \
"$(SHELL) -lc \"CMUX_VITE_HOST=$(or $(VITE_HOST),127.0.0.1) CMUX_VITE_PORT=$(or $(VITE_PORT),5173) VITE_BACKEND_URL=http://$(or $(BACKEND_HOST),localhost):$(or $(BACKEND_PORT),3000) vite\""



start: node_modules/.installed build-main build-preload build-static ## Build and start Electron app
@bun x electron --remote-debugging-port=9222 .
@$(RUNNER) electron --remote-debugging-port=9222 .

## Build targets (can run in parallel)
build: node_modules/.installed src/version.ts build-renderer build-main build-preload build-icons build-static ## Build all targets
Expand All @@ -120,7 +140,7 @@ build-main: node_modules/.installed dist/main.js ## Build main process
dist/main.js: src/version.ts tsconfig.main.json tsconfig.json $(TS_SOURCES)
@echo "Building main process..."
@NODE_ENV=production $(TSGO) -p tsconfig.main.json
@NODE_ENV=production bun x tsc-alias -p tsconfig.main.json
@NODE_ENV=production $(RUNNER) tsc-alias -p tsconfig.main.json

build-preload: node_modules/.installed dist/preload.js ## Build preload script

Expand All @@ -135,7 +155,7 @@ dist/preload.js: src/preload.ts $(TS_SOURCES)

build-renderer: node_modules/.installed src/version.ts ## Build renderer process
@echo "Building renderer..."
@bun x vite build
@$(RUNNER) vite build

build-static: ## Copy static assets to dist
@echo "Copying static assets..."
Expand All @@ -162,16 +182,16 @@ MAGICK_CMD := $(shell command -v magick 2>/dev/null || command -v convert 2>/dev
build/icon.png: docs/img/logo.webp
@echo "Generating Linux icon..."
@mkdir -p build
@$(MAGICK_CMD) docs/img/logo.webp -resize 512x512 build/icon.png
@"$(MAGICK_CMD)" docs/img/logo.webp -resize 512x512 build/icon.png

build/icon.icns: docs/img/logo.webp
@echo "Generating macOS icon..."
@mkdir -p build/icon.iconset
@for size in 16 32 64 128 256 512; do \
$(MAGICK_CMD) docs/img/logo.webp -resize $${size}x$${size} build/icon.iconset/icon_$${size}x$${size}.png; \
"$(MAGICK_CMD)" docs/img/logo.webp -resize $${size}x$${size} build/icon.iconset/icon_$${size}x$${size}.png; \
if [ $$size -le 256 ]; then \
double=$$((size * 2)); \
$(MAGICK_CMD) docs/img/logo.webp -resize $${double}x$${double} build/icon.iconset/icon_$${size}x$${size}@2x.png; \
"$(MAGICK_CMD)" docs/img/logo.webp -resize $${double}x$${double} build/icon.iconset/icon_$${size}x$${size}@2x.png; \
fi; \
done
@iconutil -c icns build/icon.iconset -o build/icon.icns
Expand All @@ -187,15 +207,16 @@ lint-fix: node_modules/.installed ## Run linter with --fix
@./scripts/lint.sh --fix

typecheck: node_modules/.installed src/version.ts ## Run TypeScript type checking (uses tsgo for 10x speedup)
@bun x concurrently -g \
@# On Windows, use npm run because bun x doesn't correctly pass arguments
@$(RUNNER) concurrently -g \
"$(TSGO) --noEmit" \
"$(TSGO) --noEmit -p tsconfig.main.json"

check-deadcode: node_modules/.installed ## Check for potential dead code (manual only, not in static-check)
@echo "Checking for potential dead code with ts-prune..."
@echo "(Note: Some unused exports are legitimate - types, public APIs, entry points, etc.)"
@echo ""
@bun x ts-prune -i '(test|spec|mock|bench|debug|storybook)' \
@$(RUNNER) ts-prune -i '(test|spec|mock|bench|debug|storybook)' \
| grep -v "used in module" \
| grep -v "src/App.tsx.*default" \
| grep -v "src/types/" \
Expand All @@ -205,7 +226,7 @@ check-deadcode: node_modules/.installed ## Check for potential dead code (manual
## Testing
test-integration: node_modules/.installed build-main ## Run all tests (unit + integration)
@bun test src
@TEST_INTEGRATION=1 bun x jest tests
@TEST_INTEGRATION=1 $(RUNNER) jest tests

test-unit: node_modules/.installed build-main ## Run unit tests
@bun test src
Expand All @@ -220,52 +241,52 @@ test-coverage: ## Run tests with coverage

test-e2e: ## Run end-to-end tests
@$(MAKE) build
@CMUX_E2E_LOAD_DIST=1 CMUX_E2E_SKIP_BUILD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun x playwright test --project=electron $(PLAYWRIGHT_ARGS)
@CMUX_E2E_LOAD_DIST=1 CMUX_E2E_SKIP_BUILD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 $(RUNNER) playwright test --project=electron $(PLAYWRIGHT_ARGS)

## Distribution
dist: build ## Build distributable packages
@bun x electron-builder --publish never
@$(RUNNER) electron-builder --publish never

# Parallel macOS builds - notarization happens concurrently
dist-mac: build ## Build macOS distributables (x64 + arm64)
@if [ -n "$$CSC_LINK" ]; then \
echo "🔐 Code signing enabled - building sequentially to avoid keychain conflicts..."; \
bun x electron-builder --mac --x64 --publish never && \
bun x electron-builder --mac --arm64 --publish never; \
$(RUNNER) electron-builder --mac --x64 --publish never && \
$(RUNNER) electron-builder --mac --arm64 --publish never; \
else \
echo "Building macOS architectures in parallel..."; \
bun x electron-builder --mac --x64 --publish never & pid1=$$! ; \
bun x electron-builder --mac --arm64 --publish never & pid2=$$! ; \
$(RUNNER) electron-builder --mac --x64 --publish never & pid1=$$! ; \
$(RUNNER) electron-builder --mac --arm64 --publish never & pid2=$$! ; \
wait $$pid1 && wait $$pid2; \
fi
@echo "✅ Both architectures built successfully"

dist-mac-release: build ## Build and publish macOS distributables (x64 + arm64)
@if [ -n "$$CSC_LINK" ]; then \
echo "🔐 Code signing enabled - building sequentially to avoid keychain conflicts..."; \
bun x electron-builder --mac --x64 --publish always && \
bun x electron-builder --mac --arm64 --publish always; \
$(RUNNER) electron-builder --mac --x64 --publish always && \
$(RUNNER) electron-builder --mac --arm64 --publish always; \
else \
echo "Building and publishing macOS architectures in parallel..."; \
bun x electron-builder --mac --x64 --publish always & pid1=$$! ; \
bun x electron-builder --mac --arm64 --publish always & pid2=$$! ; \
$(RUNNER) electron-builder --mac --x64 --publish always & pid1=$$! ; \
$(RUNNER) electron-builder --mac --arm64 --publish always & pid2=$$! ; \
wait $$pid1 && wait $$pid2; \
fi
@echo "✅ Both architectures built and published successfully"

dist-mac-x64: build ## Build macOS x64 distributable only
@echo "Building macOS x64..."
@bun x electron-builder --mac --x64 --publish never
@$(RUNNER) electron-builder --mac --x64 --publish never

dist-mac-arm64: build ## Build macOS arm64 distributable only
@echo "Building macOS arm64..."
@bun x electron-builder --mac --arm64 --publish never
@$(RUNNER) electron-builder --mac --arm64 --publish never

dist-win: build ## Build Windows distributable
@bun x electron-builder --win --publish never
@$(RUNNER) electron-builder --win --publish never

dist-linux: build ## Build Linux distributable
@bun x electron-builder --linux --publish never
@$(RUNNER) electron-builder --linux --publish never

## Documentation
docs: ## Serve documentation locally
Expand All @@ -280,19 +301,19 @@ docs-watch: ## Watch and rebuild documentation
## Storybook
storybook: node_modules/.installed ## Start Storybook development server
$(check_node_version)
@bun x storybook dev -p 6006 $(STORYBOOK_OPEN_FLAG)
@$(RUNNER) storybook dev -p 6006 $(STORYBOOK_OPEN_FLAG)

storybook-build: node_modules/.installed src/version.ts ## Build static Storybook
$(check_node_version)
@bun x storybook build
@$(RUNNER) storybook build

test-storybook: node_modules/.installed ## Run Storybook interaction tests (requires Storybook to be running or built)
$(check_node_version)
@bun x test-storybook
@$(RUNNER) test-storybook

chromatic: node_modules/.installed ## Run Chromatic for visual regression testing
$(check_node_version)
@bun x chromatic --exit-zero-on-changes
@$(RUNNER) chromatic --exit-zero-on-changes

## Benchmarks
benchmark-terminal: ## Run Terminal-Bench with the cmux agent (use TB_DATASET/TB_SAMPLE_SIZE/TB_TIMEOUT/TB_ARGS to customize)
Expand Down
1 change: 1 addition & 0 deletions bun.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "mux",
Expand Down
Loading