psy documentation

Everything the install actually does: the exact steps per operating system, every file psy writes and where, the full command set, and how a project tunes the guard.

01Install

Install

Everything lands in your home directory — no sudo, no system paths, no runtime to install. The only tool the installer needs is curl or wget.

macOS

$ curl -fsSL https://psykit.psycray.com/install.sh | sh

Prefer to see what runs before it runs? Read install.sh — it is ~70 lines: it detects your OS and architecture, downloads psy-darwin-arm64 (or -amd64) from https://psykit.psycray.com/latest/, and runs psy install. It writes nothing the install itself does not write.

The launcher lands at ~/.local/bin/psy. If that directory is not on your PATH yet:

$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc

Linux

Same one-liner, same script — it picks psy-linux-amd64 or -arm64 for your machine:

$ curl -fsSL https://psykit.psycray.com/install.sh | sh

On bash, the PATH line goes to ~/.bashrc:

$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc

No package manager route exists yet; the alternative to the script is the same thing by hand — download a binary from the downloads list, then:

$ chmod +x psy-linux-amd64 && ./psy-linux-amd64 install

Windows

The curl | sh one-liner is POSIX-only, so Windows has two routes of its own.

1 · Automatic (PowerShell). One command downloads the right build for your machine and runs its installer:

PS> irm https://psykit.psycray.com/install.ps1 | iex

Prefer to see what runs first? Read install.ps1 — it detects your architecture (x86_64 or ARM64), downloads the matching .exe from https://psykit.psycray.com/latest/, and runs psy install. It writes nothing the install itself does not write.

2 · Manual (downloaded .exe). Grab the .exe for your machine from the downloads list, then run it from the folder you saved it in (use -arm64 on ARM):

PS> .\psy-windows-amd64.exe install

Either way the launcher lands at %USERPROFILE%\.local\bin\psy.exe, and install adds that directory to your user PATH for you — no manual setup: open a new terminal and psy is on it. If it was already on PATH the installer leaves it untouched, and uninstall removes only the entry it added.

Know how native Windows differs before you rely on it:

  • Skills are linked with symlinks, which Windows allows only with Developer Mode on (Settings → System → For developers) or an elevated prompt. The installer tells you when that is the blocker.
  • psy update works here too. A running .exe cannot be overwritten in place, so the old launcher is renamed aside and the new one takes its path; a later run sweeps the leftover.
  • Both x86_64 and ARM64 builds ship — pick the row that matches your machine.
i
Running your agents inside WSL? WSL is a Linux machine as far as psy is concerned: use the curl one-liner inside WSL, and it wires the agent configs in the WSL home. A native-Windows agent and a WSL agent are two environments — each needs its own install.
02Verify

Verify the install

Open a new terminal (so the PATH change is picked up) and run the doctor. It checks every host it wired and prints one line per check:

$ psy doctor
psy doctor — home /Users/you

common
  [PASS] canonical store: 10 skills
  [PASS] bundles: core
  [PASS] launcher: present and executable
  [PASS] binary in config: ~/.local/bin/psy runs

claude
  [PASS] skill symlinks: 10/10 alive
  [PASS] hook: secret + danger all present
  [PASS] statusline: present, at statusLine.command
  [PASS] attribution.commit: "" (set by psy)

The exit code counts FAIL lines only — a WARN (say, an agent that is installed but not on PATH) never fails the run. --json emits the same report for scripts.

To see the guard itself work, ask your agent to read a secret — e.g. “cat .env” in any project. The tool call is refused before it runs; that refusal is the install working, not an error.

03The file contract

What gets installed, exactly

This table is the complete list of what install and update write, and uninstall reads the same list back. The other commands write only where you point them or where their row under Commands says: switch the account files and the two Claude sign-in files, env set the .env file and a backup beside it, diagram deliver the page you name, init missing files in your repository. Configs are edited, never rewritten: your file is parsed, our block is inserted or replaced, and every other byte survives as it was. Each touched file is backed up first.

