Skip to content
hathachPublic

About

Agent-neutral personal config for skills, commands, and hooks shared by Claude Code and Codex

Resources

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

agentrc

License: MIT Build Greptile: The War on Bugs CodeRabbit Pull Request Reviews

Personal agent config shared by Claude Code and Codex, versioned in git. It runs on Linux and Windows, both tested in CI; Windows lists its setup and the Linux-only parts. macOS is not supported.

install.py     symlinks chosen parts of this checkout into ~/.claude, ~/.codex and ~/.local/bin, or a project's .claude
instructions/  user.md, the user-wide instructions
CLAUDE.md      instructions for work in this repository
AGENTS.md      -> CLAUDE.md, for Codex
skills/        skills for both agents
agents/        <name>.md for both agents, plus <name>.toml for Codex
hooks/         Claude Code hooks, one folder each with a hooks.json
workflows/     saved workflows (Claude only)
bin/           commands for the human, such as lsusbt
statusline/    the Claude Code status line, its Codex usage fetcher and its session-PR helper
lib/           helpers the scripts share through their resolved path: windows.py (Windows runtime), greptile.py
tests/         unit tests for skill scripts, hooks and the installer
bench/         benchmark corpora and their runners; data too large for git is a test-data release asset

Install

Every entry is a symlink, so edits are live. Each flag takes every entry of its kind; nothing is installed by default. For use in a project, install into its work tree (Per project):

git clone git@github.com:hathach/agentrc.git ~/code/agentrc
~/code/agentrc/install.py install --project ~/code/tinyusb --skill --agent --workflow

The user-wide install is the owner's personal mode; its --claude-md makes instructions/user.md, the owner's own rules (hosts, sudo, document library), every session's instructions:

~/code/agentrc/install.py install --skill --agent --workflow --bin --claude-md --statusline
~/code/agentrc/install.py remove --skill
  • --skill: into ~/.claude/skills and ~/.codex/skills. A skill's hook of the same name links into ~/.claude/hooks and its events are registered in ~/.claude/settings.json (backed up the first time).
  • --agent: the .md into ~/.claude/agents and ~/.codex/agents, the .toml, if any, into ~/.codex/agents.
  • --workflow: into ~/.claude/workflows.
  • --bin: each bin/ command into ~/.local/bin, which must be on PATH. lsusbt is lsusb -t with device names, serials and the HIL board each serial belongs to; its header lists the options.
  • --claude-md: instructions/user.md as ~/.claude/CLAUDE.md, and ~/.codex/AGENTS.md to it.
  • --statusline: the statusline/ files into ~/.claude, and statusLine in ~/.claude/settings.json; install refuses if another one is set. The command names $HOME/.claude, so another CLAUDE_CONFIG_DIR can reuse it.

The target directories stay real, so local entries sit beside the links. install refuses before touching anything if a target is a file or a nonempty directory; remove never deletes one, and leaves CLAUDE.md links that point elsewhere. Rerun after adding an entry: dead links into this repo are pruned.

On a host that ran the former install.py install --herdr-plugin, run herdr plugin unlink agentrc.worktree-children once to drop herdr's registration of the removed plugin.

Project repos such as tinyusb call these skills by bare name and expect this install, user-wide or per project.

Per project

--project <dir> installs the Claude Code entries into one work tree instead, and touches nothing under ~/.claude, ~/.codex or ~/.local:

