| name | magpie-setup-isolated-setup-install |
| family | setup |
| mode | Meta |
| description | Guide an adopter through the first-time install of the
framework's secure agent setup (bubblewrap + socat +
claude-code, sandbox/permissions/clean-env layers). Walks
every step interactively; never auto-runs sudo, shell-rc
edits, or settings overwrites.
|
| when_to_use | Invoke when the user says "set up the secure agent setup",
"first-time install of the secure config", "install the
secure setup in this tracker", "walk me through the
secure-agent-setup install", or starts working on a fresh
adopter clone without secure-config wiring. Also appropriate
after a fresh OS install / new dev machine where
`~/.claude/scripts/` is empty. Skip when the secure setup is
already in place — use `setup-isolated-setup-verify` (to
confirm completeness) or `setup-isolated-setup-update` (to
refresh against the framework's latest) instead.
|
| capability | capability:platform |
| license | Apache-2.0 |
setup-isolated-setup-install
This skill is the on-ramp for adopters who do not yet have the
secure setup running. It is a thin walkthrough wrapper around the
canonical install path documented in
docs/setup/secure-agent-setup.md. The
authoritative content lives there; this skill exists so an adopter
can say "set up the secure agent setup" in a fresh session and
land in the right step-by-step flow without first reading the
document.
Adopter overrides
Before running the default behaviour documented
below, this skill consults
.apache-magpie-overrides/setup-isolated-setup-install.md
in the adopter repo if it exists, and applies any
agent-readable overrides it finds. See
docs/setup/agentic-overrides.md
for the contract — what overrides may contain, hard
rules, the reconciliation flow on framework upgrade,
upstreaming guidance.
Hard rule: agents NEVER modify the snapshot under
<adopter-repo>/.apache-magpie/. Local modifications
go in the override file. Framework changes go via PR
to apache/magpie.
Snapshot drift
Also at the top of every run, this skill compares the
gitignored .apache-magpie.local.lock (per-machine
fetch) against the committed .apache-magpie.lock
(the project pin). On mismatch the skill surfaces the
gap and proposes
/magpie-setup upgrade.
The proposal is non-blocking — the user may defer if
they want to run with the local snapshot for now. See
docs/setup/install-recipes.md § Subsequent runs and drift detection
for the full flow.
Drift severity:
- method or URL differ → ✗ full re-install needed.
- ref differs (project bumped tag, or
git-branch
local is behind upstream tip) → ⚠ sync needed.
svn-zip SHA-512 mismatches the committed
anchor → ✗ security-flagged; investigate before
upgrading.
Golden rules
- Do not auto-run privilege-elevating commands. Anything that
needs
sudo (apt / dnf installs, system-wide writes) is printed
for the user to copy-paste into their own terminal. The skill
never invokes sudo itself.
- Do not edit shell rc files without approval.
~/.bashrc /
~/.zshrc modifications (sourcing agent-iso.sh, the optional
alias claude='claude-iso') are surfaced as the exact line to
add; the user pastes it themselves. The skill confirms the rc
file path with the user first; it does not assume.
- Do not overwrite an existing settings file silently. If the
user already has a project
.claude/settings.json or a
user-scope ~/.claude/settings.json, the skill diffs the
desired merge against the existing file and asks for explicit
approval before writing. Re-installs / partial-state recoveries
are common — the skill must not blow away an unrelated
pre-existing hook or permissions.ask rule. The desired merge
includes the agent-guard hooks.PreToolUse entry (matcher
Bash, command running the user-scope ~/.claude/scripts/agent-guard.py)
— the deterministic guard from
tools/agent-guard. Install
the script as ~/.claude/scripts/agent-guard.py and populate
~/.claude/scripts/guards.d/ from both the engine's bundled
guards.d/*.py and every skill-owned skills/*/guards/*.py
(so skill-owned guards like mention / mark-ready are active),
alongside the other user-scope scripts (Step P); wire the
PreToolUse entry once, and preserve any pre-existing hooks
entries.
- Stop on the first failure. If a step fails (manifest read
fails, framework path wrong, an existing file conflicts in a way
the user has not yet decided about), stop and report. Do not
push past a failure to the next step.
Up-front confirmations
Before walking any install step, confirm with the user:
- OS / distro. macOS, Ubuntu / Debian (apt), Fedora / RHEL
(dnf), or Arch / NixOS / other. macOS skips bubblewrap +
socat (Seatbelt is built-in); Linux installs both per the
distro shortcut.
- Framework checkout path. The path to the user's local
magpie clone. Required to read
tools/agent-isolation/pinned-versions.toml,
.claude/settings.json, and the
tools/agent-isolation/*.sh scripts. If the user does not
have a clone, walk them through git clone first.
- Fresh install or re-install. For a re-install on a partial
existing state, the skill must enumerate the existing wiring
(project settings.json, user settings.json, hooks dir, the
agent-guard
hooks.PreToolUse entry + ~/.claude/scripts/agent-guard.py,
shell rc) before any merge so the user knows what is being
preserved vs replaced.
- Sync repo (optional). Whether the user maintains a
private dotfile-style
~/.claude-config repo per
Syncing user-scope config across machines.
If yes, the skill installs user-scope scripts as symlinks
into ~/.claude-config/scripts/ rather than cp-ing into
~/.claude/scripts/ — the symlink approach is what makes
sync push the upgrades to other machines automatically.
Walk-through
Follow the canonical step list at
docs/setup/secure-agent-setup.md → Adopter setup → Via a Claude Code prompt.
Each step in that list maps 1:1 to a step in this skill. Do not
re-write the list here — read the doc, follow it, and surface each
sub-step with the user. The doc names are the source of truth; the
skill is the runner.
For the verification step at the end, hand off to the
setup-isolated-setup-verify skill rather than re-walking the checklist
inline.
Agent runtime — install @latest, enforce the floor
claude-code is not pinned to an exact version. Install it with
npm install -g --no-save @anthropic-ai/claude-code@latest (the
command in the doc's install list) — latest always carries the newest
permission-rule / sandbox / prompt-injection fixes. The manifest's
[tools.claude-code] table in
pinned-versions.toml
declares a min_version floor, not a pin. Because this install is
driven from Claude Code, apply the same hard gate
setup-isolated-setup-verify check 5 applies: read the running
version (claude --version) and, if it is below min_version,
hard-fail — stop the install, tell the operator to upgrade to
@latest, and have them re-run. The secure setup must not be stood up
on a below-floor runtime.
Step P — Project-root coverage in the sandbox allowlists
Per
docs/setup/secure-agent-setup.md → Project-root coverage in the sandbox allowlists
and issue #197,
the harness pre-resolves sandbox.filesystem.allowRead: ["."] at
session start in a way that silently drops the literal . from the
resolved set, so a session in a freshly-cloned adopter repo can
write to CWD but cannot read from it under the sandbox.
The defensive measure is to add the project root as an explicit
absolute path to sandbox.filesystem.allowRead and allowWrite
in the adopter's project-local settings file
(<repo>/.claude/settings.local.json) — gitignored, per-machine,
per-project, merged on top of the committed project-scope and
user-scope by the harness. Worktrees handle themselves: each
worktree has its own <worktree>/.claude/settings.local.json,
and each gets its own root added.
Step P.0 — Ask the user: per-project or whole-user scope?
Before installing anything, ask the operator which scope they
want — see
docs/setup/secure-agent-setup.md → Per-project vs whole-user scope
for the full rationale + trade-offs:
- Per-project. The skill runs the helper for the
current project only. Future projects on this host need the
skill re-run in each, OR the operator can pick whole-user later.
No global git config changes.
- Whole-user (global). The skill walks the operator's existing
local git checkouts and populates their
settings.local.json
files, then sets git config --global core.hooksPath so every
future git checkout / git clone / git worktree add on the
host picks up the framework's universal post-checkout hook.
Detect whether whole-user (global) scope is already active first.
Read git config --global --get core.hooksPath:
- If it is unset (or does not point at the framework's shared
hook dir), whole-user isolation is not yet set up on this host.
In that case propose whole-user (global) as the default — it
is the recommended baseline because it covers every current and
future repo on the host in one pass, so the operator does not have
to re-run this skill per project. Present per-project as the
narrower alternative for operators who deliberately want to keep
their per-repo git hooks (see the Step P.0a caveats).
- If it is already set to the framework's shared hook dir,
whole-user isolation is already in place; default to per-project
for this specific project (the global hook already covers the host,
so only this project's
settings.local.json needs the local pass).
Prefer structured Q&A. When the agent harness offers a
structured-question tool (e.g. Claude Code's AskUserQuestion),
use a single-select prompt whose default is chosen by the detection
above — Whole-user (global, recommended) when whole-user is not
yet set up, otherwise Per-project. Free-form chat is the fallback.
If the user picks per-project, skip to Step P.1 — Install
the helper script and Step P.2 — Run the helper for this project,
then move on to the next step in the canonical install list.
If the user picks whole-user, follow Step P.0a's loud
disclosure first, then Step P.0b (pick the whole-user flavour —
simple vs dispatcher), then Step P.1, then Steps P.2-whole-user
(walk existing checkouts) and P.3-whole-user (install the global
hook + set core.hooksPath). If they chose the dispatcher flavour,
also run Step P.3b-whole-user (install the dispatcher + prek shim).
Step P.0a — Loud disclosure before setting whole-user scope
If the operator picked whole-user, surface this disclosure
before any global config write. The operator must
acknowledge it explicitly; no silent proceed:
!!! WHOLE-USER SCOPE — core.hooksPath GLOBAL OVERRIDE !!!
Setting git config --global core.hooksPath makes git look up
hooks in one shared directory for every repo on this host.
Every .git/hooks/* in every existing repo on this machine
becomes inert — git will no longer fire your per-repo
pre-commit, commit-msg, pre-push, or any other hook
unless the shared dir chains back to them.
There are two whole-user flavours (you choose next, Step P.0b):
- Simple — the framework installs only the
post-checkout hook in the shared dir. Every other per-repo
hook stays inert until you migrate it into ~/.claude/git-hooks/
yourself. Pick this only if you don't rely on per-repo hooks.
- With per-repo dispatcher (recommended if you use prek /
pre-commit / husky / any per-repo hook) — the framework
installs a dispatcher for each hook type. Each dispatcher
runs the framework's own logic (the
post-checkout
sandbox-allowlist sync) and then chains through to that
repo's own .git/hooks/<name>. Your per-repo hooks keep
firing, and a repo with no hook is a clean no-op. A bundled
prek PATH shim makes prek install write its shim into the
repo-local .git/hooks/ (via --git-dir) so the dispatcher
can find it.
Either way, whole-user scope is reversible:
git config --global --unset core.hooksPath restores per-repo
hook lookup, and (dispatcher flavour) removing
~/.claude/bin from PATH restores the stock prek.
Confirm the operator wants to proceed with whole-user scope after
reading the disclosure. If they hesitate or pick per-project,
fall back to the per-project path.
Step P.0b — Choose the whole-user flavour: simple or dispatcher
If the operator confirmed whole-user, ask which flavour (see the
disclosure above for the trade-off). Default to the dispatcher
flavour — it is a strict superset of simple (it still runs the
post-checkout sync everywhere) and it avoids silently shadowing
per-repo hooks, which is the most common whole-user surprise.
Pick simple only if the operator explicitly wants the minimal
footprint and confirms they run no per-repo hooks.
Prefer structured Q&A here too: a single-select with
With per-repo dispatcher (recommended) as the default and
Simple (post-checkout only) as the alternative.
- Simple → Step P.3-whole-user installs
git-global-post-checkout.sh
only.
- Dispatcher → Step P.3-whole-user still runs, and Step
P.3b-whole-user additionally installs
git-hook-dispatcher.sh
(symlinked to each hook name, including post-checkout — which
in this flavour supersedes the standalone git-global-post-checkout.sh)
and the prek PATH shim.
Step P.1 — Install the helper script
(Both scopes.)
Copy tools/agent-isolation/sandbox-add-project-root.sh into
~/.claude/scripts/sandbox-add-project-root.sh (or symlink it
from ~/.claude-config/scripts/ if the operator uses the
private sync repo), mode 0755. The script file lives
user-scope so a single install covers every adopter project on
the host; what it writes is project-local. The same install
mechanism the sandbox-bypass-warn.sh and sandbox-status-line.sh
helpers use (see the Sandbox-bypass visibility hook and
Sandbox-state status line sections of the doc).
Step P.2 — Run the helper for this project (per-project scope)
Skip if the operator picked whole-user scope (Step P.2-whole-user
below covers the equivalent).
Run the helper once with --all-worktrees in the adopter
repo's main checkout. The helper enumerates
git worktree list --porcelain and, for each worktree, writes
that worktree's absolute path into that worktree's own
<worktree>/.claude/settings.local.json (creating the file if
it does not yet exist). Idempotent, atomic, tolerant of missing
prereqs (see the script's header comment for the full
failure-mode list). On success, surface the diff so the operator
sees which entries landed; on no-op (paths already present),
surface a one-line "already covered" confirmation.
Sandbox-bypass requirement when invoked from inside an agent
session. .claude/settings.local.json is in Claude Code's
built-in sandbox denyWithinAllow set (verified empirically —
see
docs/setup/secure-agent-setup.md → Security rationale),
so the helper's Bash write is blocked when invoked through the
agent's Bash tool. If this skill is being walked from inside
a sandboxed session, invoke the helper with
dangerouslyDisableSandbox: true and the reason
"writing project-local sandbox-allowlist entries (issue #197 fix)".
The bypass triggers sandbox-bypass-warn.sh's loud-red banner
so the operator sees and approves the single write. When the
operator runs setup-isolated-setup-install directly from a
terminal (the typical first-time-install path), no bypass is
needed — the script runs outside the agent sandbox.
Step P.2-whole-user — Walk existing checkouts (whole-user scope)
Skip if the operator picked per-project scope.
Walk the operator's existing git checkouts and populate each
one's .claude/settings.local.json. This pass is settings-only
by default — it does NOT install per-repo post-checkout hooks
(the global hook installed in Step P.3-whole-user covers that
for both existing and future repos via core.hooksPath).
-
Prompt the operator for root directories to scan.
Default suggestions: ~/code/, ~/projects/, ~/dev/,
~/work/. Show the operator the list, let them edit it.
Empty list → skip the walk; the operator can re-run this
skill later when they want existing repos covered.
-
Walk each root dir. Use a depth-limited find with
reasonable exclusions:
find "<root>" -maxdepth 5 -type d -name .git \
-not -path '*/node_modules/*' \
-not -path '*/.venv/*' \
-not -path '*/__pycache__/*' \
-not -path '*/build/*' \
-not -path '*/dist/*' \
-not -path '*/.cache/*' \
-prune
For each .git/ found, the parent dir is a working tree.
De-duplicate by canonical path.
-
For each working tree found, run the helper with
--all-worktrees. Same invocation as the per-project
variant — the helper itself handles git worktree list so
linked worktrees of the same repo get processed. The helper's
built-in git check-ignore guard skips repos whose
.claude/settings.local.json is not gitignored (defense in
depth — the operator should fix the .gitignore first).
-
Tabulate the result for the operator: how many checkouts
were scanned, how many had .claude/ (so the helper wrote),
how many were skipped (no .claude/ directory, or
.claude/settings.local.json not gitignored).
-
Do not install per-repo post-checkout hooks during this
pass. The next sub-step covers future and existing repos
uniformly via the global hook.
Step P.3-whole-user — Install the global post-checkout hook (whole-user scope)
Skip if the operator picked per-project scope. Dispatcher
flavour (Step P.0b): skip step 1 below — Step P.3b-whole-user
installs the dispatcher as post-checkout instead, superseding the
standalone git-global-post-checkout.sh. Still run step 2 (set
core.hooksPath) here.
-
(Simple flavour only) Install the universal post-checkout
hook. Copy
tools/agent-isolation/git-global-post-checkout.sh into
~/.claude/git-hooks/post-checkout (or symlink it from
~/.claude-config/git-hooks/post-checkout if the operator
uses the private sync repo), mode 0755. The hook content is
in
tools/agent-isolation/git-global-post-checkout.sh —
it calls the sandbox-allowlist helper for Claude-Code-aware
worktrees.
-
Set core.hooksPath globally so every git operation across
every repo on the host uses the shared hook dir:
git config --global core.hooksPath "$HOME/.claude/git-hooks"
Surface the resulting git config --global --get core.hooksPath
value to the operator to confirm the write.
-
Reiterate the implication from Step P.0a's disclosure:
per-repo .git/hooks/* are now inert across the entire host.
The operator must migrate any per-repo hooks they want to keep.
git config --global --unset core.hooksPath is the reversal.
After this step, future git clone, git worktree add, and
git checkout operations anywhere on the host invoke the
framework's universal post-checkout, which keeps each
Claude-Code-aware project's .claude/settings.local.json in
sync without any further operator action.
Step P.3b-whole-user — Install the per-repo dispatcher + prek shim (dispatcher flavour only)
Skip unless the operator picked the dispatcher flavour in
Step P.0b. This is what keeps the operator's per-repo hooks (prek,
pre-commit, husky, hand-written) firing under global
core.hooksPath — the shared dir runs the framework's logic and
then chains through to each repo's own .git/hooks/<name>. See
docs/setup/secure-agent-setup.md → Whole-user with the per-repo dispatcher
for the full rationale + the validated behaviour matrix.
-
Install the dispatcher for each hook type. Copy
tools/agent-isolation/git-hook-dispatcher.sh
into ~/.claude/git-hooks/git-hook-dispatcher.sh (or symlink
from ~/.claude-config/git-hooks/ under the private sync repo),
mode 0755, then create one symlink per hook name pointing at
it — including post-checkout, which replaces the standalone
file from Step P.3 (the dispatcher runs the same sandbox sync,
then chains to any repo-local post-checkout):
cd ~/.claude/git-hooks
for h in post-checkout pre-commit prepare-commit-msg commit-msg \
post-commit pre-push post-merge post-rewrite \
pre-rebase pre-merge-commit; do
ln -sf git-hook-dispatcher.sh "$h"
done
The dispatcher is basename-keyed, so one file serves every hook
type. It resolves the repo-local hook via
git rev-parse --git-common-dir (worktree-safe) and execs it
with the original argv + inherited stdin, so a failing local
pre-commit / pre-push still aborts the git operation.
-
Install the prek PATH shim so prek install writes its
shim into the repo-local .git/hooks/ (where the dispatcher
chains) instead of the shared dir. Copy
tools/agent-isolation/prek-shim.sh
into ~/.claude/bin/prek (or symlink from
~/.claude-config/bin/prek), mode 0755. The shim rewrites
only prek install (injecting
--git-dir "$(git rev-parse --git-common-dir)" unless the caller
already passed --git-dir, asked for --help, or is outside a
git work tree); every other prek invocation passes through
unchanged. It is a no-op on hosts with no global
core.hooksPath.
-
Surface the PATH line for the operator to add to their
~/.bashrc / ~/.zshrc (per the golden rules — never edit the
rc file directly):
export PATH="$HOME/.claude/bin:$PATH"
Confirm the rc path with the operator; print the line for them
to paste. Until it is on PATH ahead of the real prek, the
shim is inert and prek install reverts to writing into the
shared dir.
-
Tell the operator how their existing prek repos are picked up.
Repos that already ran prek install before this setup have
their shim in .git/hooks/pre-commit already (that is where a
pre-core.hooksPath prek install put it), so the dispatcher
finds them with no further action. Repos where they run
prek install after the global switch will now install
locally via the shim. Only repos where the shim already landed
in the shared dir (from a prek install run while
core.hooksPath was set but before this dispatcher setup)
need a one-time prek install re-run through the shim.
The . entry stays in the committed project-scope allowRead
regardless — the explicit absolute path in
settings.local.json is belt-and-braces, not a replacement. If
the harness ever stops resolving ., the explicit path still
covers the project; if . works correctly, the explicit entry is
redundant but harmless. The committed project-scope file is
never modified by the helper (machine-specific absolute paths
have no business in a file shared across contributors).
The helper is also invoked by /magpie-setup adopt,
/magpie-setup upgrade, and /magpie-setup worktree-init for
the same reason. The post-checkout git hook installed by
/magpie-setup adopt chains into the helper too, so new
worktrees added via git worktree add after this install pass
inherit access automatically — no operator action needed.
After the install lands
Suggest two follow-up routines the user can wire later:
setup-isolated-setup-verify — re-run after every Claude Code upgrade
or settings-file edit, to confirm denials still fire as
expected. The "did a denial silently turn into an allow?"
signal is exactly what this skill exists for.
setup-isolated-setup-update — periodic check for framework
updates, pinned-tool upgrade candidates, and drift between the
installed user-scope copies and the framework's
source-of-truth. Recommend a per-Claude-Code-upgrade or
monthly cadence, whichever comes first.
Always propose shared-config sync once the install lands.
Regardless of whether the operator already maintains the
~/.claude-config sync repo, proactively offer to run
setup-shared-config-sync as a follow-up:
- If the
~/.claude-config sync repo is already in place, the
skill commits + pushes the local modifications (the user-scope
scripts, hooks, and settings this install just wired up) so the
other machines pick them up.
- If the operator does not yet have
~/.claude-config, the
setup-shared-config-sync skill bootstraps it (clones the
default private remote if it exists, or creates a fresh private
remote and scaffolds the layout). Mention this so a first-time
operator knows the follow-up will set sync up from scratch — the
point of proposing it is precisely so the just-installed config
does not stay machine-local.
Surface it as an offer for the operator to accept, not an
auto-run — the sync skill has its own confirmation gates before it
commits or pushes anything.