{"id":51160692,"url":"https://github.com/benthamite/agent-sync-template","last_synced_at":"2026-06-26T13:01:57.774Z","repository":{"id":356780085,"uuid":"1234022048","full_name":"benthamite/agent-sync-template","owner":"benthamite","description":"Toolkit for keeping Claude Code and Codex instructions, skills, and hooks synchronized.","archived":false,"fork":false,"pushed_at":"2026-05-16T18:28:36.000Z","size":67,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-16T20:21:25.654Z","etag":null,"topics":["agent-tools","ai-agents","claude-code","codex","configuration","developer-tools","dotfiles"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benthamite.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-09T16:43:36.000Z","updated_at":"2026-05-16T18:23:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/benthamite/agent-sync-template","commit_stats":null,"previous_names":["benthamite/agent-sync-template"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/benthamite/agent-sync-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benthamite%2Fagent-sync-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benthamite%2Fagent-sync-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benthamite%2Fagent-sync-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benthamite%2Fagent-sync-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benthamite","download_url":"https://codeload.github.com/benthamite/agent-sync-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benthamite%2Fagent-sync-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34817641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-tools","ai-agents","claude-code","codex","configuration","developer-tools","dotfiles"],"created_at":"2026-06-26T13:01:56.010Z","updated_at":"2026-06-26T13:01:57.762Z","avatar_url":"https://github.com/benthamite.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Agent Sync Template\n\nThis is a small toolkit for keeping Claude Code and Codex configuration in sync without requiring a particular directory layout. It is a companion to my note [How I keep Claude Code and Codex in sync](https://stafforini.com/notes/how-i-keep-claude-code-and-codex-in-sync/).\n\nThe setup agent infers where your Claude files live and where your Codex files live, records those paths in a small config file, ports missing counterparts, and registers hook commands that remind or block an agent when it edits only one side.\n\nIt does not install sample skills. It does not require symlinks. It does not assume that your configuration lives inside this repository.\nIt is directory-agnostic: the bootstrap must implement the same synchronization contract using the user's existing topology, not a hardcoded dotfiles layout.\n\n## Quick start\n\nGive Claude Code or Codex this prompt:\n\n```text\nSet up Claude Code \u003c\u003e Codex synchronization using https://github.com/benthamite/agent-sync-template.\n\n- Clone the repository to a sensible local location if it is not already available.\n- Run its smoke test before touching my live Claude or Codex configuration.\n- Then follow BOOTSTRAP.md exactly.\n```\n\n## What the agent writes\n\nThe generated `ai-config-sync.json` maps your real Claude and Codex files. It will look roughly like this, but with paths inferred from your machine:\n\n```json\n{\n  \"global\": {\n    \"file_pairs\": [\n      {\n        \"name\": \"global instructions\",\n        \"claude\": \"~/.claude/CLAUDE.md\",\n        \"codex\": \"~/.codex/AGENTS.md\",\n        \"normalizer\": \"instructions\"\n      }\n    ],\n    \"skill_roots\": [\n      {\n        \"name\": \"global skills\",\n        \"claude\": \"~/.claude/skills\",\n        \"codex\": \"~/.codex/skills\"\n      }\n    ],\n    \"hook_roots\": [\n      {\n        \"name\": \"global hooks\",\n        \"claude\": \"~/.claude/hooks\",\n        \"codex\": \"~/.codex/hooks\"\n      }\n    ]\n  },\n  \"project_local\": {\n    \"project_roots\": [],\n    \"instruction_pairs\": [\n      {\n        \"name\": \"project-local instructions\",\n        \"claude\": \"CLAUDE.md\",\n        \"codex\": \"AGENTS.md\",\n        \"mode\": \"sibling\",\n        \"normalizer\": \"instructions\"\n      }\n    ],\n    \"skill_roots\": [\n      {\n        \"name\": \"project-local skills\",\n        \"claude\": \".claude/skills\",\n        \"codex\": \".codex/skills\"\n      }\n    ],\n    \"hook_roots\": [\n      {\n        \"name\": \"project-local hooks\",\n        \"claude\": \".claude/hooks\",\n        \"codex\": \".codex/hooks\"\n      }\n    ],\n    \"registration_pairs\": [\n      {\n        \"name\": \"project-local hook registrations\",\n        \"claude\": \".claude/settings.json\",\n        \"codex\": \".codex/hooks.json\"\n      }\n    ],\n    \"mcp_servers\": []\n  }\n}\n```\n\nThe `global` section points to specific files and directories on your machine.\n\nFor `project_local`, the agent should make one of these choices and explain it in its summary:\n\n```text\nKeep the defaults   if project-local files use sibling CLAUDE.md/AGENTS.md\n                    and .claude/ / .codex/,\n                    or if you do not currently use project-local files.\nEdit the paths      if your project-local files use different directories.\nSet arrays to []    if you never want project-local checks.\n```\n\nKeeping the defaults does not create any directories and does not require you to change existing projects. It just says what the guard should do if a future edit or commit touches one of those paths inside the current git repository.\n\nThe default convention means:\n\n```text\nPROJECT/CLAUDE.md             \u003c-\u003e PROJECT/AGENTS.md\nPROJECT/path/CLAUDE.md        \u003c-\u003e PROJECT/path/AGENTS.md\nPROJECT/.claude/skills/      \u003c-\u003e PROJECT/.codex/skills/\nPROJECT/.claude/hooks/       \u003c-\u003e PROJECT/.codex/hooks/\nPROJECT/.claude/settings.json \u003c-\u003e PROJECT/.codex/hooks.json\nPROJECT/.mcp.json             \u003c-\u003e PROJECT/.codex/config.toml  (optional MCP wiring)\n```\n\nThat means: if an agent edits `PROJECT/CLAUDE.md`, the guard expects `PROJECT/AGENTS.md` to be edited too; if it edits `PROJECT/docs/CLAUDE.md`, the guard expects `PROJECT/docs/AGENTS.md`. Likewise, if an agent edits `PROJECT/.claude/skills/foo/SKILL.md`, the guard expects the corresponding `PROJECT/.codex/skills/foo/SKILL.md` to be edited too. If the project has no local agent files, nothing happens. If the project has only one side and you edit it, the guard will ask you to port the counterpart or disable/change the `project_local` rule.\n\n`project_roots` is an optional bootstrap/audit backstop. Add repositories or parent directories there when setup inventories existing project-local files; `bin/ai-config-sync audit` will then scan those roots for existing `CLAUDE.md`/`AGENTS.md`, `.claude/skills`/`.codex/skills`, `.claude/hooks`/`.codex/hooks`, and hook registration pairs. Leave it empty if you only want project-local enforcement when an agent edits or commits inside a project.\n\n## Ported files\n\nThe setup agent should port your existing skills and hooks so both tools have counterparts:\n\n```text\nClaude skill root / NAME / SKILL.md  \u003c-\u003e  Codex skill root / NAME / SKILL.md\nClaude hook root / HOOK              \u003c-\u003e  Codex hook root / HOOK\nClaude instructions                  \u003c-\u003e  Codex instructions\nProject CLAUDE.md                    \u003c-\u003e  sibling AGENTS.md\n```\n\nThe two sides do not need to be byte-for-byte identical. Claude and Codex may need different frontmatter, hook payload parsing, or registration syntax. They should implement the same behavior.\n\n## MCP servers\n\nMCP server configuration is wiring, not a semantic peer artifact like a skill. If both Claude Code and Codex should expose the same MCP-backed service, add an entry to `mcp_servers` so the audit can check the mechanical invariants:\n\n```json\n{\n  \"name\": \"asana\",\n  \"claude_config\": \"~/work/project/.mcp.json\",\n  \"codex_config\": \"~/work/project/.codex/config.toml\",\n  \"claude_server\": \"asana\",\n  \"codex_server\": \"asana\",\n  \"compare_public_config\": true,\n  \"required_env\": [\"ASANA_ACCESS_TOKEN\"],\n  \"identity_check\": \"scripts/check-asana-mcp-identity.sh\"\n}\n```\n\nThe audit checks that both tools define the server, that public command/args/transport/url fields match when `compare_public_config` is true, and that required environment-variable names exist. It never prints or compares secret values. Use `identity_check` for service-specific account checks such as confirming that an Asana token resolves to the expected email address.\n\n## Audit\n\nRun:\n\n```sh\nbin/ai-config-sync audit\n```\n\nUse `--config` if your mapping file lives elsewhere:\n\n```sh\nbin/ai-config-sync --config /path/to/ai-config-sync.json audit\n```\n\nThe audit checks global instruction pairs, skill roots, hook roots, registration pairs, and configured MCP server pairs. It reports missing counterparts and content drift after basic tool-specific normalization.\n\n## Hook commands\n\n`BOOTSTRAP.md` asks the setup agent to register the hooks for you. This section shows the commands that need to end up in your Claude and Codex hook configuration:\n\n```sh\n/path/to/agent-sync-template/hooks/require-commit-sync.sh\n/path/to/agent-sync-template/hooks/remind-claude.sh\n/path/to/agent-sync-template/hooks/remind-codex.sh\n```\n\nIf your `ai-config-sync.json` is not in the toolkit repo, the registered hook command should set `AGENT_SYNC_CONFIG`:\n\n```sh\nAGENT_SYNC_CONFIG=/path/to/ai-config-sync.json /path/to/agent-sync-template/hooks/require-commit-sync.sh\n```\n\nThe reminder hooks run after edits. The commit guard runs before `git commit` and blocks one-sided commits, including already-staged changes, `git add ... \u0026\u0026 git commit ...`, `git -C ... commit ...`, and `git commit -a`. The same guard can also handle optional project-local files using the `project_local` section of the config, such as:\n\n```text\nCLAUDE.md              \u003c-\u003e AGENTS.md\npath/CLAUDE.md         \u003c-\u003e path/AGENTS.md\n.claude/skills/      \u003c-\u003e .codex/skills/\n.claude/hooks/       \u003c-\u003e .codex/hooks/\n.claude/settings.json \u003c-\u003e .codex/hooks.json\n```\n\nProject-local paths are resolved inside the git repository being committed. They are not global paths, they are not scanned across your whole filesystem, and they do not require changing projects that do not use project-local agent configuration.\n\n## Commands\n\n```sh\nbin/ai-config-sync audit\nbin/ai-config-sync inventory\nbin/ai-config-sync guard-commit \"git commit -m message\"\nbin/ai-config-sync remind --agent claude\nbin/ai-config-sync remind --agent codex\n```\n\n`inventory` prints the same missing or drifting pairs as the audit, but without framing it as a pass/fail check.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenthamite%2Fagent-sync-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenthamite%2Fagent-sync-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenthamite%2Fagent-sync-template/lists"}