~/code/agentrc/install.py install --project ~/code/tinyusb --skill --agent --workflow
~/code/agentrc/install.py install --project ~/code/tinyusb/.worktrees/<branch> --skill --agent --workflow
~/code/agentrc/install.py remove --project ~/code/tinyusb --skill --agent --workflow
  • <dir> must be a repository's or a linked worktree's top level. Claude Code finds project skills, agents and workflows only up to the work tree root, and reads project settings, hooks and env included, only from the directory the session starts in, so install every worktree a session starts in, and start sessions at its top level.
  • Skills, a skill's hook, agents (the .md only) and workflows link into <dir>/.claude/{skills,hooks,agents,workflows}. The hooks are registered in <dir>/.claude/settings.local.json, never the tracked settings.json, by absolute path as user-wide, with env.AGENTRC_DIR, this checkout. The agentrc paths the agents, workflows and skill recipes name are "${AGENTRC_DIR:-$HOME/.claude}/..." ($HOME/.codex for Codex), so they resolve to this checkout in a project install and to the user-wide links otherwise; a model that reads one by path expands it in the shell first. A skill names a sibling skill's page relative to its own directory, its symlink resolved, which reaches this checkout either way. captain is the exception (below).
  • --claude-md is opt-in: it links instructions/user.md, the owner's personal rules, as <dir>/CLAUDE.local.md, Claude Code's per-user instructions for that project, so those rules and the co-words hold in that work tree only. A link, because an @-import of a file outside the project loads only once approved interactively, never in a headless session.
  • Each link, and settings.local.json unless git already ignores it, gets a line in one marked block of the repository's .git/info/exclude, which its worktrees share; a line stays while any worktree has our link there, so git status stays clean everywhere. remove drops the links, our hooks and AGENTRC_DIR (once no link of ours is left), the file once it is empty, and the lines no worktree needs. Since the block is shared, a project's own untracked file at the path of our link in another worktree is hidden from git status too.
  • A destination the repository tracks, or a real file or nonempty directory there, such as the project's own skill of the same name, refuses before any change; --skip-clashes leaves it and skips that agentrc entry (and the hook registration with a skipped hook, and a skipped skill's hook, whose link and registration from an earlier install go, unless every selected entry is skipped: then install changes nothing). A directory left with only __pycache__ from a skill the project has since moved out is named as such; delete it. Pruning leaves a tracked link alone.
  • Both actions refuse a symlinked directory between <dir> and a destination, .claude itself included, which would carry them out of the work tree, and a settings.local.json that is tracked or a link, since they rewrite it.
  • Project mode is Claude Code only: --bin and --statusline are user-wide and refused with --project, and Codex's own skills and agents have no project install. A Codex coworker needs no CLAUDE.local.md: cowork sends it its rules.
  • A user-wide install on the same host is not supported beside a project install: the hooks are registered twice and race, and the headless-chief shadow check misjudges links into a different checkout. A project install leaves a user-wide one in place; remove it as its own step, install.py remove with the user-wide flags and no --project.
  • Not supported with a project install either:
    • captain, whose guard and scripts are user-wide only;
    • worktrees of one repository installed from different agentrc checkouts;
    • install or remove runs at the same time in worktrees of one repository;
    • bench/finding-verify replay fingerprints, which read the user-wide install.

Windows

Claude Code on Windows runs the Bash tool, hooks and the status line through Git Bash (or MSYS2), and the scripts run on native Windows from there. They read and write UTF-8 themselves, so no -X utf8 or PYTHONUTF8 is needed. Bash's PATH needs:

  • a python3 that is a native CPython 3.12+ (python.org's install manager or MSYS2's mingw build), not the WindowsApps alias and not MSYS2's /usr/bin/python3, for the workflows' script calls and the simplify gate's wrapper; PyYAML, pytest and pytest-xdist in it for the tests, and PyYAML for pr-babysit's harvest.py, which pr-review also imports, to date a head by its pull_request runs (without it they go unread and the harvest says so). install.py registers a Python hook as '<the python that ran install.py>' '<hook>.py', since a hook that cannot start lets the tool call through; rerun it after replacing that Python, and name the interpreter the same way in the credit guard's recipe below.
  • bash, cygpath and git from Git for Windows or MSYS2: from Python a bare bash is System32's WSL launcher, so scripts look it up on PATH. chief_guard.py maps /tmp-style paths through cygpath and denies them without it; it also denies cmd, PowerShell and wsl outright.
  • poppler's pdfinfo and pdftotext for read-doc (MSYS2 mingw-w64-x86_64-poppler); pdftotext is taken from beside pdfinfo, so Git's xpdf earlier on PATH does not matter.

Claude Code sets NoDefaultCurrentDirectoryInExePath; set it too when running the scripts from another shell, or Windows runs a git.exe or gh.exe from the current directory, a PR checkout for many of them.

pr-babysit's build check puts a temp directory into the build command unquoted, so it refuses a temp dir with a blank or shell syntax in it, as a profile path such as C:\Users\First Last gives; point TMP and TEMP at a plain path such as C:\tmp there.

