Skip to content

Commit 2d1ee47

Browse files
committed
Add shims to readme
1 parent b3c13b0 commit 2d1ee47

2 files changed

Lines changed: 62 additions & 19 deletions

File tree

.claude/plans/mount-claude-config.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
## Context
44

5-
Clave sessions lose Claude Code state (auto-memory, history, slash commands, skills, agents, rules) because `SetupClaudeCode` only copies a curated subset of config files. This makes features like long-term memory, `--continue`/`--resume`, and personal commands unavailable in VM sessions. The fix is to mount the host's `~/.claude/` directory directly into the VM via VirtioFS, sync `~/.claude.json` round-trip, and handle the macOS-vs-Ubuntu instruction mismatch with an injectable preamble.
5+
Clave sessions lose Claude Code state (auto-memory, history, slash commands, skills, agents, rules) because `SetupClaudeCode` only copies a curated subset of config files. This makes features like
6+
long-term memory, `--continue`/`--resume`, and personal commands unavailable in VM sessions. The fix is to mount the host's `~/.claude/` directory directly into the VM via VirtioFS, sync
7+
`~/.claude.json` round-trip, and handle the macOS-vs-Ubuntu instruction mismatch with an injectable preamble.
68

7-
**Why paths match:** Clave already bind-mounts the project at its original absolute macOS path inside the VM (e.g., `/Users/inxilpro/Development/laravel-zero/clave`). Claude Code's `~/.claude/projects/` directories encode that path (e.g., `-Users-inxilpro-Development-laravel-zero-clave`), so project memory resolves correctly. The `~/.claude.json` projects map keys also match.
9+
**Why paths match:** Clave already bind-mounts the project at its original absolute macOS path inside the VM (e.g., `/Users/inxilpro/Development/laravel-zero/clave`). Claude Code's
10+
`~/.claude/projects/` directories encode that path (e.g., `-Users-inxilpro-Development-laravel-zero-clave`), so project memory resolves correctly. The `~/.claude.json` projects map keys also match.
811

912
**What doesn't match:** Global `~/.claude/CLAUDE.md` may contain macOS-specific instructions (e.g., Herd PHP paths). Solution: inject a default preamble, with override support via `.clave.json`.
1013

