Repository navigation
Sandbox
This page tells how to run Claude Code with the plugins of this checkout, in a config apart from your own. It is for AI agents and for people. Use it to see a hook, the status line, or an agent work in a real session before you release a change.
-
claude --plugin-dir plugins/dotclaudein your own setup loads the checkout next to your installed dotclaude, your settings, and your sessions. - A test there can change your settings files and write to your session history.
- The sandbox has its own
CLAUDE_CONFIG_DIRandHOME. So a test changes only the sandbox.
| Command | Config | Plugins loaded |
|---|---|---|
just sandbox |
dotclaude-sandbox in the temp folder, kept until you clean it |
Each folder of plugins/ that has .claude-plugin/plugin.json
|
just sandbox-alone PLUGIN |
/tmp/dotclaude-sandbox-alone, removed and made again on each run |
Only PLUGIN
|
just sandbox-install [PLUGIN...] |
/tmp/dotclaude-sandbox-install, removed and made again on each run |
Each PLUGIN, or dotclaude when you give none, installed from this checkout |
just sandbox-installed [ARGS] |
/tmp/dotclaude-sandbox-install, kept |
The plugins that just sandbox-install installed |
just sandbox-clean |
Removes the temp folder of just sandbox
|
None |
- Arguments after
just sandboxorjust sandbox-alone PLUGINgo toclaude. For example,just sandbox -p "prompt"runs one headless turn and prints the reply. -
just sandboxrunstools/sandbox/run.ts. That script runsplugins/dotclaude-lab/bin/dotclaude-sandbox. -
just sandbox-aloneruns the same bin two times. The first run removes the folder. The second run startsclaudewith--plugin-dir PLUGINand your arguments. - To test an add-on with no core plugin, run
just sandbox-alone plugins/dotclaude-docs. You can also runbun plugins/dotclaude-lab/bin/dotclaude-sandbox --plugin-dir plugins/dotclaude-docsin its place. -
just debug-agentsruns one headless session with a debug log. The command fails when the log names an unknown frontmatter field.
just sandbox and just sandbox-alone load the plugin folders with --plugin-dir.
A user installs the plugins from the marketplace instead.
just sandbox-install tests that path before a release:
- Run
just sandbox-install, or name the plugins, such asjust sandbox-install dotclaude dotclaude-docs. The script removes the folder, and it runsclaude plugin marketplace addwith this checkout. Then it runsclaude plugin install NAME@dotclaudefor each plugin, and it starts Claude Code. - In the session, run
/dotclaude:setup, then/exit. - Run
just sandbox-installedto start the session again with the settings of the setup. Add--continueto continue the last session.
- Claude Code reads each plugin of a local marketplace from its folder in the checkout.
So after
just generate, an edit takes effect at the next session start or at/reload-plugins.claude plugin updatesays that it has nothing to update. - The install does not fetch from GitHub. That step runs only for a marketplace that a user adds from GitHub.
The sandbox folder has three parts:
| Folder | Use |
|---|---|
config/ |
The sandbox CLAUDE_CONFIG_DIR. |
project/ |
An empty git repository. It is the working directory of claude. |
home/ |
The HOME of claude. |
- The default folder is
dotclaude-sandboxin the system temp folder. Use--dir DIRto choose another folder. - The script creates
project/only when it has no.gitfolder. It runsgit initthere. - The script writes no
settings.jsontoconfig/. Copy the keys that a test needs from the settings snippet intoconfig/settings.json. With no snippet, the sandbox runs with the defaults of Claude Code and the plugins that you load. - The
--diroption also works forjust start-run.
The script does these steps before it starts claude:
- It creates the three folders, and it runs
git initinproject/. - It writes
config/.claude.jsonwith these keys:-
hasCompletedOnboarding: true, and athemewhen none is set. The TUI then does not show onboarding. -
projects.<dir>.hasTrustDialogAccepted: true, so the TUI does not ask for trust. On macOS,/tmpand/varresolve to/private, and Claude Code uses the resolved path. The script sets both paths.
-
- It removes the variables
ZDOTDIRandXDG_CONFIG_HOMEfrom the environment ofclaude. Your global git config does not apply in the sandbox. Inference: the git config lives in your realHOME, andclaudegets the sandboxHOME. - It removes the variables of a parent Claude Code session from the environment.
These are the variables such as
CLAUDECODEandCLAUDE_CODE_ENTRYPOINT. It also removes the variables with the prefixesCLAUDE_CODE_SESSION_,CLAUDE_CODE_MESSAGING_, andCLAUDE_PLUGIN_. It also removes each variable that has the same value as theenvof your own settings. A value that you set on the command line for the sandbox stays. - It sets
CLAUDE_CONFIG_DIRandHOMEforclaude. It runsclaudeinproject/.
- The
ghlogin of your machine does not apply in the sandbox.ghreads its config fromHOME. So a policy test in Contributions sees no login name. -
ghreads its token from the keychain with the realHOME. So agh apicall can fail in the sandbox. A failedghcall gives no owner. So the policy ask does not show, and the next check tries again. - The script finds
claudeonPATHonly. A shell function or an alias does not work. The native installer keeps each version at~/.local/share/claude/versions/<version>. This path holds for version2.1.296on macOS, as the native installer of that version left it.
On a machine whose managed enabledPlugins holds a plugin name, Claude Code ignores the --plugin-dir copy of that plugin.
The debug log then says "plugin is locked by managed settings".
Remove the managed entry, or test on a machine without it.
A new config directory has no login.
The script gives claude a token in its environment only.
It uses the first of these sources that holds a token:
-
CLAUDE_CODE_OAUTH_TOKEN, when you set it.claude setup-tokenmakes a long-lived token. - Your own login token.
On macOS, it is in the Keychain entry
Claude Code-credentials. On other systems, it is in~/.claude/.credentials.json. The script readsclaudeAiOauth.accessTokenfrom the file.
- The script does not write the token to disk, and it does not print it.
- If it finds no token, it prints "No login token found".
Then you run
/loginin the sandbox. - Inference: a sandbox session uses your account, so its usage counts against your plan limits.
Rules for agents:
- Get the approval of the user before you use their login in a sandbox.
- Never put the token in a command line, a file, or your output. Command lines show in process lists and in the transcript. Give the token through the environment only, as the script does.
- A shell
echoorenvin the sandbox session shows the token. Do not run them there.
-p runs one turn and prints the reply.
Use it for hook behavior that the reply shows.
-
Put test files in
project/first. For example, add apkg/CLAUDE.mdwith a rule that the reply must show. -
Run the prompt:
just sandbox --model claude-sonnet-5-5 -p "Run cat pkg/a.ts and describe it." -
Add
--debug-file <path>to see which hooks ran and what they returned.
- The script warns when a
--modelvalue is not inavailableModelsof the sandboxsettings.json. Claude Code gives no error for such a model. The session runs on another model. - Put the prompt before variadic flags such as
--allowedTools. A variadic flag takes all the arguments after it. Thenclaudefinds no prompt. - Add
--allowedTools 'Bash(cat:*)'when the test needs a tool without a permission prompt. - The binary of Claude Code 2.1.296 has the text "you haven't granted it yet".
It shows that text for a tool that needs approval and has no grant.
Inference: a headless run cannot answer a prompt, so each tool that
--allowedToolsdoes not list is denied. - List each tool that the test needs before the run.
For example, use
--allowedTools 'Bash' 'Write' 'Edit'.
The status line, the agent panel, and dialogs show only in the TUI.
Run the TUI in a detached tmux session, and read the screen:
tmux new-session -d -s sandbox -x 150 -y 40 "just sandbox; sleep 60"
tmux send-keys -t sandbox -l 'your prompt'
tmux send-keys -t sandbox Enter
tmux capture-pane -p -t sandbox | tail -30
tmux kill-session -t sandbox- Wait some seconds after the start and after each prompt. Then read the screen.
- Send the text with
-l. SendEnterin a second command. If you do not,tmuxcan read words of the text as key names. - Ask for an agent with
run_in_backgroundto see the agent panel.
just capture is a proxy that writes each request body that passes through it.
Run a capture session to record the requests of an agent session and of its sandbox runs.
-
In a terminal, run
just capture. It listens on port8787by default. It writes to$TMPDIR/dotclaude-capture. -
In a second terminal, start the agent session with the proxy:
ANTHROPIC_BASE_URL=http://127.0.0.1:8787 ENABLE_TOOL_SEARCH=true claude
-
just sandboxruns in that session. It inherits both variables.
- The proxy forwards to
--upstream URL. Without that option, it forwards toANTHROPIC_BASE_URL. Without that variable, it forwards tohttps://api.anthropic.com. It stops with an error when the upstream is its own address, because the requests would loop. This happens when you exportANTHROPIC_BASE_URLin the terminal ofjust capture. -
ENABLE_TOOL_SEARCH=truekeeps tool search enabled. Claude Code disables it for a base URL that is not first-party. - The proxy does not write request headers, because they hold the credential.
just start-run runs one clean sandbox session through the proxy.
Then it runs just measure on the transcript.
Send one prompt, then type /exit.
-
just capturelistens on a local port. On macOS, an agent session with a sandbox can block that port. The fix issandbox.network.allowLocalBinding: truein the settings of the session that runs the agent. The user sets that value.
To test the status line script without a session, give it input on stdin.
The script is plugins/dotclaude/bin/dotclaude-statusline.
Read the fields of the input in the script, and in the status line docs of Claude Code, before you write the input.
echo '<input JSON>' | bun plugins/dotclaude/bin/dotclaude-statuslineTo see the input that Claude Code gives to the status line, set a status line command in config/settings.json.
That command writes stdin to a file.
Then read that file.
- Stop each
tmuxsession that you started. - Run
just sandbox-clean. It removes the wholedotclaude-sandboxfolder. That folder holdsconfig/,project/,home/, and the.dotclaude-sandboxmarker. - For
just sandbox-alone, runjust sandbox-alone PLUGINagain. Or remove/tmp/dotclaude-sandbox-alonewithbun plugins/dotclaude-lab/bin/dotclaude-sandbox --dir /tmp/dotclaude-sandbox-alone --clean. The setup writes the marker into each folder that it makes.--cleanof a folder other than the default one refuses a folder without the marker. A folder that an older sandbox made has no marker, so remove it by hand. - For
just sandbox-install, runbun plugins/dotclaude-lab/bin/dotclaude-sandbox --dir /tmp/dotclaude-sandbox-install --clean.
A sandbox session also writes into your checkout.
Claude Code writes .claude-plugin/types/ and a tsconfig.json into each plugin folder that it loads, and git ignores them.
See Types.
The sandbox of just sandbox keeps its sessions and settings until you remove it.
That lets you continue a test.
But an old sandbox can hide a first-run problem.
just sandbox-alone starts from an empty folder on each run.
So it shows a first-run problem.
The symptoms of the sandbox and their fixes are on Troubleshooting.
- Development lists the repository commands.
- Hook Events describes how to test a hook. Lab lists the bins of this page.
- Guards tells how to read guard asks and denies.
- Start
- How dotclaude Works
- Add-On Plugins
- How Claude Code Works
- Reference
- Rationale
- Evidence
- Development
- Archive