install.py creates symlinks, which needs Developer Mode or an elevated shell. Not on Windows yet; #148 has the skipped tests and notes for a port:

  • hil's rig-host scripts (hil_lock.py, hil_reserve.py, usb_observe.py, hil_process.py, hil_farm_check.py, hil_tt.py, hil_usbtest.py) and usb-kernel-recover's usb_recover.sh: POSIX flock and fork, /proc and sysfs.
  • hil's hil_report_notice.py locks the report writer with msvcrt on Windows, but its tests and hil_report_core.py's use fcntl and skip there.
  • lsusbt: sysfs.
  • usb-kernel-debug: kernel dynamic debug and usbmon, which have no Windows counterpart to port to.
  • the capture scripts of usb-sniffer (sniff.py) and target-debug (etm_capture.py, dual_capture.py): usbfs, /proc and POSIX process control.
  • target-debug's sysview_record.py, which refuses there: it drives SystemView under Xvfb and tracks its processes through /proc and process groups.
  • target-debug's esp_session.py, which refuses there: it detaches openocd as a POSIX session and identifies it through /proc (#130).

The hardware-debug scripts run on Windows but are unverified on a board there (#130), and their tests skip the cases that drive POSIX stub executables or ptys. Other skips are for /proc, permission bits or file names NTFS refuses.

Skills

Agent collaboration and PRs:

Skill Use it to Start with
cowork hand the other agent (Codex or Claude) a task, question or review headless, in resumed lanes cowork.py send --lane L --task -
simplify-gate switch the per-repository Codex YAGNI check at Stop (below) /simplify-gate status|on|off
pr-reply post PR review replies from a manifest, read each back, resolve verified review threads reply.py --pr N --manifest f.json
followup-issue open a follow-up issue, or comment new evidence on one that covers the topic, read it back, never post twice publish.py create --repo o/r --allow-repo o/r --title t --body-file f
pr-babysit the fact collectors the pr-babysit workflow runs before it commits a fix preflight.py --pr N, hooks.py <path>...
pr-review review someone else's PR in one request, such as /pr-review N auto-post boards: <ids>: pin it, run the pr-review workflow on the boards named (none by default), leave a pending review for you to submit (or submit it under auto-post); re-review after a push prepare.py --pr N
ci-rerun read a failed CircleCI job's or Read the Docs build's log, rerun it once after an infra failure circleci.py log <job>, rerun <job>..., rtd.py log <url>
headless-chief launch a headless chief and follow its status lines while it runs, its report when it exits chief_run.py --out <dir> --worktree <wt> --task-file <task>
worktree-remove remove a worktree when its task is done, run from outside it: check it, gate on pending follow-ups and PR state, close the hc terminals working in it when run from one, then git removes it and scripts/worktree_children.py its clean <name>--* siblings and its branches whose commits are on their remote or a PR head worktree_remove.py [PATH] --dry-run [--pending <item>], --proceed <token>

Hardware documentation, from the Calibre library at ~/Documents/calibre-library (CALIBRE_LIBRARY overrides):

Skill Use it to Start with
read-doc look up hardware manuals, specs and errata in Calibre instead of answering from memory search.py --kind reference-manual stm32h7
download-doc fetch vendor datasheets, manuals and errata into the library, refresh stale revisions sync.py st --family STM32H7 --types datasheet,errata (dry run; --apply imports)

Board designs, from KiCad schematics in hathach/pcb, or EAGLE and KiCad schematics in a directory you name:

Skill Use it to Start with
read-pcb answer board-wiring questions (which pin, net, part or revision) from the schematic pcb.py find --in <dir> <board>, then net <sch> <net>

Firmware footprint; the project supplies the launcher, its builds and its CI baseline through the Code size section of its Build contract::

Skill Use it to Start with
code-size measure flash/RAM per board, file, section or symbol, and diff it against a base ref or CI's stored snapshots <launcher> diff -b <board>

Firmware and USB debugging on real hardware; target-debug maps which one answers what, and the project supplies the build variant and board locks through its Build contract: and HIL contract: lines:

Skill Use it to Start with
hil run a project's HIL tests and judge their results: contract sections, board locks, verdicts, retries; a farm check (each board's probe on the bus and resetting it), never a test verdict the project's HIL contract: page
arduino the mechanics an Arduino library's build and HIL contracts share: arduino-cli builds against its checkout, prepared dependencies, the HIL build record, the harness and its remote runs; how a further library adopts them the library's Build contract: and HIL contract: pages
hil-farm-setup provision or update a Debian 13 HIL farm host: CI runners, pinned flashing tools, udev rules, runner firewall, from a root-owned checkout and the host's file in hosts/ setup.sh install --host hosts/<host>.toml --inputs DIR, then verify.sh
target-debug explain what the firmware did: GDB autopsy, faults, RAM trace, PC sampling, SWO, ETM instruction trace (J-Trace + Ozone), SEGGER SystemView scheduling profiles; Espressif chips over USB-Serial-JTAG or pad JTAG with openocd-esp32 (read its espressif.md first) pc_sample.py --probe <uid> --device <dev> --interface swd --speed 4000 --elf <elf>
rtt console or log over a debug probe (SEGGER RTT), live or post-mortem rtt.py --backend jlink --probe <serial> --device <dev> --interface swd --speed auto --seconds 20
usb-kernel-debug capture Linux host URBs (usbmon); kernel dynamic debug on a host or gadget usbcap.py <vid:pid> 10; sudo usb_dyndbg.sh on|off usbcore xhci_hcd
usb-sniffer what crossed D+/D- with the ataradov sniffer, where usbmon cannot see sniff.py capture raw.pcapng --port <port> --seconds 30