PathWhat it isHow it is written
~/.agents/skills/ the canonical skill store — real directories, one per skill, named psy-* created; skills copied in from the binary's embedded bundle
~/.agents/.skill-lock.json the lockfile: every skill installed and every link made, so uninstall knows exactly what is ours written each install
~/.local/bin/psy the launcher (.exe on Windows) — the binary everything else calls copied, made executable; skipped when already identical
~/.claude/skills/ Claude Code's skill directory one symlink per skill into the store
~/.codex/skills/ Codex's skill directory one symlink per skill into the store
~/.gemini/antigravity/skills/ Antigravity's skill root one symlink per skill into the store
~/.gemini/antigravity-cli/skills/ Antigravity CLI's skill root one symlink per skill into the store
~/.claude/settings.json secret + danger guards on PreToolUse, the status line, the attribution keys that keep agent trailers off commits and PRs (sessionUrl: false, commit: "", pr: ""), and the instructionFiles mode that has Claude Code read a project's AGENTS.md beside its CLAUDE.md JSON edited in place; our hook and status line entries carry our marker; an attribution key or the mode is written only when absent and recorded in the lockfile — one you already set is left alone
~/.claude/CLAUDE.md the base prompt — the general guidelines every agent gets one block at the end of the file between two marker lines that record the build and a checksum; everything outside the block is yours and is never rewritten; a block you edited inside is left alone and named
~/.codex/AGENTS.md the same base prompt for Codex the same marked block
~/.codex/hooks.json the same secret + danger guards for Codex JSON edited in place; our entries carry our marker
~/.codex/config.toml an optional [tui] status line a fenced comment block; skipped entirely if you already declare [tui]
~/.gemini/config/hooks.json secret + danger guards for Antigravity JSON edited in place; our entries carry our marker
~/.gemini/antigravity-cli/settings.json Antigravity's status line JSON edited in place; our entries carry our marker
~/.psy-backups/ a timestamped directory per run with a copy of every file about to be touched; runs are kept until you delete them created lazily, on the first real change

The hooks call ~/.local/bin/psy hook run <agent> <guard> — a local process with a timeout. The guard decides locally; the tool call never leaves your machine. The binary touches the network only when you ask it to — the install script, install's own downloads (external skills; --offline skips them), or psy update.

Install wires all three agents whether or not they are on the machine, creating the directories above if they are missing — a config written before an agent is installed is simply ready for it. doctor tells you which hosts it could actually verify.

04Reference

Commands

CommandWhat it does
psy install The whole setup, and the upgrade: places the launcher, syncs skills into the store, links them into every agent, wires hooks and the status line, writes the base prompt into each agent's instruction file, and has Claude Code read a project's AGENTS.md beside its CLAUDE.md. Safe to re-run — it reconciles in place. --from <dir> uses a local skill source instead of the embedded bundle; --offline skips external skills; --wire-only touches no skills at all — launcher, hooks and status line only; --with demo adds the n8n demo bundle and --without demo removes it, remembered across updates; --with comms adds psy-translate, the ;;vi / ;;en / ;;sl translation shortcuts.
psy update Downloads the latest build for this platform, compares it byte-for-byte with the launcher, and if it differs lets the new binary run its own install — so the launcher, the embedded skills and the wiring always move together. --check only reports. On native Windows the old launcher is renamed aside so a running .exe can be replaced.
psy doctor One PASS/WARN/FAIL line per check, per host. The exit code counts FAILs only. --json for scripts.
psy uninstall Prints exactly what it will remove and asks for the literal word yes (--yes skips the prompt). See Uninstall for what goes and what stays.
psy init Writes what a repository needs to work with the skills: AGENTS.md (the routes and the rules every agent reads — no CLAUDE.md, Claude Code reads it itself), CONTEXT.md and docs/architecture.md to fill in, the scripts/verify-quick.sh and verify-full.sh checks, a dependency check, and a CI workflow that runs them. Creates only what is missing — a file that exists is kept as it is.
psy version The release tag this binary was built from (or the VCS revision for a local build).
psy hook run <agent> <guard> What the wired hooks execute — you never run it by hand. The secret and danger guards each decide one tool call, payload on stdin. The units an older install wired, tier and workflow, let every call through until psy install rewires the machine. Agents: claude, codex, antigravity.
psy statusline <agent> Renders the status bar from the session JSON the agent pipes in (claude and antigravity; Codex uses its own TOML status line instead). On Claude it also names the saved account ~/.claude is signed in as.
psy switch [<name> [--kill]] Keeps one ~/.claude for every Claude account and swaps only who it is signed in as, so sessions, memory and history stay in one place. save, import and list manage the saved accounts; see Multiple Claude accounts.
psy track <next|idea|add|status|check> Reads and moves the work on the tracker a project opts into with one Tracker: line in CLAUDE.local.md (a GitHub project or GitHub issues), through gh. Never deletes, archives or creates a project, field or label.
psy docs record <slug> Prints the path of a new decision record, docs/decisions/<date>-<slug>-<4 hex>.md (or under docs/adr/ when only that exists), so two branches recording the same slug still merge cleanly. Creates nothing.
psy env set <file> KEY=VALUE… Writes keys into an existing .env file without printing it — the one way through the secret guard. Says added or set per key, never whether a value changed, and backs the file up before overwriting.
psy diagram <validate|render|deliver|examples> Compiles a typed JSON description (architecture, flow, sequence, state, data) into one self-contained HTML page, naming every layout fault with the fields allowed to fix it. What the psy-diagram skill drives.
psy skills graph [--json] Prints who hands work to whom across the installed skills, from each skill's declared uses.
psy help [<command>] Usage for the tool or for one command.
05Customize

