You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Runs default to `--setting-sources project` (project-only). The new
topbar chip lets the user opt into `user,project`, which pulls in
`~/.claude/CLAUDE.md`, slash commands from `~/.claude/skills/`,
enabled plugins, and the env/permissions from `~/.claude/settings.json`.
With user scope on, `--disable-slash-commands` is dropped so installed
slash commands actually dispatch.
Replaces the old `skillsEnabled` field; the schema migrates the old
key forward. The rename reflects that this is all-or-nothing in
`claude -p` — there is no finer knob that loads skills alone, so
the toggle has to advertise the wider scope honestly.
Folds in CLAUDE.md docs for both the user-scope flag gating and the
write-mode --append-system-prompt landed in 9f7a280, since the two
edits to the Spawn paragraph were physically interleaved.
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@ Tests are plain tsx scripts, not a framework. Each file declares scenarios via a
65
65
- Top-level entries of the user's project are mirrored by recursively walking the source tree, creating real directories on the sandbox side and **hardlinking individual files** (with a copy fallback on `EXDEV` when source and storage live on different filesystems). Hardlinks rather than symlinks because Claude Code's `Glob` and `Grep` are backed by `rg --files`, which skips symlinks without `--follow` — symlinked leaves would make the entire tree invisible to glob discovery. Filtering applies at every level: `HARD_EXCLUDED` (`.git`, `.claude`, `node_modules`, `.DS_Store`), root + nested `.gitignore`, the user's global git excludes file, source-side symlinks whose realpath escapes `cwd`, and symlink cycles.
66
66
- Filtering subtleties — easy to regress when refactoring `sandbox.ts`: the ignore chain is walked **most-specific to least-specific** using `Ignore.test()` so a nested `!keep.log` overrides a root `*.log` (matches git's per-directory precedence — don't switch to `.ignores()`, which short-circuits and ignores negation). `Mirror.walk` records the **realpath of every directory it descends into** (not only symlink targets), so a symlink pointing back at a real-dir ancestor (`a/b/loop -> a`) is rejected on first encounter rather than after a wasted level of mirroring. Storage-root exclusion uses **realpath** (`realIsStorageRoot` against `classified.realTarget ?? <cwdReal>/<name>`), not the entry's path string — a top-level symlink like `alias -> .storage` would otherwise pass a name-based guard and let the mirror copy the sandbox's own state into a run dir.
67
67
- In `write` mode, a `.claude/settings.json` allows `Write`/`Edit` only against `../outputs/**` (the per-run outputs dir, one level above the child's cwd). Read-only mode passes a tools allowlist of `Read,Glob,Grep,WebSearch,WebFetch`; write mode adds `Write,Edit`.
68
-
3.**Spawn** (`src/server/runner.ts`) — invokes `claude -p <prompt> --output-format stream-json --include-partial-messages --verbose --model <m> --tools <list> --allowedTools <list> --strict-mcp-config --setting-sources project --disable-slash-commands`. The runner **strips**`NODE_OPTIONS`, `GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE`, `GIT_COMMON_DIR`, `GIT_CEILING_DIRECTORIES`, `CLAUDE_PROJECT_DIR`, `CLAUDE_PROJECT_NAME` from the spawn environment so a parent shell can't override the planted sandbox. `HOME` / `CLAUDE_CONFIG_DIR` are kept so the child reads the user's auth.
68
+
3. **Spawn** (`src/server/runner.ts`) — invokes `claude -p <prompt> --output-format stream-json --include-partial-messages --verbose --model <m> --tools <list> --allowedTools <list> --strict-mcp-config --setting-sources <scope>` and (when user scope is off) `--disable-slash-commands`. The two trailing flags are gated by `session.userScopeEnabled` (topbar "User scope" chip): off by default ⇒ `--setting-sources project --disable-slash-commands`; on ⇒ `--setting-sources user,project` and the disable flag is dropped. `user,project` is all-or-nothing in `claude -p` — there's no finer knob that loads `~/.claude/skills/` alone, so flipping it on also injects the user CLAUDE.md, enabled plugins (skills/hooks/agents), and the env/permissions blocks from `~/.claude/settings.json`. Naive removal of `--disable-slash-commands` looks like it should re-enable installed skills but doesn't: the user setting source has to be widened too. When `mode === 'write'`, the runner additionally passes `--append-system-prompt <WRITE_MODE_SYSTEM_PROMPT>` (text lives in `src/shared/constants.ts`) telling the child to mirror source paths under `../outputs/<rel>` and write full files — without this nudge, models often recognize the `Write(**)`/`Edit(**)` deny rule from the planted `.claude/settings.json` and bail out ("I cannot apply these fixes") instead of producing modified copies in the outputs dir. Confounder to watch for when designing a write-mode variant: if the variant's own content prescribes a different output convention (e.g. "place results in `results/`"), it conflicts with this directive — that's the first place to look if write-mode A/B results seem off. The runner **strips** `NODE_OPTIONS`, `GIT_DIR`, `GIT_WORK_TREE`, `GIT_INDEX_FILE`, `GIT_COMMON_DIR`, `GIT_CEILING_DIRECTORIES`, `CLAUDE_PROJECT_DIR`, `CLAUDE_PROJECT_NAME` from the spawn environment so a parent shell can't override the planted sandbox. `HOME` / `CLAUDE_CONFIG_DIR` are kept so the child reads the user's auth.
69
69
4.**Stream parsing** (`src/server/claudeStream.ts`) — line-buffers stdout into Anthropic stream events. Normalizes them into `NormalizedEvent`s (see `src/shared/schemas/events.ts`). Two non-obvious rules:
70
70
-**Turn counting:** increment on every `message_stop` where `currentMessageRole === 'assistant'`. Tool-use stops count too, so the live counter advances on each intermediate assistant message — useful as a "model is doing work" heartbeat. Partial deltas never count.
71
71
-**Tool result pairing:**`tool_result` events arrive in a later (user) message and may be reordered relative to their `tool_use` blocks. Pair by `tool_use_id`, not by recency (issue #5).
0 commit comments