Simplify gate (per repository)

hooks/simplify-gate snapshots the checkout, the worktrees nested in it and its <checkout>--* sibling worktrees (linked worktrees of any repository; the user rules reserve that prefix for them) when a prompt arrives and when the session stops, then sends the diff to a read-only codex exec YAGNI challenge at Stop: at most two rounds per user turn, one retry on Codex failure, then the stop goes through with a notice. One review runs at a time; edits it did not cover wait for the next turn. The challenge notes that a peer sharing the checkout may have made part of the diff, and Claude rejects findings on files it neither wrote nor commissioned.

--skill registers the hook; switch the gate on per repository with /simplify-gate on in a Claude session there, or:

cd ~/code/tinyusb && ~/code/agentrc/skills/simplify-gate/scripts/gate.py on

The hook costs one git rev-parse per checkout and starts the gate only where <git common dir>/simplify-gate exists, so one marker covers a repository and its worktrees. /simplify-gate status prints the state with the effective model and effort; on --model M --effort E overrides the defaults at the top of simplify_gate.py. Session state lives under ~/.cache/agentrc/simplify-gate/.

Credit guard (per config dir)

hooks/credit-guard keeps a session on an account with extra usage enabled from spending credits. At session start it asks /api/oauth/usage once whether credits are enabled; if not, it stays off for that session. Otherwise it blocks the prompt, or denies the next tool call and ends the session, once any plan limit reaches 100%. Usage is re-read on a window that shrinks with headroom and burn rate (HEADROOM in credit_guard.py, tuned from credit-guard/readings.log); a read that fails, or a payload it cannot parse, keeps the last reading, and blocks only when there is none. It cannot stop a model call already running, a subagent's next turn, or a hook that Claude Code kills on timeout, and it does not know the session's model.

install.py does not register it. Register it per config dir that needs it (~/.claude-ada here); the recipe refuses a dir that already has it:

d=~/.claude-ada; ! grep -q credit_guard.py $d/settings.json && mkdir -p $d/hooks &&
ln -sfn ~/code/agentrc/hooks/credit-guard $d/hooks/credit-guard &&
jq --arg c "$d/hooks/credit-guard/credit_guard.py" --slurpfile h ~/code/agentrc/hooks/credit-guard/hooks.json \
  '.hooks = reduce ($h[0] | to_entries[]) as $e (.hooks // {}; .[$e.key] += [$e.value[] | .hooks |= map(.command = $c)])' \
  $d/settings.json > $d/settings.json.new && chmod --reference=$d/settings.json $d/settings.json.new &&
mv $d/settings.json.new $d/settings.json

Chief session

agents/chief.md is the orchestrating main session: it reads and runs short chores itself, and every worktree edit, long build or investigation and review goes to the repository's agents, skills and workflows. hooks/headless-chief/chief_guard.py denies chief's own direct git/gh writes, workflow-only publishers and writes into its worktree, a guard against mistakes, not isolation: it judges only the command families it knows, and a program outside them passes by design, even one that runs its arguments as a command. Its direct Codex exchanges go through agents/coworker.md, the cowork.py transport. workflows/code-audit.js is its saved review: one code-verifier per directory x dimension, then finding-verifier on every finding (on Sonnet for a scanner-labelled nit) (args: { dirs, dimensions, diff? }, the first two required; diff: { base, head } narrows it to a change); the verifier sets each finding's level by the one severity scale in agents/finding-verifier.md's Severity section. workflows/pr-review.js reviews a pinned PR head through it. Start it inside the task worktree:

