Skip to content

Commit 7087ef4

Browse files
🤖 feat: add Local/Worktree runtime distinction (#824)
## Summary Adds explicit runtime type distinction between Local (project-directory) and Worktree (isolated git worktrees): - **Local runtime**: Uses project directory directly, no isolation. Creation is a no-op (just "use this directory"), delete is a no-op (won't delete user's project). Good for existing repos where user wants to work in-place. - **Worktree runtime**: Creates isolated git worktrees under `~/.mux/src/<project>/<branch>`. Existing behavior, now made explicit. - **SSH runtime**: Unchanged, remote execution. ## Changes - `RuntimeMode` now includes `"local" | "worktree" | "ssh"` - Extracted `LocalBaseRuntime` for shared exec/file operations between Local and Worktree - `LocalRuntime` uses project path directly (no-op create/delete) - `WorktreeRuntime` manages git worktrees (renamed from old LocalRuntime) - UI shows Local/Worktree/SSH options in creation controls - Updated VS Code extension to handle all runtime types with appropriate icons - Runtime badges: SSH (server icon), Worktree (git branch icon), Local (no badge) ## Backward Compatibility - `type: "local"` with `srcBaseDir` = legacy worktree config (still works) - `type: "local"` without `srcBaseDir` = new project-dir runtime - `type: "worktree"` = explicit worktree (new workspaces) Users can upgrade/downgrade without breaking existing workspaces. _Generated with `mux`_ --------- Co-authored-by: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
1 parent 894a0d4 commit 7087ef4

36 files changed

+1797
-848
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ Here are some specific use cases we enable:
3333

3434
## Features
3535

36-
- **Isolated workspaces** with central view on git divergence
37-
- **Local**: git worktrees on your local machine ([docs](https://cmux.io/local.html))
38-
- **SSH**: regular git clones on a remote server ([docs](https://cmux.io/ssh.html))
36+
- **Isolated workspaces** with central view on git divergence ([docs](https://cmux.io/runtime.html))
37+
- **[Local](https://cmux.io/runtime/local.html)**: run directly in your project directory
38+
- **[Worktree](https://cmux.io/runtime/worktree.html)**: git worktrees on your local machine
39+
- **[SSH](https://cmux.io/runtime/ssh.html)**: remote execution on a server over SSH
3940
- **Multi-model** (`sonnet-4-*`, `grok-*`, `gpt-5-*`, `opus-4-*`)
4041
- Ollama supported for local LLMs ([docs](https://cmux.io/models.html#ollama-local))
4142
- OpenRouter supported for long-tail of LLMs ([docs](https://cmux.io/models.html#openrouter-cloud))

docs/SUMMARY.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
# Features
1010

1111
- [Workspaces](./workspaces.md)
12-
- [Local](./local.md)
13-
- [SSH](./ssh.md)
12+
- [Runtimes](./runtime.md)
13+
- [Local](./runtime/local.md)
14+
- [Worktree](./runtime/worktree.md)
15+
- [SSH](./runtime/ssh.md)
1416
- [Forking](./fork.md)
1517
- [Init Hooks](./init-hooks.md)
1618
- [VS Code Extension](./vscode-extension.md)

docs/runtime.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Runtimes
2+
3+
Runtimes determine where and how mux executes agent workspaces.
4+
5+
| Runtime | Isolation | Best For |
6+
| ------------------------------------- | ------------------------------------------ | ---------------------------------------- |
7+
| **[Local](./runtime/local.md)** | All workspaces share the project directory | Quick edits to your working copy |
8+
| **[Worktree](./runtime/worktree.md)** | Each workspace gets its own directory | Running multiple agents in parallel |
9+
| **[SSH](./runtime/ssh.md)** | Remote execution over SSH | Security, performance, heavy parallelism |
10+
11+
## Choosing a Runtime
12+
13+
When creating a workspace, select the runtime from the dropdown in the workspace creation UI.
14+
15+
## Init Hooks
16+
17+
[Init hooks](./init-hooks.md) can detect the runtime type via the `MUX_RUNTIME` environment variable:
18+
19+
- `local` — Local runtime
20+
- `worktree` — Worktree runtime
21+
- `ssh` — SSH runtime
22+
23+
This lets your init hook adapt behavior, e.g., skip worktree-specific setup when running in local mode.

docs/runtime/local.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Local Runtime
2+
3+
Local runtime runs the agent directly in your project directory—the same directory you use for development. There's no worktree isolation; the agent works in your actual working copy.
4+
5+
## When to Use
6+
7+
- Quick one-off tasks in your current working copy
8+
- Reviewing agent work alongside your own uncommitted changes
9+
- Projects where worktrees don't work well (e.g., some monorepos)
10+
11+
## Caveats
12+
13+
⚠️ **No isolation**: Multiple local workspaces for the same project see and modify the same files. Running them simultaneously can cause conflicts. mux shows a warning when another local workspace is actively streaming.
14+
15+
⚠️ **Affects your working copy**: Agent changes happen in your actual project directory.
16+
17+
## Filesystem
18+
19+
The workspace path is your project directory itself. No additional directories are created.

docs/ssh.md renamed to docs/runtime/ssh.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
# SSH Workspaces
1+
# SSH Runtime
22

33
mux supports using remote hosts over SSH for workspaces. When configured, all tool operations will
44
execute over SSH and the agent is securely isolated from your local machine.
55

6-
Our security architecture considers the remote machine potentially hostile. No keys or credentials are implicitly transferred there—just the git archive and [Project Secrets](./project-secrets.md).
6+
Our security architecture considers the remote machine potentially hostile. No keys or credentials are implicitly transferred there—just the git archive and [Project Secrets](../project-secrets.md).
77

88
We highly recommend using SSH workspaces for an optimal experience:
99

1010
- **Security**: Prompt injection risk is contained to the credentials / files on the remote machine.
11-
- SSH remotes pair nicely with [agentic git identities](./agentic-git-identity.md)
11+
- SSH remotes pair nicely with [agentic git identities](../agentic-git-identity.md)
1212
- **Performance**: Run many, many agents in parallel while maintaining good battery life and UI performance
1313

14-
![ssh workspaces](./img/new-workspace-ssh.webp)
14+
![ssh workspaces](../img/new-workspace-ssh.webp)
1515

1616
The Host can be:
1717

docs/local.md renamed to docs/runtime/worktree.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Local Workspaces
1+
# Worktree Runtime
22

3-
Local workspaces use [git worktrees](https://git-scm.com/docs/git-worktree) on your local machine. Worktrees share the `.git` directory with your main repository while maintaining independent working changes and checkout state.
3+
Worktree runtime uses [git worktrees](https://git-scm.com/docs/git-worktree) on your local machine. Worktrees share the `.git` directory with your main repository while maintaining independent working changes and checkout state.
44

55
## How Worktrees Work
66

@@ -10,7 +10,7 @@ It's important to note that a **worktree is not locked to a branch**. The agent
1010

1111
## Filesystem Layout
1212

13-
Local workspaces are stored in `~/.mux/src/<project-name>/<workspace-name>`.
13+
Worktree workspaces are stored in `~/.mux/src/<project-name>/<workspace-name>`.
1414

1515
Example layout:
1616

docs/vscode-extension.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,5 @@ repository.
8585
## Related
8686

8787
- [Workspaces Overview](./workspaces.md)
88-
- [SSH Workspaces](./ssh.md)
88+
- [SSH Runtime](./runtime/ssh.md)
8989
- [VS Code Remote-SSH Documentation](https://code.visualstudio.com/docs/remote/ssh)

docs/workspaces.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22

33
Workspaces in mux provide isolated development environments for parallel agent work. Each workspace maintains its own Git state, allowing you to explore different approaches, run multiple tasks simultaneously, or test changes without affecting your main repository.
44

5-
## Workspace Types
5+
## Runtimes
66

7-
mux supports two workspace backends:
7+
mux supports three [runtime types](./runtime.md):
88

9-
- **[Local Workspaces](./local.md)**: Use [git worktrees](https://git-scm.com/docs/git-worktree) on your local machine. Worktrees share the `.git` directory with your main repository while maintaining independent working changes.
9+
- **[Local](./runtime/local.md)**: Run directly in your project directory. No isolation—best for quick edits to your working copy.
1010

11-
- **[SSH Workspaces](./ssh.md)**: Regular git clones on a remote server accessed via SSH. These are completely independent repositories stored on the remote machine.
11+
- **[Worktree](./runtime/worktree.md)**: Isolated directories using [git worktrees](https://git-scm.com/docs/git-worktree). Worktrees share `.git` with your main repository while maintaining independent working changes.
1212

13-
## Choosing a Backend
13+
- **[SSH](./runtime/ssh.md)**: Remote execution over SSH. Ideal for heavy workloads, security isolation, or leveraging remote infrastructure.
1414

15-
The workspace backend is selected when you create a workspace:
15+
## Choosing a Runtime
1616

17-
- **Local**: Best for fast iteration, local testing, and when you want to leverage your local machine's resources
18-
- **SSH**: Ideal for heavy workloads, long-running tasks, or when you need access to remote infrastructure
17+
The runtime is selected when you create a workspace:
18+
19+
- **Local**: Quick tasks in your current working copy
20+
- **Worktree**: Best for parallel agent work with isolation
21+
- **SSH**: Heavy workloads, security, or remote infrastructure
1922

2023
## Key Concepts
2124

src/browser/App.tsx

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -559,26 +559,31 @@ function AppInner() {
559559
<div className="mobile-main-content flex min-w-0 flex-1 flex-col overflow-hidden">
560560
<div className="mobile-layout flex flex-1 overflow-hidden">
561561
{selectedWorkspace ? (
562-
<ErrorBoundary
563-
workspaceInfo={`${selectedWorkspace.projectName}/${selectedWorkspace.namedWorkspacePath?.split("/").pop() ?? selectedWorkspace.workspaceId}`}
564-
>
565-
<AIView
566-
key={selectedWorkspace.workspaceId}
567-
workspaceId={selectedWorkspace.workspaceId}
568-
projectName={selectedWorkspace.projectName}
569-
branch={
570-
selectedWorkspace.namedWorkspacePath?.split("/").pop() ??
571-
selectedWorkspace.workspaceId
572-
}
573-
namedWorkspacePath={selectedWorkspace.namedWorkspacePath ?? ""}
574-
runtimeConfig={
575-
workspaceMetadata.get(selectedWorkspace.workspaceId)?.runtimeConfig
576-
}
577-
incompatibleRuntime={
578-
workspaceMetadata.get(selectedWorkspace.workspaceId)?.incompatibleRuntime
579-
}
580-
/>
581-
</ErrorBoundary>
562+
(() => {
563+
const currentMetadata = workspaceMetadata.get(selectedWorkspace.workspaceId);
564+
// Use metadata.name for workspace name (works for both worktree and local runtimes)
565+
// Fallback to path-based derivation for legacy compatibility
566+
const workspaceName =
567+
currentMetadata?.name ??
568+
selectedWorkspace.namedWorkspacePath?.split("/").pop() ??
569+
selectedWorkspace.workspaceId;
570+
return (
571+
<ErrorBoundary
572+
workspaceInfo={`${selectedWorkspace.projectName}/${workspaceName}`}
573+
>
574+
<AIView
575+
key={selectedWorkspace.workspaceId}
576+
workspaceId={selectedWorkspace.workspaceId}
577+
projectPath={selectedWorkspace.projectPath}
578+
projectName={selectedWorkspace.projectName}
579+
branch={workspaceName}
580+
namedWorkspacePath={selectedWorkspace.namedWorkspacePath ?? ""}
581+
runtimeConfig={currentMetadata?.runtimeConfig}
582+
incompatibleRuntime={currentMetadata?.incompatibleRuntime}
583+
/>
584+
</ErrorBoundary>
585+
);
586+
})()
582587
) : creationProjectPath ? (
583588
(() => {
584589
const projectPath = creationProjectPath;

src/browser/components/AIView.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { useAIViewKeybinds } from "@/browser/hooks/useAIViewKeybinds";
3636
import { evictModelFromLRU } from "@/browser/hooks/useModelLRU";
3737
import { QueuedMessage } from "./Messages/QueuedMessage";
3838
import { CompactionWarning } from "./CompactionWarning";
39+
import { ConcurrentLocalWarning } from "./ConcurrentLocalWarning";
3940
import { checkAutoCompaction } from "@/browser/utils/compaction/autoCompactionCheck";
4041
import { executeCompaction } from "@/browser/utils/chatCommands";
4142
import { useProviderOptions } from "@/browser/hooks/useProviderOptions";
@@ -44,6 +45,7 @@ import { useSendMessageOptions } from "@/browser/hooks/useSendMessageOptions";
4445

4546
interface AIViewProps {
4647
workspaceId: string;
48+
projectPath: string;
4749
projectName: string;
4850
branch: string;
4951
namedWorkspacePath: string; // User-friendly path for display and terminal
@@ -55,6 +57,7 @@ interface AIViewProps {
5557

5658
const AIViewInner: React.FC<AIViewProps> = ({
5759
workspaceId,
60+
projectPath,
5861
projectName,
5962
branch,
6063
namedWorkspacePath,
@@ -561,6 +564,11 @@ const AIViewInner: React.FC<AIViewProps> = ({
561564
onEdit={() => void handleEditQueuedMessage()}
562565
/>
563566
)}
567+
<ConcurrentLocalWarning
568+
workspaceId={workspaceId}
569+
projectPath={projectPath}
570+
runtimeConfig={runtimeConfig}
571+
/>
564572
</div>
565573
</div>
566574
{!autoScroll && (

0 commit comments

Comments
 (0)