How to deploy OPS on a new machine, from zero to fully working. Read this first when setting up a new host.
This doc is the authoritative deployment reference. CLAUDE.md and the READMEs
point here. Do not duplicate steps elsewhere; reference this file.
Deployment splits cleanly into two stages with no overlap. Stage 1 invokes Stage 2 automatically — no manual step between them.
| Stage | Owner | Repo | What it does |
|---|---|---|---|
| 1 — Host setup | shellSetup.sh / winSetup.ps1 |
linuxploitacious (public) |
OS packages, dotfiles via Stow, AI CLI install, gh auth, Level 1 Claude files (CLAUDE.md, settings.json, statusline.sh), clone your private OPS copy, then invoke Stage 2. |
| 2 — OPS setup | .claude-config/deploy.sh (Linux/macOS) / deploy.ps1 (Windows) |
your private OPS copy | Symlink ~/.claude/{skills,commands,agents,workflows}. Wire WORKFORCE/bin onto PATH. Wire the claude-wrapper shim. Initialize auto-memory git-sync. Schedule daily backup. Install Claude Code plugins. Seed the trust anchor. |
Rule: anything OPS-specific lives in Stage 2. linuxploitacious clones the repo and hands off — it does not configure OPS itself.
On Linux, Stage 1's OPS routine ends with bash ~/OPS/.claude-config/deploy.sh.
The operator does nothing extra.
Run on a fresh machine. Source: https://github.com/Exploitacious/linuxploitacious (public).
The installer lives at ~/linuxploitacious/ on both Linux and Windows —
outside the OPS/PROJECTS/ convention because Stow + matching symlink layouts
expect it as a home-folder sibling. See its README for the full reason.
Linux:
curl -fsSL https://raw.githubusercontent.com/Exploitacious/linuxploitacious/main/shellSetup.sh | bashIn the menu, pick:
SSHKEY(provisions a GitHub key +gh auth— required before the OPS clone: your OPS copy is a private repo, so the clone can't see it without an authenticatedgh)AI Harness(theHARNESSmenu item — clones your private OPS copy to~/OPS; if you don't have one yet, it offers to create one from theExploitacious/OPStemplate)- Anything else relevant (zsh, omp, btop, etc.)
Windows: Run from an elevated PowerShell. Same source repo.
Set-ExecutionPolicy Bypass -Scope Process -Force
.\winSetup.ps1In the checkbox menu, toggle on:
SSHKEY(provisionsgh auth login— required before the OPS clone)AI Harness(theHARNESSmenu item — clones your private OPS copy to~/OPS, then auto-invokes Stage 2)- Anything else relevant (PS7, WezTerm, OMP, Claude Code, etc.)
After Stage 1 completes with the AI Harness item selected, the installer
automatically invokes Stage 2 — bash ~/OPS/.claude-config/deploy.sh on Linux,
& "$HOME\OPS\.claude-config\deploy.ps1" on Windows — once the clone/sync
finishes. No additional command needed.
Auto-invoked by Stage 1 on both Linux and Windows (when the AI Harness menu
item is selected). Manual invocation is only needed when:
- You deselected the
AI Harnessitem and want to invoke it later - You pulled updates and want to re-run idempotent setup
- Stage 1's auto-invoke failed and you're recovering
Linux / macOS (bash):
bash ~/OPS/.claude-config/deploy.shWindows (PowerShell):
& "$HOME\OPS\.claude-config\deploy.ps1"Both scripts are idempotent — safe to re-run anytime.
In order:
-
Symlink
~/.claude/{skills,commands,agents,workflows}→ the matching OPS directories.skills→SKILLS/; the other three →.claude-config/{commands,agents,workflows}/. Any real dir already at a target is backed up, not clobbered. This is the single source for Claude Code's skills, slash commands, sub-agent definitions, and workflow scripts. -
Wire
WORKFORCE/binonto PATH.chmod +xevery non-.ps1script in that dir, then export the dir ahead of existing PATH by writing to the untracked~/.zshrc.localand~/.bashrc.localseam. It writes to the.localfiles, not~/.zshrc/~/.bashrcdirectly, because those are Stow symlinks into the public linuxploitacious repo — appending there would either dirty that repo or be wiped on its nextgit pull --ff-only. Result:ac-status,ac-msg, and the rest of theac-*helpers are globally callable in new shells. -
Wire
claude-wrapper.shinto the invoking user's and root's shellrcs. The wrapper handles two problems that appear whenclauderuns under both a regular user and root on the same host: (a) Claude Code refusesbypassPermissionsas root, so the wrapper injects--permission-mode autoat the CLI level; (b) concurrentclaudeprocesses across two UIDs corrupt shared state, so the wrapper holds aflockon~/.claude/.instance.lock. Root's rcs are wired only if passwordless sudo is available. -
Wire the always-on ultracode shim (untracked
~/.<shell>rc.localseam). Aclaude()shell function that launches interactive Claude Code with--settings '{"ultracode":true}'unless the caller already passed--settings. Kept out of the tracked repo and out of the flock wrapper on purpose — see the comment indeploy.shfor why. Idempotent via a marker. -
Initialize Claude Code auto-memory git-sync. Invokes
WORKFORCE/bin/ac-memory-init --auto-commit, which walks Claude's encoded cwd subdirs under~/.claude/projects/and symlinks each into.claude-memory/<host>-<encoded>/so per-session memory survives in this repo (and syncs to your other machines on push). -
Schedule daily backup. Installs a user crontab entry (
0 9 * * * ~/OPS/.claude-config/backup/daily-sync.sh). The sync script commits + pushes any uncommitted changes to the OPS origin (no-op if clean). -
Install Claude Code plugins. Adds the
JuliusBrussee/cavemanmarketplace and installs thecavemanplugin. A barebash deploy.shdoesn't source a login profile, so it probes known install locations (~/.local/bin, pnpm, npm-global,/usr/local/bin,/opt/homebrew/bin) to findclaude. -
Seed the trust anchor. Marks
$HOMEtrusted in~/.claude/.claude.json(hasTrustDialogAccepted: true) so Claude Code's parent-dir trust walk covers every repo under$HOMEwith no folder-trust prompt. Keyed off$HOME(never a hardcoded username); idempotent and atomic — merges into the existing.claude.jsonwithout touching other keys. If you run a second Claude Code profile viaCLAUDE_CONFIG_DIR, re-run against that config dir's.claude.jsontoo — it isn't covered automatically.
Windows runs single-user, so the pipeline is shorter: symlink
~/.claude/{skills,commands,agents,workflows}, wire WORKFORCE/bin onto
PATH via an untracked profile.local.ps1 seam (one per PowerShell edition,
under Documents/PowerShell and Documents/WindowsPowerShell) — never
$PROFILE itself, since $PROFILE is a shim/symlink tied to the public
linuxploitacious repo and deploy.ps1 refuses to append there — initialize
auto-memory git-sync (ac-memory-init.ps1), register a OPS Daily Backup
scheduled task, and install the caveman plugin.
Windows parity gap: the claude-wrapper remains Linux-only (a root/user
flock is meaningless on single-user Windows). The ultracode shim and the
trust-anchor seed ARE implemented in deploy.ps1 (steps 4-5) — parity holds
for those two.
After the script finishes:
# Skills should resolve — listing matches SKILLS/ contents
diff <(ls ~/.claude/skills/) <(ls ~/OPS/SKILLS/) # empty diff == correct
# WORKFORCE/bin should be on PATH after opening a new shell
which ac-status # Linux/macOS
Get-Command ac-status # Windows PowerShell
# Plugin should be registered (expect: caveman)
claude plugin list
# Backup scheduler
crontab -l | grep OPS # Linux
schtasks /Query /TN "OPS Daily Backup" # WindowsIf all succeed, deployment is complete. Open a new shell and use Claude Code normally.
Two pieces of OPS machinery ship in the repo but are not wired by the Stage 2 deploy scripts — they activate through Claude Code's config and systemd directly.
-
Hooks — the scripts in
.claude-config/hooks/(SessionStart briefing + foreman-charter injection, pre-compact snapshot, post-compact resume, handoff-check, memory-index, the context-watch escalation ladder (Stop + PostToolUse), and the secrets/git guards) are registered insettings.json, which is a Level 1 file owned by Stage 1 (linuxploitacious). Its hook entries point at these scripts by path, so the hooks come alive as soon as Stage 1 has deployedsettings.jsonand this repo is at~/OPS. Editing the hook scripts here takes effect immediately; adding a new hook means registering it in the Stage 1settings.json. -
systemd
ops-*timers —.claude-config/systemd/ships two user units:ops-verify.timer(nightly drift gate that runs.claude-config/bin/verify-ops.sh) andops-memory-gc.timer(weekly read-only memory-pool inventory viaac-memory-gc). Both write their reports to~/.local/state/ops/, whichsession-briefing.shsurfaces at session start. Enable them per-user:mkdir -p ~/.config/systemd/user ln -sf ~/OPS/.claude-config/systemd/ops-*.{service,timer} ~/.config/systemd/user/ systemctl --user daemon-reload systemctl --user enable --now ops-verify.timer ops-memory-gc.timer
linuxploitacious is the paved road, not a hard dependency. Stage 2 only needs
this repo cloned to ~/OPS and the Claude Code CLI installed. On any Linux or
macOS host:
git clone git@github.com:<you>/<your-name>.git ~/OPS
bash ~/OPS/.claude-config/deploy.shOn Windows:
git clone git@github.com:<you>/<your-name>.git $HOME\OPS
& "$HOME\OPS\.claude-config\deploy.ps1"What you give up by skipping Stage 1: the Level 1 Claude files (CLAUDE.md,
settings.json, statusline.sh) and the OS-level shell/dotfile setup. Provide
your own settings.json (that's where hooks register — see above), and Stage 2
handles the rest.
OPS is designed to run on several machines off one private repo. Clone the
same repo to ~/OPS on each host and run Stage 2 there. Auto-memory is the
shared surface: ac-memory-init symlinks each machine's Claude session-memory
dirs into .claude-memory/<host>-<encoded>/, and the daily backup cron commits
- pushes them. Pull on another machine and that machine sees the memory the
first one wrote. Doctrine, identity (
CONTEXT/), project lessons, and handoff batons ride the same git sync — every machine boots from the same durable state.
Pull latest, then re-run Stage 2:
cd ~/OPS && git pull --ff-only
bash ~/OPS/.claude-config/deploy.sh # or deploy.ps1 on WindowsStage 2 is idempotent — it no-ops where state is already correct.
Explicitly NOT handled by Stage 2 (deploy.sh / deploy.ps1):
- Level 1 Claude files (
CLAUDE.md,settings.json,statusline.sh) — these come from linuxploitacious (Stage 1). Editing them inside OPS does nothing. - OS-level shell config (
.bashrc,.zshrc, prompts, AI CLI installs) — Stage 1's responsibility. - The systemd
ops-*timers — shipped here but enabled manually (see "Hooks and the drift gate" above). - Project-scoped skills that live inside specific
PROJECTS/<org>/<repo>/.claude/skills/directories — those deploy with each project's own workflow. - Multi-agent runtime dirs (
WORKFORCE/FLEETPROJECTS/<project>/) — created on-demand by the Coordinator agent, not pre-provisioned.
If a new piece of OPS setup needs to run on every host:
- Add it to both
deploy.shanddeploy.ps1, with the same semantics. - Make it idempotent (check state before acting; no-op if already correct).
- Update this doc's "What Stage 2 does" list.
- Test on both OSes.
.claude-config/deploy.sh— Linux/macOS Stage 2 entry point..claude-config/deploy.ps1— Windows Stage 2 entry point..claude-config/backup/daily-sync.sh— backup script invoked by the scheduler..claude-config/hooks/— SessionStart/pre-compact/guard hooks (registered via Stage 1settings.json)..claude-config/systemd/—ops-verify+ops-memory-gctimers..claude-config/bin/verify-ops.sh— the drift gate run byops-verify.timer.SKILLS/README.md— what gets symlinked into~/.claude/skills/.WORKFORCE/bin/claude-wrapper.sh— root/user flock + permission-mode shim (Linux).WORKFORCE/bin/ac-memory-init,ac-memory-init.ps1— auto-memory git-sync.Exploitacious/linuxploitacious— public Stage 1 repo (dotfiles + host provisioning).