~/code/agentrc/install.py install --agent --workflow --skill
git worktree add .worktrees/<branch> -b <branch> <base> && cd .worktrees/<branch> && claude --agent chief

From a linked checkout the task worktree is its sibling <checkout>--<branch> instead (instructions/user.md, linked checkouts).

Headless, launch it through the headless-chief skill, which runs claude -p --agent chief and gives the caller chief's status lines while it runs and its report when it exits.

A branch whose .claude/workflows or .claude/agents entries differ from the same-named agentrc ones installed would run them in their place, since project scope wins; merge the default branch first. The launcher refuses such a worktree, and the headless-chief hook (--skill registers it) denies every Workflow and Agent call in it, interactive sessions included; the chief prompt itself loads before any tool call and is not covered.

For headless PR publishing, follow agents/chief.md's Authorization exception: the human's request to launch chief to babysit a PR, their yes to the offer, or their request to open a new PR under the user-wide rule, is the grant, with no further question. Include that message verbatim (with the offer it answered when it is a yes, and any later restriction), the PR resolved from it (repository, PR URL, head repository and branch, expected HEAD, worktree) and the task scope, and, when a pr-babysit launch continues an earlier chief's run, that run's last reported stateRef. The task scope names the review scope: the whole PR, or takeover-only (earlier PR commits are context, the takeover HEAD is the task base) for a task limited to the repairs it commissions; an offer to launch names that choice when the human has not, and an unspecified review scope defaults to the whole PR; ask only when the PR, repository or worktree is ambiguous. A relaunch for the same PR task copies the grant verbatim from the earlier launch task, without asking again, until the task is done, the PR changes, or the human narrows or withdraws it; a later restriction rides along verbatim. A merge is granted only by the human's own words asking to merge that PR, under chief's merge exception: the task then also names the base branch and carries Merge grant: yes — "<the words>", else Merge grant: none, a line a relaunch copies too. A PR review submits under its own PR review exception, as skills/pr-review/SKILL.md asks it, an auto-fix review with the babysitting of its repairs; its pending reviews need no grant, under chief's standing exception. Leave the checkout to chief until it exits.

Captain session

agents/captain.md is one seat over the other Claude Code sessions in this host's hc terminals (Codex terminals are left out): it lists them, opens new ones on existing checkouts under either account, asks the idle ones for status, reports one table and carries the human's own words, read from its own transcript, to the sessions they name. It checks what sessions report with read-only git and gh lookups and the files sessions and chiefs leave, and keeps its own memory, but never does the sessions' work; the one thing it publishes is a follow-up issue in hathach/agentrc for a defect in its own tooling, on the human's word. Its guard, skills/captain/scripts/captain_guard.py, declared as a hook in the agent file so it applies on every account, names in its docstring the calls it allows. It reads sessions from hc terminals, each pinned to the session Claude Code registered under its process's account config dir, because sessions run under more than one account (CLAUDE_CONFIG_DIR), each shown with the account its process runs under. Start it in its own terminal:

~/code/agentrc/install.py install --agent --skill
claude --agent captain

A watch has sessions report at the end of each task, new and restarted ones too, until the captain session that started it ends. It types into no session: hooks/captain, installed with the captain skill, lets a covered session join it at its next Stop or after a compaction or /clear, and costs a Python start and one marker-file check while no watch is open. Another config dir (~/.claude-ada here) needs the hook's two entries from hooks/captain/hooks.json in its own settings.json, as the credit-guard recipe above does for its own; a session started before its settings held them joins only once restarted.

Its memory is shared across accounts: an account whose config directory is not ~/.claude links its agent-memory there, as the ada account does with ln -s ~/.claude/agent-memory ~/.claude-ada/agent-memory; without that link the guard denies its memory writes.

Tests

python -X utf8 -m pytest -n auto --dist worksteal -p no:cacheprovider -q tests

Needs PyYAML, pytest and pytest-xdist; BUILD.md is the full contract. The pre-commit hook (pre-commit install) runs the same command with -n 8, well inside pr-babysit's 540 s hook deadline; CI shards it across jobs (.github/workflows/tests.yml), and --shard I/K (tests/conftest.py) runs one part of it. Where Git for Windows has core.fsmonitor=true (an installer option), each temporary test repository leaves a git fsmonitor--daemon behind; run the suite with GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.fsmonitor GIT_CONFIG_VALUE_0=false.

About

Agent-neutral personal config for skills, commands, and hooks shared by Claude Code and Codex

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages