Add Muse Code as a first-class agent - #238
Merged
Merged
Conversation
Closes webcoyote#236. - guest/home/bin/muse wrapper: Homebrew prefix resolution by default, the upstream shell installer under --native-install. Muse is not an npm package; its installer drops a self-updating launcher into MUSE_INSTALL_DIR (default ~/.local/bin) which then downloads the real binary beside itself. MUSE_NO_MODIFY_PATH=1 stops it appending "export PATH=..." to .zshrc/.zprofile, which sandvault owns and rewrites from guest/home on every build -- the edit would be both lost and noise. ~/.local/bin is already on PATH via .zprofile, matching the other wrappers. The wrapper resolves by explicit path, never `command -v muse`, for the reason the pi wrapper documents: it is installed at ~/bin/muse and $HOME/bin precedes ~/.local/bin on PATH, so a PATH search self-resolves and exec-loops. The upstream installer is safe here for the same reason -- it invokes its launcher by absolute path, so it cannot re-enter this wrapper the way the opencode installer does in webcoyote#235. - The wrapper passes --yolo, which is worth naming precisely. Unlike pi's --approve, this *is* a permission bypass, and a broad one: muse ships both a Seatbelt sandbox and staged approval prompts, ON by default, and --yolo disables both and trusts the workspace for the run. It is the direct analogue of codex's --dangerously-bypass-approvals-and-sandbox rather than of claude's --dangerously-skip-permissions, because muse's own sandbox is the part that matters: nested inside sandvault it adds no protection while defaulting to proxy-only network and workspace-only writes. Workspace trust is what loads project-local skills and rules, and the sandbox is where to make that decision. The README names it as a bypass. - No tool-prompt injection, and so no sv-tool-prompts.sh source in the wrapper -- muse is the first agent with no mechanism for one. Verified against the 1.0.2 binary, not just the docs: there is no --append-system-prompt (claude, pi), no instructions-file option (codex's model_instructions_file) and no extra-context-directory option (gemini's --include-directories). --prompt-file is `muse exec` only and carries the user prompt. Muse takes instructions solely from rules files -- AGENTS.md / CLAUDE.md / .agents/AGENTS.md / .claude/CLAUDE.md in the workspace, plus "foreign personal rules" at ~/.claude/CLAUDE.md -- and every one of those writes into either the user's repository or another agent's config, where it would outlive the session and leak into `sv claude` too. So `sv -b muse` and `sv -i muse` start the browser and simulator but do not announce the endpoints; SV_BROWSER_ENDPOINT and SV_IOS_SIMULATOR_ENDPOINT remain in muse's environment for the agent or the user to read. The wrapper records this so the omission does not read as an oversight. - sv wiring: m|muse dispatch, help text, install_deps, and --fix-permissions coverage. muse installs from the homebrew/cask "muse-code" token, whose name differs from its "muse" binary -- that is what ensure_brew_tool's second argument is for. The native branch needs no arm: muse ships a self-contained binary, so unlike codex, gemini and pi it never needs node. - agentsview session export: muse is deliberately absent from AGENTSVIEW_AGENTS. agentsview 0.42.0 has no muse parser and no muse config key -- its nearest entry, commandcode_project_dirs (~/.commandcode/projects), is a different tool, while muse keeps sessions under ~/.local/share/muse/sessions. Wiring it would mirror a directory agentsview cannot read. helpers/agentsview-paths.sh records the omission, what to add when upstream ships support, and the documented refresh for existing opt-in users. A test asserts the omission stays deliberate rather than half-done: an entry here with no matching key in agentsview-config.py's VALID_KEYS aborts sv-agentsview-setup for every agent, not just muse. Tests: help listing, --yolo anchored to the exec line (it also appears in a comment and the startup banner, so a whole-file grep would stay green after it was dropped from the exec), dispatch arm, Homebrew cask token, native installer URL and MUSE_NO_MODIFY_PATH, agentsview omission, and muse added to the brew-symlink permission loop. No CHANGELOG entry: .github/scripts/patch-changelog generates that section from commits at release time. Docs: README feature list, quick start, agent-flag list, native install, and a note that the agentsview dashboard does not yet cover muse. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #236.
Adds Meta's Muse Code (
muse) alongside the existing five agents, following theconventions established by #182 (pi).
What's here
guest/home/bin/muse--native-install, execs--yolosvm|musedispatch, help text,install_depsHomebrew arm,--fix-permissionscoveragehelpers/agentsview-paths.shscripts/testsREADME.mdNo CHANGELOG entry —
.github/scripts/patch-changeloggenerates that from commitsat release time.
Three decisions worth reviewing
--yoloMuse ships both a Seatbelt sandbox and staged approval prompts, ON by default.
--yolodisables both and trusts the workspace for the run.This makes it the analogue of codex's
--dangerously-bypass-approvals-and-sandboxrather than claude's
--dangerously-skip-permissions, because muse's own sandboxis the part that matters: nested inside sandvault it adds no protection while
defaulting to proxy-only network and workspace-only writes. The narrower
--disable-approvalwould have kept that nested sandbox.The README names it as a bypass, as it does for the other agents.
No tool-prompt injection
Muse is the first agent with no mechanism for one, so the wrapper does not source
sv-tool-prompts.sh. Verified against the 1.0.2 binary, not just the docs:--append-system-prompt(claude, pi)model_instructions_file)--include-directories)--prompt-fileismuse execonly and carries the user promptMuse takes instructions solely from rules files —
AGENTS.md/CLAUDE.md/.agents/AGENTS.md/.claude/CLAUDE.mdin the workspace, plus "foreign personalrules" at
~/.claude/CLAUDE.md. Every one of those writes into either the user'srepository or another agent's config, where it would outlive the session and leak
into
sv claudetoo.So
sv -b museandsv -i musestart the browser and simulator but do notannounce the endpoints.
SV_BROWSER_ENDPOINTandSV_IOS_SIMULATOR_ENDPOINTremain in muse's environment for the agent or the user to read. Both the wrapper
and the README's Browser Automation and iOS Simulator sections say so, so a user
who runs
sv -b museand sees muse ignore the browser knows why, and knows tomention the endpoint in their prompt or
AGENTS.md. Happy to revisit if areviewer knows of a mechanism I missed.
Deliberately not wired into agentsview
agentsview 0.42.0 has no muse parser and no muse config key. Its nearest entry,
commandcode_project_dirs(~/.commandcode/projects), is a different tool — musekeeps sessions under
~/.local/share/muse/sessions. Wiring it would mirror adirectory agentsview cannot read.
helpers/agentsview-paths.shrecords the omission and what to add when upstreamships support; existing opt-in users would then pick it up the documented way
(delete
agentsview-export.state, re-runsv-agentsview-setup). A test assertsthe omission stays deliberate rather than half-done — an entry there with no
matching key in
agentsview-config.py'sVALID_KEYSabortssv-agentsview-setupfor every agent, not just muse.
Verification
Both install paths tested end-to-end against muse 1.0.2 (1.0.2-R2040.1) on
macOS 24.6.0 / Apple Silicon, including the full
svflow from a host account.Homebrew path (default) —
sv musemuse-codeinstalls a binary namedmuseat$(brew --prefix)/bin/musecom.apple.quarantine, "Homebrew Cask")muse --helpexits 0, soensure_brew_tool's warm-up succeeds instead of aborting$HOMEBREW_PREFIX/bin/museand execssv musefrom a host accountThe third row is the one that could have bitten: a cask binary exiting non-zero on
--helpwould makesv museabort with a misleading "quarantined and failed towarm up" on every host.
The composed run was made falsifiable by deleting the native install first, so the
wrapper had no fallback to hide behind. It never printed
Installing Muse Code natively..., confirming it took the Homebrew branch:Native install path —
sv -N muse.zshrc/.zprofilesv --rebuild -N musefrom a host accountThe loop row was tested by reproducing the exact conditions that made OpenCode
loop in #235: wrapper at
~/bin/muse,~/binahead of~/.local/binonPATH,invoked by name, with nothing pre-installed. It installed once and ran.
Muse is immune for a structural reason rather than by luck: its installer invokes
its launcher by absolute path (
MUSE_LAUNCHER_INSTALL=1 "$launcher"), so it cannever re-enter the wrapper. OpenCode's installer resolves through
PATHand does.The wrapper also resolves by explicit path, never
command -v muse, for the reasonthe pi wrapper documents — it lives at
~/bin/museahead of everything else, so aPATHsearch self-resolves and exec-loops.Runtime
--yolois effective at runtime, not merely parsed. Inside a live sandvaultsession (
sandbox-execactive, host home unreadable), muse reportsworkspace trust: trusted source=run-flag.Both composed runs reached an authenticated interactive session on the real
provider (
Model set to muse-spark-1.3-contributor,You are logged in.), withcredentials persisting in the sandbox home across both install paths.
Because
--yolodisables muse's own Seatbelt, muse never attempts to initialize asandbox nested inside sandvault's — the nesting risk is avoided rather than
survived.
One incidental observation: muse loads "foreign personal context" from
~/.claude/, so in the sandbox it picks up sandvault's own/svskill and reportsIncluding your 1 Claude Code personal skill. Benign, and arguably useful;--no-foreign-personal-contextwould suppress it if that is ever unwanted.Not verified
Sustained agentic work — a long multi-turn session exercising muse's shell and edit
tools under
--yolo. Session startup, authentication and a completed turn areverified; extended tool use is not.
Note for maintainers
While testing this I hit a separate, unrelated bug, now filed as #237:
scripts/testsdeletes its own working tree when the checkout sits at$SHARED_WORKSPACE/repos/sandvault— the exact pathsv-cloneproduces for thisrepo, and the invocation the README documents. Not addressed here, to keep this PR
focused.
🤖 Generated with Claude Code