Guard & customization

The guard runs before every tool call and blocks two things by default: reads and writes of secret-shaped files (.env, keys, credentials) and destructive command segments (rm -rf, git rebase, …). A project tunes it with one optional file.

.aiguard.json — per-project guard rules

psy never writes this file; you create it in the project. The guard uses the nearest one, walking up from the working directory, and stops at the first it finds — a config in a subdirectory shadows the parent's. All four keys are optional arrays:

KeyEffect
deny Extra secret-file patterns to block, on top of the built-in list. Glob-matched (*.private, config/*.key).
allow Exceptions — files lifted out of the deny list, built-in defaults included. Weighed before deny.
dangerAllow Regexes; a whole command segment that matches one is let past the danger guard.
dangerDeny Commands this project rules out: objects with a pattern regex and the command to use instead, which the block hands the agent. It also matches past a VAR=…, sudo or path in front. Weighed before dangerAllow on the same command, but a wrapper dangerAllow lets past is not looked inside.
{
  "deny":        ["config/master.key", "*.private"],
  "allow":       [".env.ci"],
  "dangerAllow": ["^git rebase --continue$"],
  "dangerDeny":  [{ "pattern": "npm( .*)?", "use": "pnpm" }]
}

A broken .aiguard.json fails closed for everything the project loosened: the guard falls back to the built-in rules. The project's own dangerDeny bans are lost with it — the one way a broken file opens — while a single unreadable entry in an otherwise valid file blocks every command until it is fixed. And a first guard refusal in a fresh project is not a bug — it is the moment to add the one narrow exception the project needs.

!
Keep allow as narrow as the exception you need. .aiguard.json is itself on the default deny list — the file that binds the agent must not be editable by the agent — and a broad pattern like "allow": ["*.json"] re-exposes it along with your real secrets. psy doctor warns when it sees one.

Customizing — a project skill, never a copy

The skills are read only from what the installed kit ships. A project keeps no copy of them: a copy freezes at the version it was taken from and silently misses what the kit adds later. What a project needs of its own lives in plain files in the repo, versioned with the code — psy never updates them, and psy init writes one only when it is missing.

Project fileWhat it does
AGENTS.md The routes — which skill a kind of work goes through — and the rules every agent in the project reads. psy init writes the first one; from then on it is the project's to edit.
.aiguard.json The guard rules, exactly as above.
A skill directory in the project (Claude Code: .claude/skills/<name>/) Adds a project-only skill — a ticket column, a compliance section, a review step, a house protocol. The agent host reads it natively; psy neither installs nor rewrites those files. To have one stand in for a shipped skill, name it in the project's AGENTS.md routes in place of the shipped one. A same-named directory does not shadow a shipped skill — the host resolves personal skills over project ones, and psy installs at the personal level — and a shipped skill invoked directly by name still loads.

The product's own rules are not a kit file: CONTEXT.md at the repo root holds the terms and business rules in force, and one record per change lands under docs/decisions/, named by psy docs record <slug>; docs/architecture.md holds the modules and the data flow.

!
Customize in the project, never the installed copy. ~/.claude/skills/ and its siblings are the store's fan-out: every psy install or psy update rewrites what its ownership record lists, so an edit there is gone on the next update — reported only as refreshed. Project skills live in the repo, travel with git clone, and survive update, reinstall, and a fresh machine.

Multiple Claude accounts

One ~/.claude, several accounts: psy switch swaps only who it is signed in as, so sessions, memory and history stay in one place. Save the account you are signed in as, import the others from their old config dirs, then switch:

$ psy switch save main
$ psy switch import work --from ~/.claude-accounts/work
$ psy switch work --kill   # from a plain terminal
$ psy switch               # list; * marks the signed-in one

Each account is one file, ~/.claude-accounts/<name>.json, mode 0600, holding its refresh token; MCP tokens, per-project state and settings never move. Before a swap the outgoing account's rotated token is written back to its file and both host files are backed up. A swap refuses while a Claude Code session runs; --kill lists them, asks, ends them, swaps, and prints a claude --resume line for each. A swap also refuses when the signed-in account was never saved, and every subcommand refuses while CLAUDE_CONFIG_DIR is set — switch serves the one ~/.claude, not the per-directory route below. --kill runs from a plain terminal, not from inside a session. Not on macOS yet, where the token lives in the Keychain; --kill is not on Windows yet. Tokens rotate, so on another machine sign in there and save — never copy an account file across machines.

Or run one Claude account per directory by pointing the host at each with CLAUDE_CONFIG_DIR, and that account reads its skills and settings under that dir instead of ~/.claude — so without a step the whole layer, skills and guard both, is absent there. Install once from a shell where the variable is set, or name the dir explicitly:

$ CLAUDE_CONFIG_DIR=~/.claude-accounts/work psy install
$ psy install --claude-dir ~/.claude-accounts/work  # same, named outright

The dir is wired as a full Claude surface — skills, guards, status line — and recorded, so every later psy update keeps it wired from any shell with no re-declaration. psy doctor lists it under claude config dirs; psy uninstall unwires and forgets it, leaving that account's own settings untouched.

Environment variables

VariableRead byEffect
CLAUDE_CONFIG_DIR psy install, psy switch, psy statusline For install, a dir other than ~/.claude is declared and wired as an extra Claude surface (multi-account, above), then remembered across updates. While it is set, switch refuses and the status line leaves out the account name.
PSY_BASE_URL install script, psy update Download from your own host or mirror instead of the built-in site.
PSY_VERSION install script Fetch a tagged release directory instead of latest.

The status line has no config file; besides CLAUDE_CONFIG_DIR above, nothing tunes it. What you see is what ships.

06Update

Update

$ psy update          # download + reinstall if newer
$ psy update --check  # just tell me

There is no version negotiation: the latest build is downloaded and compared byte-for-byte with your launcher. Identical means you are current; different means the new binary is verified to run and then performs its own install — launcher, embedded skills, hooks and status line move as one, so an update can never leave wiring from one version pointing at skills from another.

  • Re-running the install one-liner is the same upgrade by another door — install reconciles in place.
  • Released binaries know their download site from build time; PSY_BASE_URL overrides it. A from-source build with neither refuses to guess and points you at the manual route.
  • Nothing updates by itself: no daemon, no background check. Updates happen when you run one of the two commands above.
  • On native Windows the running .exe cannot be overwritten in place, so update renames the old launcher aside and puts the new one at its path.
07Uninstall

Uninstall

$ psy uninstall

It prints the full plan first and proceeds only on the literal word yes. What it removes is read from the lockfile — the record of what install actually wrote — never guessed from directory contents.

Removed

  • Our marked blocks in every agent config and the base prompt block in each instruction file — the surrounding file is restored byte-for-byte.
  • The attribution keys install wrote, when the lockfile records them and they still hold the value install gave them.
  • The skill symlinks the lockfile recorded, each re-verified on disk before deletion.
  • Those skills from the canonical store, and then the lockfile itself.

Kept

  • The launcher ~/.local/bin/psy — reported as kept; delete it by hand.
  • All backups under ~/.psy-backups/, and the store directory itself.
  • Saved Claude accounts under ~/.claude-accounts/ — each holds a refresh token; delete them by hand if the accounts should go too.
  • A Codex feature flag install turned on (default_mode_request_user_input) — after install its bytes are indistinguishable from a setting you chose yourself, so uninstall leaves it.
  • Anything that is yours: skills you added, config blocks without our marker, files it cannot prove it wrote.