@@ -17,6 +20,7 @@ Clave sessions lose Claude Code state (auto-memory, history, slash commands, ski
1720
**`app/Support/TartManager.php`** — Update `runBackground()` to support named directory mounts:
1821

1922
Currently passes `--dir={path}` for each dir. Change to support `--dir=name:path` syntax. Pass two mounts:
23+
2024
- `--dir=project:{project_path}`
2125
- `--dir=claude-home:{home}/.claude`
2226

@@ -25,6 +29,7 @@ The `dirs` parameter should change from `array $dirs` (list of paths) to `array
2529
**`app/Pipelines/Steps/BootVm.php`** — Update `mountSharedDirectories()`:
2630

2731
After mounting the project share (existing), mount the claude config share:
32+
2833
```bash
2934
sudo mkdir -p /home/admin/.claude
3035
sudo mount -t virtiofs claude-home /home/admin/.claude
@@ -33,6 +38,7 @@ sudo mount -t virtiofs claude-home /home/admin/.claude
3338
The `cleanupResumedVm()` method should also unmount `~/.claude` on resume before remounting.
3439

3540
Update the `--dir` call in `handle()` to pass both directories:
41+
3642
```php
3743
$mount_path = $context->clone_path ?? $context->project_dir;
3844
$dirs = [
@@ -45,24 +51,29 @@ $this->tart->runBackground($context->vm_name, $dirs);
4551
### 2. Simplify `SetupClaudeCode` (most config is now mounted)
4652

4753
**`app/Pipelines/Steps/SetupClaudeCode.php`** — Remove manual writing of:
54+
4855
- `~/.claude/settings.json` (now mounted)
4956
- `~/.claude/CLAUDE.md` (now mounted, with preamble — see step 4)
5057

5158
**Keep writing:**
59+
5260
- `~/.claude.json` — still a file (not a directory), can't be VirtioFS mounted. Continue writing curated version at session start.
5361
- `~/.claude/.credentials.json` — needs VM-specific auth values. Write OVER the mounted file (VirtioFS is read-write).
5462
- `~/.claude/ide/{port}.lock` — needs VM-specific IDE integration values.
5563

5664
### 3. Round-trip sync `~/.claude.json`
5765

58-
**At session start** (`SetupClaudeCode`): Write curated `~/.claude.json` into VM as today, but include more fields from the host version (the full `projects` map, `githubRepoPaths`, etc.) since we now want transparency.
66+
**At session start** (`SetupClaudeCode`): Write curated `~/.claude.json` into VM as today, but include more fields from the host version (the full `projects` map, `githubRepoPaths`, etc.) since we now
67+
want transparency.
5968

6069
**At session end** (`SessionTeardown`): Read `~/.claude.json` from the VM via SSH, diff relevant fields, and merge changes back to the host file. Relevant fields to sync back:
70+
6171
- `projects.{current_project}.allowedTools` — user may have approved new tools
6272
- `theme` — user may have changed theme
6373
- Other user-facing preference changes
6474

6575
Implementation in `SessionTeardown`:
76+
6677
```php
6778
// Read VM's ~/.claude.json
6879
$vm_config = json_decode($this->ssh->run('cat ~/.claude.json')->output(), true);
@@ -83,6 +94,7 @@ Since `~/.claude/` is mounted read-write via VirtioFS, we **cannot** modify `~/.
8394
This shadows the VirtioFS file without modifying the host. Automatically cleaned up when the VM stops.
8495

8596
**Default preamble:**
97+
8698
```
8799
# Clave VM Environment
88100
You are running inside an Ubuntu VM managed by Clave. Ignore any macOS-specific
@@ -91,6 +103,7 @@ PHP is available as `php`. Node.js is available as `node`/`npm`.
91103
```
92104

93105
**`.clave.json` config for custom preamble:**
106+
94107
```json
95108
{
96109
"claude_md_preamble": "Use php83 instead of php. Redis is on port 6380."
@@ -101,19 +114,20 @@ If set, replaces the default preamble. If set to `false` or empty string, no pre
101114

102115
### 5. Concurrent sessions
103116

104-
Since `~/.claude/` is mounted from the host into potentially multiple VMs, concurrent sessions share the same memory and history files. This is the same as running multiple Claude Code sessions locally — Claude Code already handles this. No special handling needed.
117+
Since `~/.claude/` is mounted from the host into potentially multiple VMs, concurrent sessions share the same memory and history files. This is the same as running multiple Claude Code sessions
118+
locally — Claude Code already handles this. No special handling needed.
105119

106120
---
107121

108122
## Files to modify
109123

110-
| File | Changes |
111-
|------|---------|
112-
| `app/Support/TartManager.php` | `runBackground()`: change `$dirs` from indexed to associative, generate `--dir=name:path` flags |
113-
| `app/Pipelines/Steps/BootVm.php` | `handle()`: pass both project and claude-home dirs. `mountSharedDirectories()`: mount both VirtioFS shares + bind-mount CLAUDE.md override. `cleanupResumedVm()`: handle claude mount cleanup |
114-
| `app/Pipelines/Steps/SetupClaudeCode.php` | Remove settings.json/CLAUDE.md writing. Keep ~/.claude.json and credentials writing. Add CLAUDE.md preamble logic (read mounted file, prepend preamble, write to temp, bind-mount) |
115-
| `app/Support/SessionTeardown.php` | Add `~/.claude.json` sync-back logic |
116-
| `app/Data/ProjectConfig.php` | Add `claude_md_preamble` field |
124+
| File | Changes |
125+
|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
126+
| `app/Support/TartManager.php` | `runBackground()`: change `$dirs` from indexed to associative, generate `--dir=name:path` flags |
127+
| `app/Pipelines/Steps/BootVm.php` | `handle()`: pass both project and claude-home dirs. `mountSharedDirectories()`: mount both VirtioFS shares + bind-mount CLAUDE.md override. `cleanupResumedVm()`: handle claude mount cleanup |
128+
| `app/Pipelines/Steps/SetupClaudeCode.php` | Remove settings.json/CLAUDE.md writing. Keep ~/.claude.json and credentials writing. Add CLAUDE.md preamble logic (read mounted file, prepend preamble, write to temp, bind-mount) |
129+
| `app/Support/SessionTeardown.php` | Add `~/.claude.json` sync-back logic |
130+
| `app/Data/ProjectConfig.php` | Add `claude_md_preamble` field |
117131

118132
---
119133

@@ -133,26 +147,30 @@ Since `~/.claude/` is mounted from the host into potentially multiple VMs, concu
133147
## Research notes
134148

135149
### Claude Code local file structure
150+
136151
- `~/.claude/projects/{encoded-path}/memory/MEMORY.md` — auto-memory (first 200 lines loaded at session start)
137152
- `~/.claude/projects/{encoded-path}/{session-uuid}.jsonl` — session transcripts
138153
- Path encoding: every `/` in the absolute project path becomes `-`
139154
- Project path derived from git repo root
140155
- `~/.claude.json` `projects` map keys are absolute paths (used as lookup identifiers)
141156

142157
### Tart VirtioFS capabilities
158+
143159
- Multiple `--dir` flags supported: `--dir=name1:path1 --dir=name2:path2`
144160
- Each name becomes a unique VirtioFS mount tag
145161
- Linux guests require manual `mount -t virtiofs` (macOS guests auto-mount at `/Volumes/My Shared Files/`)
146162
- Cannot add/remove mounts to a running VM
147163
- ~3x slower than native filesystem (acceptable for config files)
148164

149165
### Why not macOS VM
166+
150167
- Hard 2-VM limit enforced by Apple's Virtualization.Framework — kills multi-session support
151168
- 15-30GB+ images vs ~2GB for Ubuntu
152169
- Slower boot, more complex provisioning
153170
- The path problem is already solved by bind-mounting at the original path
154171

155172
### Why not persistent VM (for now)
173+
156174
- Can't dynamically add VirtioFS mounts to a running VM
157175
- Would require pre-mounting a broad directory (e.g., ~/Development)
158176
- Loses clean-room isolation

README.md

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ cd /path/to/your/project
3939
clave --isolate
4040
```
4141

42-
Before spinning up a fresh VM, clave will create a local git clone of your working directory. (This is similarly efficient as a
42+
Before spinning up a fresh VM, clave will create a local git clone of your working directory. (This is similarly efficient as a
4343
worktree, but has a copy of your full git history.) Clave will then mount that clone into the VM, keeping any changes made inside
4444
the VM completely isolated from your project until you quit. Once you quit, you will be prompted to decide what to do with the
4545
changes before the clone is cleaned up.
@@ -68,16 +68,41 @@ Add a `.clave.json` file to your project root to customize VM setup for your pro
6868

6969
### Options
7070

71-
| Key | Type | Description |
72-
|--------------|----------|---------------------------------------------------------------------------|
73-
| `base_image` | string | Custom Tart VM base image. Defaults to `ghcr.io/cirruslabs/ubuntu:latest` |
74-
| `cpus` | integer | CPUs allocated to the VM. Defaults to `CLAVE_VM_CPUS` (4) |
75-
| `memory` | integer | Memory (MB) allocated to the VM. Defaults to `CLAVE_VM_MEMORY` (8192) |
76-
| `provision` | string[] | Bash commands to run during VM provisioning |
77-
| `env` | string[] | Environment variable names to pass through from your host into the VM |
71+
| Key | Type | Description |
72+
|--------------|----------|--------------------------------------------------------------------------------|
73+
| `base_image` | string | Custom Tart VM base image. Defaults to `ghcr.io/cirruslabs/ubuntu:latest` |
74+
| `cpus` | integer | CPUs allocated to the VM. Defaults to `CLAVE_VM_CPUS` (4) |
75+
| `memory` | integer | Memory (MB) allocated to the VM. Defaults to `CLAVE_VM_MEMORY` (8192) |
76+
| `provision` | string[] | Bash commands to run during VM provisioning |
77+
| `env` | string[] | Environment variable names to pass through from your host into the VM |
78+
| `shims` | string[] | Host commands to proxy into the VM (see [Host Proxy Shims](#host-proxy-shims)) |
7879

7980
See the [`tart` documentation](https://tart.run/quick-start/#vm-images) for a list of available base images.
8081

82+
### Host Proxy Shims
83+
84+
Shims let Claude Code running inside the VM transparently execute specific commands on your Mac host. This is useful for tools that
85+
only exist on macOS or GUI-based CLIs like `playwright-cli`.
86+
87+
When a shim is invoked inside the VM, it sends the command over a Unix socket tunnel to a proxy daemon running on the host, which
88+
executes it and returns the output. From Claude's perspective, it's a normal command.
89+
90+
**Shims are opt in and disabled by default.**
91+
92+
Enable shims in your `.clave.json`:
93+
94+
```json
95+
{
96+
...
97+
"shims": [
98+
"playwright-cli"
99+
]
100+
}
101+
```
102+
103+
> **Note:** Adding shims for the first time requires reprovisioning the base VM. The shim symlinks themselves
104+
> are created fresh each session, so changes to the `shims` list take effect next run without reprovisioning.
105+
81106
### Environment Variables Passed Through Automatically
82107

83108
Clave always forwards these environment variables into the VM when present on the host:

0 commit comments

Comments
 (0)