██████╗ █████╗ ██████╗
██╔════╝ ██╔══██╗██╔══██╗
██║ ███╗███████║██████╔╝
██║ ██║██╔══██║██╔═══╝
╚██████╔╝██║ ██║██║
╚═════╝ ╚═╝ ╚═╝╚═╝
GAP is one set of rules that every coding agent on your machine follows. You, or an agent, write the rules once in global/AGENTS.md. The gap sync command copies them into the file each agent already reads.
- Go 1.24 or newer (
go version) - Go's bin folder on your
PATH:$(go env GOPATH)/bin, orGOBINif you set it
Run this once from the repo root:
go install ./cmd/gapCheck that your shell finds it:
gap --helpgap publishes the checkout it was installed from, so it runs from any folder. Run go install ./cmd/gap again after cmd/gap changes or after you move the repo. To skip installing, run go run ./cmd/gap sync from the repo root.
| Command | What it does |
|---|---|
gap sync |
Publish global/AGENTS.md and each global/commands/*.md, install missing skills, and check skills for drift |
gap sync --remove-codex-legacy |
Same, then strip the old policy copy from ~/.codex/AGENTS.md |
gap status |
Show each component's pin, its store copy, and skill drift. Writes nothing; exits 1 on drift or a missing store |
gap restore [id] |
Back up and replace missing or drifted component skills at the locked commit, reusing a verified store copy |
gap pin <id>@<ref> |
Resolve a branch, tag, or commit and write it to gap.lock. The only command that moves a pin |
go test ./... |
Run the self-test against throwaway folders |
gap.toml selects the pinned component skills, including Do-Skills. global/skills.json selects additional skills by source. In that manifest, "source": "." means a skill in this repo's skills/ folder; any other source is a GitHub owner/repo, optionally followed by a subdirectory path.
[
{ "source": "kunchenguid/gh-axi", "skills": ["gh-axi"] }
]gap sync installs missing listed skills by running skills.sh (npx -y skills add <source> -g -y -a claude-code -a codex -s <name>, repeating -s for each missing skill). It goes into ~/.agents/skills, which Codex, Cursor, Pi, and OMP read, and skills.sh links it into ~/.claude/skills for Claude Code. You need Node for installations. If an installation is needed but npx is unavailable, sync still publishes the policy, skips the installation, and exits 1.
Sync also checks every installed managed skill against its source: the pinned store for a component, this repo's folder for "source": ".", or the hash skills.sh recorded for another manifest source. That hash is a Git tree id or a SHA-256 of the skill files; sync compares the same kind. A lock entry whose source is not the selected source is reinstalled after a backup, without asking, only when both destinations still match the recorded hash. This also migrates unedited Do-Skills copies previously installed from the GAP checkout to the component store. An edited copy, or a hash sync cannot check, is reviewed like other drift, against the selected source. For each drifted skill, it shows the diff and asks:
kkeeps the installed copies for this run. Later runs check them again. An incomplete installation still exits1.ttakes the source. Sync backs up both destinations under~/.gap/backups/, then reinstalls. A failed install restores the previous destinations.sskips it for now. Sync exits1.
With no terminal to ask in, sync lists the drifted skills, including a foreign lock that was not migrated, changes none of them, and exits 1. Skills selected by neither gap.toml nor global/skills.json are never touched.
To add a skill from an existing component, add its name to that component's names array in gap.toml. For another source, add an entry to global/skills.json. Then follow the merge and publish steps.
For a direct skills.sh install that does not use gap.lock, run npx skills add edheltzel/Do-Skills -s <skill-name>.
A component is a repo that ships many skills, pinned to one commit. gap.toml names each component and its skills, and gap.lock records the exact commit and tree:
# gap.toml
schema = "2"
[components.do-skills]
repo = "github.com/edheltzel/Do-Skills"
ref = "master"
names = ["do-commit", "do-todo"]gap pin do-skills@master writes the requested ref, resolved commit, and tree to gap.lock. If a branch and tag share a name, use refs/heads/<name> or refs/tags/<name> to choose one. gap sync fetches the locked commit into ~/.gap/store/<id>/<commit>/, checks its tree against the lock, and installs the skills from there through skills.sh, so Node is still needed. Sync never moves a pin. A component missing from gap.lock stops the sync and names the gap pin command to run. gap.toml and gap.lock pass the same merge gate as global/.
gap status reports component skills only, using the locked ref when pinned. gap restore do-skills repairs that component's missing or drifted skills without asking; omit the id to repair all components. Restore backs up replaced copies and rolls them back if installation fails. A verified store avoids fetching the component again; skills.sh may still need network access.
Do these steps every time you change the rules.
Make your change in global/AGENTS.md. Longer notes it links to live in global/docs/agents/. Keep one policy for every agent; don't add a policy file for a single agent.
Commit the edit, merge it to master, and pull. gap sync publishes only merged rules. It refuses when:
- you're on a branch other than
masteror GitButler'sgitbutler/workspace - you're in a git worktree
global/,skills/,gap.toml, orgap.lockdiffers fromorigin/HEAD, including untracked files. An ignored file blocks only when skills.sh would install it, or when gap reads it (global/AGENTS.md,global/skills.json, top-levelglobal/commands/*.md,gap.toml,gap.lock). Ignorednode_modulesand.DS_Storedo not block.
gap syncIt prints each file it wrote:
~/.claude/rules/global-agent-policy.md
~/.pi/agent/APPEND_SYSTEM.md
~/.omp/agent/APPEND_SYSTEM.md
~/.codex/gap/global-agent-policy.md
~/.cursor/rules/global-agent-policy.mdc
~/.empryo/EMPRYO.md
~/.omp/agent/commands/state-docs.md
~/.claude/commands/state-docs.md
Policy files are the shared rules. Each top-level global/commands/*.md is a slash-command prompt, copied unchanged as a whole file to OMP and Claude Code. See how sync handles existing files.
The bundled /state-docs prompt writes project maps under docs/state. See the prompt for arguments and local Nessa docs requirements.
Each is a regular file, not a symlink. Start a new session in any agent to load the new rules.
gap sync asks before it appends the policy to a foreign file without a complete GAP marker pair, or replaces a foreign command file or a symlink. An empty file without a matching ledger entry also needs confirmation.
- Type
y. For a policy file, sync copies it under~/.gap/backups/and appends the policy between<!-- gap:begin -->and<!-- gap:end -->. The rest of the file stays, so the harness keeps loading it. For a command file, sync backs up the old contents and replaces them. For a symlink, sync retains the link and writes a regular file. If that replace stops before the move, the empty recovery folder is removed. In those replacement cases, the harness stops loading the old file. The backup keeps the bytes only. - Later policy syncs replace only that block and do not ask again. When the live file already equals the publish, sync leaves it in place. Otherwise it moves the previous file or symlink under
~/.gap/recovery/<dest-id>/<timestamp>/(<dest-id>is the destination path relative to$HOME) and prints that path. If the destination is on another filesystem, the previous file is copied with its mode, fsync'd, and verified, then the live destination is re-checked and only then removed. A destination that changes or disappears during that copy is refused, and the rest of the sync continues. Recovery stays out of harness rule directories. Leftover.<filename>.gap-recovery-*directories next to a managed destination are moved into~/.gap/recovery/<dest-id>/and the new path is printed. A failed move prints a warning and leaves the folder in place. GAP keeps the 3 newest regular copies per destination. A displaced user symlink is kept and is never pruned. A moved leftover.<filename>.gap-recovery-*folder is never pruned. A prune failure prints a warning and does not fail the sync. Concurrent edits, including writes through an already open file, are kept in that recovery copy; if those bytes are no longer in the live file, sync prints a warning with the recovery path. Installation refuses to overwrite a file created during publication and names the recovery path. On failure, sync restores the previous file if the destination is still absent; otherwise both versions remain available, and a failed restore is printed with that path. Existing regular files keep their access permissions. Command files stay whole-file copies ofglobal/commands/. - Rules you want every harness to follow still belong in
global/AGENTS.md. Text outside the policy markers stays in that file. Command edits belong in the matching source underglobal/commands/.
Any other answer leaves that file alone. With no terminal to ask in, such as an agent's shell, sync prints Refusing to replace foreign file, skips that file, publishes the rest, and exits 1. Run gap sync yourself in a terminal to answer.
Codex has no rules folder. The first gap sync adds two hooks to ~/.codex/hooks.json, SessionStart and SubagentStart. They print ~/.codex/gap/global-agent-policy.md into every Codex session and subagent.
-
Open Codex and run
/hooks. Trust both GAP hooks. -
Start a session and a subagent. Confirm both see the policy.
-
If
~/.codex/AGENTS.mdstill holds an old copy of the policy, remove it:gap sync --remove-codex-legacy
The flag refuses unless both hooks were already in
hooks.jsonbefore that run. On a clean machine it reports that there's nothing to strip.
GAP uses ~/.codex unless you set CODEX_HOME. An empty CODEX_HOME counts as unset. A relative path is refused.
Grok has no file of its own. It reads the Claude copy when Claude compatibility is on, which is the default.
grok inspectLook for ~/.claude/rules/global-agent-policy.md in the global rules list. If it's missing, turn Claude compatibility back on. Don't add a Grok file. A second copy would load the policy twice.
Empryo reads ~/.empryo/EMPRYO.md, which already holds SoulStack's block. The first gap sync asks once to append the GAP block there, so run it from a terminal and type y. SoulStack's block and anything else in the file stay as they are.
Empryo injects every skill in skills.autoLoad in full into every prompt. GAP recommends three: do-todo (the policy tracks tasks every turn), do-commit and but-axi (the GitButler flow). Everything else loads by name when a task needs it. Until your pins match, gap sync prints the exact settings call. Ask an agent inside Empryo to run it. gap never writes Empryo's config.
gap sync also warns when ~/.empryo/skills/<name> differs from a managed skill. Empryo loads that copy first, so the managed one never runs there.
Skills live in skills/<scope>/<group>/<skill>/. Regenerate this table with bash scripts/update-readme.sh.
Useful in any repo, or none.
| Group | Skills | Coverage |
|---|---|---|
| Core | 12 | Stack-agnostic foundations - review lenses, reasoning, testing, simplification, agent-legible repos, comments, design patterns, skill authoring, and AI-writing cleanup. |
| Workflow | 15 | Change delivery - the brainstorm, plan, work, debug, and ship loop (LFG), commits, docs, GitHub projects and stacks, PR workflow and review triage, worktrees, and technical writing. |
| Operations | 4 | Operating AI agents - delegation, handoffs, prompt audits, and project status check-ins. |
| Content | 3 | Audience-facing media - illustrations, diagrams, images, and visual explanations. |
| Personal | 4 | Personal extras you want everywhere - teaching, todo.txt, isometric explainer figures. |
Pay off only when a project uses that stack or product.
| Group | Skills | Coverage |
|---|---|---|
| TypeScript | 4 | Language-wide TypeScript - standards, refactoring, and type-driven design. Install alongside frontend, backend, or tooling. |
| Frontend | 7 | Web frontend - Astro, CSS, React effects, design systems, UX flows, cleanup, and review. |
| Backend | 1 | Backend engineering - Effect (TypeScript) services and code, and review for Effect, Go, Python, and Rust. |
| Tooling | 3 | CLIs and lightweight tools - TypeScript CLI scaffolding, zero-dependency patterns, and performance profiling. |
| Swift | 3 | Swift and Apple platforms - macOS desktop apps, iOS review, and cleanup. |
| Product | 1 | Product marketing - positioning, SEO and GEO discovery, launches, conversion, and retention. |
| Marketing | 9 | SEO marketing on the Ubersuggest MCP - keyword research, site audits, competitors, content briefs, AI visibility, and action plans. |
| Personal | 1 | Personal extras for specific projects - recipe diagrams. |
| Agent | File it reads |
|---|---|
| Claude | ~/.claude/rules/global-agent-policy.md |
| Pi | ~/.pi/agent/APPEND_SYSTEM.md |
| OMP | ~/.omp/agent/APPEND_SYSTEM.md |
| Codex | ~/.codex/gap/global-agent-policy.md, printed by the SessionStart and SubagentStart hooks |
| Cursor | ~/.cursor/rules/global-agent-policy.mdc |
| Empryo | ~/.empryo/EMPRYO.md, beside the SoulStack block. The first sync asks once, so run it from a terminal. |
| Grok | No file of its own. It reads the Claude copy. |
The sync contract defines ledger fingerprints, legacy-file recognition, and Cursor frontmatter handling.
Claude searches parent folders for CLAUDE.md. A file at ~/CLAUDE.md sits above every project, and it stops Claude from reading that project's AGENTS.md. The Claude rules file above already covers every project.
go test ./...The test builds gap inside a throwaway clone of this repo and runs it against a temporary home folder. It never touches your real agent files. It exits 0 when every check passes.

