From 676026a6c34ca3241f8dde8998d2722b26e20439 Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 30 Apr 2026 16:37:10 -0700 Subject: [PATCH] feat(cli): default claude/opencode to sandbox mount with git included Bumps @relayfile/local-mount to ^0.6.0 and passes includeGit: true so the per-session mount carries .git, letting agents run git inside the sandbox. Tracked paths matching the hidden patterns (CLEAN_IGNORED_*, SKILL_INSTALL_IGNORED_*, persona configFile paths) are flagged skip-worktree and added to .git/info/exclude inside the mount, so git status doesn't report them as deleted/untracked. The mount's .git is per-session and noSyncBack, so this state is sandboxed. Flips the claude branch in decideCleanMode to mirror opencode: mount engages by default, --install-in-repo opts out. Removes the legacy --clean flag entirely (mount is now the default behavior on both harnesses; codex is unchanged). Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 44 ++++--- packages/cli/README.md | 65 ++++++---- packages/cli/package.json | 2 +- packages/cli/src/cli.test.ts | 190 +++++++++++++++++---------- packages/cli/src/cli.ts | 240 +++++++++++++++++++++-------------- pnpm-lock.yaml | 10 +- 6 files changed, 331 insertions(+), 220 deletions(-) diff --git a/README.md b/README.md index 5d9ec546..8f3d0c65 100644 --- a/README.md +++ b/README.md @@ -147,13 +147,16 @@ layer for reusing installs across sessions is planned but not yet wired up. See **[packages/cli/README.md#skill-staging](./packages/cli/README.md#skill-staging)** for the full mechanics. -### Clean mode (`--clean`) +### Sandbox mount (default for claude / opencode) -For interactive `claude` sessions, `--clean` launches the harness inside a +Interactive `claude` and `opencode` sessions launch inside a [`@relayfile/local-mount`](https://www.npmjs.com/package/@relayfile/local-mount) -sandbox that hides repo-level Claude Code configuration from the model: +sandbox by default. The mount hides repo-level harness configuration +(claude) and routes skill-install writes into the sandbox (opencode), so +the model sees persona context + user-level context — and nothing the +repo itself declares. Codex sessions never mount. -| Hidden in `--clean` mode | Still visible | +| Hidden in the mount (claude) | Still visible | | --- | --- | | `CLAUDE.md` (at any depth) | `~/.claude/CLAUDE.md` (user-level) | | `CLAUDE.local.md` | `~/.claude/skills/` (user-level skills) | @@ -162,23 +165,26 @@ sandbox that hides repo-level Claude Code configuration from the model: | | your keychain auth (unchanged) | ```bash -agent-workforce agent --clean posthog@best +agent-workforce agent posthog@best ``` -The repo tree is mirrored into -`~/.agent-workforce/sessions//mount/` via symlinks; claude sees the -mount as its cwd. Writes inside the mount sync back to the real repo on -exit. Ignore semantics follow gitignore — `.claude` hides nested variants -like `packages/foo/.claude/` too. - -**Scope:** interactive claude only. Pass it to codex/opencode and the flag -is ignored with a note. `--clean` and `--install-in-repo` are mutually -exclusive — they ask for opposite things. - -**Caveat:** user-level Claude Code config in `~/.claude/` still loads -inside the session. `--clean` hides the *repo's* context, not the user's. -If you need to hide user-level config too, launch under a scratch -`$HOME`. See **[packages/cli/README.md#clean-mode](./packages/cli/README.md#clean-mode)** +The repo tree is mirrored into `~/.agent-workforce/sessions//mount/` +via symlinks; the harness sees the mount as its cwd. Writes inside the +mount sync back to the real repo on exit. Ignore semantics follow +gitignore — `.claude` hides nested variants like `packages/foo/.claude/` +too. `.git` is included in the mount (one-way project→mount sync) so git +commands work inside the sandbox; mount-side commits are discarded on +cleanup, so push to persist work. + +**Opt out:** `--install-in-repo` runs against the real cwd and stages +skills into the repo's harness-conventional dirs. Useful when you want to +inspect installed skills on disk or when the mount conflicts with +something else (network filesystem, etc.). + +**Caveat:** user-level harness config in `~/.claude/` etc. still loads +inside the session — the mount hides the *repo's* context, not the +user's. If you need to hide user-level config too, launch under a scratch +`$HOME`. See **[packages/cli/README.md#sandbox-mount](./packages/cli/README.md#sandbox-mount)** for the full mount layout and semantics. ## Packages diff --git a/packages/cli/README.md b/packages/cli/README.md index e3dd9120..ea990989 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -392,11 +392,11 @@ persona session, add it to the persona's `mcpServers` block. ## Interactive ```sh -agent-workforce agent [--install-in-repo] [--clean] [@] +agent-workforce agent [--install-in-repo] [@] ``` -`--install-in-repo` and `--clean` are mutually exclusive — see -[**Clean mode**](#clean-mode) below. +By default, claude and opencode sessions run inside a sandbox mount — see +[**Sandbox mount**](#sandbox-mount) below. `--install-in-repo` opts out. 1. Resolves the persona, walks the cascade, resolves `$VAR` refs. 2. **Stages skills outside the repo by default** (claude interactive only — @@ -483,20 +483,21 @@ stage dir conflicts with something else (network filesystem, read-only into a new stage dir. A `~/.agent-workforce/cache/` content-addressed cache is planned but not wired up. -## Clean mode +## Sandbox mount -`--clean` launches an interactive claude session inside a +By default, claude and opencode interactive sessions run inside a [`@relayfile/local-mount`](https://www.npmjs.com/package/@relayfile/local-mount) -symlink mount that hides the repo's Claude Code configuration from the -session — so the model sees persona context + user-level context, and -nothing the repo itself declares. +symlink mount that hides repo-level harness configuration from the session +and routes skill-install writes into the sandbox — so the model sees +persona context + user-level context, and nothing the repo itself declares. +Codex sessions never mount (no harness-side support). -```sh -agent-workforce agent --clean [@] -``` +`--install-in-repo` opts out and runs against the real cwd. **What's hidden (gitignore semantics, at any depth):** +For claude: + | Pattern | Rationale | | --- | --- | | `CLAUDE.md` | Repo-level project memory | @@ -504,18 +505,35 @@ agent-workforce agent --clean [@] | `.claude` | Repo Claude Code config dir (settings, agents, skills, commands) | | `.mcp.json` | Repo-declared MCP servers | +For opencode (skill-install pollution that would otherwise leak back to +the repo): + +| Pattern | Rationale | +| --- | --- | +| `.agents`, `.claude/skills`, `.factory/skills`, `.kiro/skills`, `skills` | skill.sh universal install root + per-harness symlink farms | +| `.opencode`, `.skills` | prpm `--as ` output roots | +| `prpm.lock`, `skills-lock.json` | provider lockfiles | + **What's preserved:** - **User-level context** under `~/.claude/` — `CLAUDE.md`, skills, etc. - still load. `--clean` scrubs the *project*, not the user. To exclude + still load. The mount scrubs the *project*, not the user. To exclude user-level context too, launch under a scratch `$HOME`. -- **Persona skills.** The `--plugin-dir` passed to claude resolves to an - absolute path *outside* the mount, so staged skills from - `~/.agent-workforce/sessions//claude/plugin/` load normally. -- **Keychain auth.** `--clean` does NOT pass `--bare`; it only hides - files via the mount. Claude Code's macOS keychain login stays active. +- **Persona skills.** For claude, the `--plugin-dir` passed to the harness + resolves to an absolute path *outside* the mount, so staged skills from + `~/.agent-workforce/sessions//claude/plugin/` load normally. For + opencode, the install runs inside the mount so the writes land in the + sandbox. +- **Keychain auth.** The mount does not pass `--bare`; it only hides + files. Claude Code's macOS keychain login stays active. - **Persona `mcpServers`.** Still passed via `--mcp-config` — unaffected by the mount. The repo's `.mcp.json` is hidden regardless. +- **Git.** `.git` is included in the mount (one-way project→mount sync per + `@relayfile/local-mount` 0.6+'s `includeGit`). Tracked paths matching + the hidden patterns are flagged `skip-worktree` so `git status` doesn't + report them as deleted, and the patterns are added to `.git/info/exclude` + to suppress untracked-and-hidden files. Mount-side commits/refs are + sandboxed and discarded on cleanup — `git push` to persist work. ### Session layout @@ -531,7 +549,7 @@ is generated once and both paths are derived from it: │ ├── .claude-plugin/plugin.json │ ├── skills → .claude/skills │ └── .claude/skills//SKILL.md - └── mount/ ← --clean: claude's cwd + └── mount/ ← session cwd └──