About set-up-machine
set-up-machine is an agent skill for installing and auditing selected harnesses and their configurations on a workstation. It compares machine state with rule tables and optional workstation-repository declarations, proposes a diff, and verifies both persisted setup and live behavior.
Key features
- 01Install or update selected harnesses
- 02Audit configuration and release freshness
- 03Generate a proposed machine-setup diff
- 04Back up files before approved changes
- 05Verify configuration and live harness behavior
Use cases
- 01Set up harnesses from workstation declarations
- 02Check whether tools match current upstream releases
- 03Review permission, hook, and plugin differences
- 04Verify harness behavior after configuration changes
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ yahyabedirhan/ skills --skill set-up-machineAfter installing, type/set-up-machinein Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
rules.jsonreferences/global-instructions.mdlive-check.mdnew-remote-machine.mdremote-machine.mdrule-table.mdworkstation.mdreferences/claude-code/cli.mdcloud.mddesktop.mdindex.mdreferences/codex/cli.mddesktop.mdindex.mdreferences/cursor/cli.mdcloud.mddesktop.mdindex.mdreferences/grok-bot/desktop.mdreferences/opencode/cli.mdopencode-plugin.jsreferences/pi/cli.mdpi-extension.tsscripts/cloud_bootstrap.pypre_tool_hook.pyscripts/setupmachine/__init__.pychecks.pycommands.pyscripts/setupmachine/harnesses/__init__.pyscripts/setupmachine/harnesses/claude_code/__init__.pyDetails
From the project READMESet up machine
Set up every selected harness and environment from its sources, as Harnesses lists them. Install or update each selected tool to the latest available version. Running the skill again is the audit: check configuration and freshness, then propose any required updates.
Sources
- Rule table,
rules.json. - Workstation repo,
<workstation-repo>, found through the pointer,~/.config/agents/source.md, which names the repository and its clone. Each of these files is optional:setup/instructions.md: the working agreement, the glossaries, which tool fills each role, and the personal workflow;setup/permissions.json: the personal permissions, rows each harness carries beside the rule table's;setup/installs.json: the skills, plugins and commands every machine gets;setup/harnesses.json: where each harness's instructions and declared config are. Every harness can have a declared config, such as its default model.
Outputs
- Shared global instructions file,
~/.config/agents/AGENTS.md, which every harness reads; its personal parts come fromsetup/instructions.md. - Harness instructions, for a harness whose
instructionsfilesetup/harnesses.jsonnames: Cursor's~/.cursor/rules/harness-instructions.mdcand Claude Code's~/.claude/rules/harness-instructions.md, asreferences/workstation.md> 'Harness instructions file' describes. - Each harness's permission entries, from the rule table and the personal permissions.
- The pre-tool hook,
scripts/pre_tool_hook.py, run before every tool call. - The pointer, when it is missing or records none.
- Each harness's declared config, from the
configfilesetup/harnesses.jsonnames for it: only the keys that file sets are merged into the harness's own config file, asreferences/workstation.md> 'Declared config' describes, and every other key stays the user's. - The skills and commands
setup/installs.jsonlists for this machine, and its plugins in each harness it names: the plugins setup area, a plugin bundle or a standalone MCP server per entry. - Memory off in every harness.
Rules for every run
- Reproduce installation choices, configuration and steps, not a snapshot of software versions. Use the latest stable release, or the latest default branch for repository-distributed code. Apply this to harnesses, skills, plugins, packages and command-line tools.
- Record installed versions and source commits as audit evidence, not future installation targets. Keep historical citations and dependency lockfiles separate from machine setup choices.
- Check the current upstream source before calling an installation up to date. A matching declaration or an existing executable proves presence only. Report unavailable freshness checks as
gap. - Keep every entry the rule table didn't produce, as it is: it's the user's.
- Harness formats change: check the docs a harness reference links before writing. Where they differ, follow the docs and name the difference in your report.
Parameters
<skills-repo>: the user's own skills repo on GitHub, asowner/repo. Installed only when the workstation repo has nosetup/installs.json.<workstation-repo>: the user's own repo for their personal agent setup, usually private, asowner/repo.<path-to-workstation-repo>: where that repo is cloned, e.g.~/code/agent-setup.<session-host>: where agent sessions run, such asherdr. The live check starts a session of each harness there.
Steps
-
Inspect.
- Harnesses and tools: find each selected one, compare its installed version with the latest source, and read its current documentation. Preserve its installation channel.
- Harness references: read each one, then the non-secret configuration fields it needs. Leave credentials, authentication stores and runtime state unread. Inspect each harness's optional
configas its reference directs. - Shared file and pointer: read both. When the pointer names a workstation repo, read
references/workstation.md, thensetup/instructions.md,setup/permissions.json,setup/installs.jsonandsetup/harnesses.jsonin<path-to-workstation-repo>, then each filesetup/harnesses.jsonpoints at. - Codex and Pi: use one resolved Codex config home, and one resolved Pi agent folder, throughout setup and verification.
- When the pointer is missing: read
references/workstation.md, then ask the user once for<workstation-repo>and<path-to-workstation-repo>, or whether they have none. The diff writes the pointer, recording "none" too, so the next run doesn't ask. - When the pointer records no workstation repo: set up the machine as
references/workstation.mdsays for a run without one, or, when the user names one now, as it says for rewriting the pointer. - When the machine is remote or headless, such as a VPS: read
references/remote-machine.mdfirst. - When the harness is Grok Bot: its instructions go into memory, not files. Follow
references/grok-bot/desktop.md> 'Instructions' for apply, reconcile and audit. - When trying a change without touching the real machine: run the steps against a copy of the home folder in the project's
.scratch/, and check it withverify.py --home <copy>. Start no harness there, since it would read the real login.
-
Propose one diff that brings each harness in line with the rule table, the shared file's shape and personal parts, the pointer, memory off, the hook wired, each harness's declared config, and the installs: every skill and command
setup/installs.jsonlists for this machine, each selected installation up to date. Include harness upgrades and plugin updates, not only missing installs. Give every harness found its own section, listing each gap its reference names and the hook's blind spots. Readreferences/global-instructions.mdwhenever a global instructions file is in the diff.Work out what each harness should hold, from
rules.json, the personal permissions, the shared file's shape and the harness's reference, and compare it with what the machine holds. Write the whole diff: per harness and file, the exact entries or a unified diff, each line marked:added;tightened, a stricter entry added where it wins;removed;updated, an installed harness, skill, plugin or tool behind its latest source;present;wiredfor the hook;foundfor the MCP tools a row matched;stricter: the machine is stricter than the table, kept;extra: neither the table's nor a personal permission's, kept;ignored: an installed skillsetup/installs.jsonleaves out on purpose, kept, never installed or removed;gap: what the harness can't express;none: no such feature;personalbeside the mark of every entry a personal permission produced, such asadded, personalorpresent, personal, so the user can tell those entries from the table's and never reads them asextra;n/a, not applicable, for a personal permission whose tool this harness lacks, with why: nothing is written for it there.
Use
removedonly for memory files, a harness's own global file whose every line is already in the shared file (it becomes a link), and this skill's own wiring that points at an old script. An entry a dropped row, the table's or a personal one, left behind isextra, for the user to remove.- When the workstation repo has
setup/installs.json: the diff holds a line per entry that applies to this machine, and ann/aline, with why, per entry that doesn't. The file is the whole list: install nothing it leaves out.references/workstation.mdgives the format, the checks and the commands. - Without the file, with a
<skills-repo>value: the diff installs that repo's skills globally:npx --yes skills add <owner>/<repo> -g -a codex -a claude-code -y, leaving out-a claude-codewhen~/.claude/skillsis a link to~/.agents/skills. Compare the installed skill hashes with the source's latest default branch, asreferences/workstation.mddescribes. Presence in the lock file alone does not prove freshness. With no value, it's anoneline.
-
Ask once for one approval of the whole diff. A change after that needs a new approval.
- When the diff holds only
addedandupdatedinstalls fromsetup/installs.json: write it without asking, since the user approved that list by writing it.
- When the diff holds only
-
Back up every file the diff changes or removes. Before writing any file, copy each one into
~/.config/agents/backups/<UTC time as YYYYmmddTHHMMSSZ>/, at its path relative to the home folder, such as.claude/settings.json. Copy a symlink as a link. -
Write exactly the approved diff. Run any install command first, such as a skill the diff installs. If one fails, stop subsequent writes and report what changed before the failure. Check configuration and guardrail compatibility after upgrades; report failures rather than making an old snapshot the desired state. In JSON and TOML files, change only the keys the diff names and keep the rest. Write through a symlink to its target.
-
Verify with
python3 <skill-folder>/scripts/verify.py, where<skill-folder>is this skill's installed folder. It needs Python 3.9+ and the standard library only, and it writes nothing. The run passes when Verification holds. Then inspect again until the diff changes nothing: no pendingadded,updated,tightenedorremovedline. Report the backup folder, what's wired, the gaps, and the stricter and extra entries.The verifier checks persisted configuration, not upstream releases. Complete the freshness checks from Inspect before you report that every installation is the latest.
-
Live check each harness, as references/live-check.md says. Start a real session of each harness in
<session-host>, with its default model, give each the same probe in a throwaway folder, and read the disk and the screen. The verifier proves the files; the live check proves each harness acts on them. Fix eachFAILthrough steps 2 to 6, then check that harness again. The run is done when the verifier and the live check both pass.- When
<session-host>can't start a session of a harness: name that harness's live check as agap.
- When
Verification
Two global lines, then one table, then one detail line per FAIL and gap cell:
setup ok <owner>/<repo>: the shared file matches setup/instructions.md
rules ok 38 rows and 2 personal permissions, 197 samples
harness instructions guardrails sandbox plugins skills config
Claude Code ok ok n/a ok ok ok
Cursor FAIL ok ok ok ok ok
FAIL Cursor instructions: ~/.cursor/rules/global-instructions.mdc is stale; generate it again
setup: the pointer and everysetup/file are readable, and the shared file carries whatsetup/instructions.mdgenerates.n/awhen the pointer says there is no workstation repo.rules: every sample of every rule-table row and personal permission gets its row's answer from the hook.- A row per harness present. Each cell is
ok,FAIL,gapwhen the harness can't do it or the check can't see it, orn/awhen the area doesn't apply. The columns:instructions: the harness reads the shared file, its own instructions fromsetup/harnesses.jsonwhen named, and memory is off;guardrails: the hook is wired to an installed script, and each personal permission has its native entry where the harness has native rules;sandbox: the harness's sandbox and approval settings: Cursor's run mode, and the sandbox and approval keys of the declared Codex config;plugins: eachsetup/installs.jsonplugin for this harness is installed and enabled;skills: each skillsetup/installs.jsonlists is in a folder this harness reads skills from;config: each key the harness's declared config sets has the same value in the harness's config file;n/awithout a declared config. It isn't a setup area: a default model is none of the five.
The run passes when no line or cell is FAIL; the verifier then exits 0. Report each gap line. Each harness reference's 'Verification' section says what its row checks.
Rule table
rules.json holds each global rule once, by meaning, not in any harness's form. Each field of a row, each match kind, and the spellings are in references/rule-table.md. Each harness reference turns a row into that harness's entries. The hook and verify.py read the same rows through scripts/setupmachine/rules.py, which refuses a malformed row. A workstation repo's setup/permissions.json holds personal permissions in the same format, which every harness, the hook and verify.py take beside the table's, as the section references/workstation.md > setup/permissions.json describes.
Pre-tool hook
scripts/pre_tool_hook.py is the hook every harness runs before each tool call. It needs Python 3.9+ and the standard library only. It reads rules.json, and the personal permissions through the pointer, and checks the call:
- deny rows: it reads a command the way the shell runs it, and checks file tools, redirects and MCP tools too. It refuses the call, naming each refused part with its rule's reason and instruction.
- allow-and-report rows: it writes one JSON line per call to
<report-folder>/<date>.jsonl, readable by the user alone; the harness's permissions decide. - ask rows: the harness's native ask entries do the asking. A row with
approver: "user"is refused instead where the call shows that no one will ask the user: Claude Code inauto,dontAskorbypassPermissionsmode, Codex with approval policynever, and every call under Cursor and opencode, which can't promise a prompt, as their references explain. Cursor has no ask list at all, so there the hook refuses every ask row,approveror not. Pi has no ask list either, but its extension can ask: with a UI, the hook names the ask rows and the extension asks the user in Pi's dialog; without one, the hook refuses every ask row, asreferences/pi/cli.md> 'Hook wiring' describes. - allow rows: it says nothing; the harness's native allow entries let the call run without a prompt.
A malformed personal file leaves the table's rows in force: the hook skips the personal permissions and verify.py reports the file.
Wiring
Each harness reference shows its wiring with <script> in place of the script's path. Replace it with the absolute path of scripts/pre_tool_hook.py in the installed skill, under ~/.agents/skills or ~/.claude/skills, never in a checkout or worktree, which can be deleted. If there is no installed copy, name the gap. verify.py fails wiring whose script is elsewhere.
The wiring fails open, written as [ -f <script> ] && … || true or in the form a harness reference gives: if the script is gone, each call goes on under the native entries. Without it, python3 would exit 2 on the missing script, which blocks every call. Pi is the exception: it has no native entries underneath, so its extension fails closed and refuses each call the hook can't check, as references/pi/cli.md > 'Hook wiring' describes.
<report-folder> is ~/.local/state/agents/reports, or report_dir in ~/.config/agents/hook.json.
Limitations
Name these once in every audit:
- a command inside a script file or another interpreter (
python -c); - a command built from variables (
$cmd -rf x); - an alias or function defined elsewhere;
- an abbreviated long option (
--recur); - a force push by refspec (
git push origin +main); - a glob the shell expands (
cat .env*); - an automatic reviewer, such as Codex's
auto_review, answering anapprover: "user"row, since the call's payload doesn't show the reviewer; - each row's own
gap.
Harnesses
The one list of the harnesses and environments this skill sets up. The id is what setup/harnesses.json, installs.json, verify.py --harness and the hook's --harness take; an environment is what --env and environments in setup/harnesses.json take. Which of them a person uses is the workstation repo's.
| Harness (id) | Environments | Reference |
|---|---|---|
Claude Code (claude-code) | cli, desktop, cloud | claude-code/index.md for what the CLI and the desktop app share, then cli.md, desktop.md, cloud.md |
Codex (codex) | cli, desktop | codex/index.md for shared setup, then cli.md or desktop.md |
Cursor (cursor) | cli, desktop, cloud | cursor/index.md for what the CLI and the desktop app share, then cli.md, desktop.md, cloud.md |
Grok Bot (grok-bot) | desktop | grok-bot/desktop.md: instructions live in memory; permissions, connectors, secrets and routines live in the app |
opencode (opencode) | cli | opencode/cli.md |
Pi (pi) | cli | pi/cli.md: the hook's extension is its only enforcement |
A harness with environments has references/<harness>/<env>.md and scripts/setupmachine/harnesses/<harness>/<env>.py. One without has references/<harness>.md and one module. To add a harness or an environment, add its row here, as docs/design/set-up-machine.md > 'Extensibility' describes.
References
- references/global-instructions.md: the shared file's shape, the roles table, the working agreement and glossaries, what counts as personal workflow, moving a harness's own file, and why memory stays off.
- references/rule-table.md: a row's fields, its
matchkinds, and the spellings a harness needs. - references/workstation.md: what this skill reads from the workstation repo: the pointer, the
setup/files, the filessetup/harnesses.jsonpoints at, and their format, generating the shared file's personal parts and the personal permissions from them, the installs list, and the run without one. - references/live-check.md: starting a real session of each harness, the probe, and reading what it did.
- references/remote-machine.md: running agents on any remote or headless machine: signing in without a browser, per-machine settings, keeping sessions alive, and containing a misled agent.
- references/new-remote-machine.md: for a brand-new machine, first: the base it needs, done with the user as root, from key access, the dedicated user, keys-only SSH, the firewall, updates, swap and the docker group to the session host's integration, git credentials,
PATHover SSH and a headless browser.
- references/new-remote-machine.md: for a brand-new machine, first: the base it needs, done with the user as root, from key access, the dedicated user, keys-only SSH, the firewall, updates, swap and the docker group to the session host's integration, git credentials,
- One reference per harness environment, read for each one found: the Harnesses table lists them. Each page has the same headings: Presence detection, Instructions, Guardrails, Sandbox, Plugins, Skills, Config, Hook wiring, Limitations, Verification, and leaves out what its harness's
index.mdalready covers. A page whose facts were checked against a version or a test ends with Notes, which records that history so the sections above it state only current facts.
Scripts
- scripts/pre_tool_hook.py: the pre-tool hook every harness calls before each tool call.
- scripts/cloud_bootstrap.py: run from a Claude Code cloud environment's setup script; it leaves the agent the steps to set the session up from the workstation repo, as
references/claude-code/cloud.md> 'Bootstrap' describes. - scripts/verify.py: the check above. It writes nothing.
--home <folder>: checks a copy of a home folder, where a harness is detected by its folder only, not by a CLI onPATH.--grok-bot-rules: prints the rule table and personal permissions as Grok Bot's Auto-review rules, asreferences/grok-bot/desktop.md> 'Guardrails' describes.--harness <harness> [--env <env>]: keeps one harness's rows, or one environment's, such as--harness cursor --env cloud.--codex-trust-hash '<command>': prints thetrusted_hashCodex needs for a hook command, asreferences/codex/index.md> 'Hook wiring' describes.
scripts/setupmachine/: the modules both scripts use.harnesses/__init__.pylists every environment inALL. A harness is a package,harnesses/<harness>/, with one module per environment and the shared code in its__init__.py.- references/opencode/opencode-plugin.js: the opencode plugin that calls the hook.
- references/pi/pi-extension.ts: the Pi extension that calls the hook and asks the user for ask rows.