Standardising a setup without a mandate
Nobody on your team has the authority to pick everyone's harness. What actually travels in the repo, what needs each person's consent, and what only CI can enforce.
The mandate exists, and it isn't yours
You could say there's top-down control but you probably don't own the lever.
Vendors delivering actual enforcement layers:
Claude Code — the managed policy settings, marked as "cannot be overridden" in the precedence table and above anything a dev can set, as well as an organisation-wide
CLAUDE.mdfile which they deploy to a fixed path on every machineGitHub Copilot CLI — it allows you to pin the model, reasoning effort and context tier for each session started within a trusted repository
Cursor — has centralised rules administration from a dashboard, on Team and Enterprise plans
But… For all of these, there's a delivery channel which you might not own:
The managed
CLAUDE.mdfile? Claude Code docs say to distribute it using MDM, Group Policy or AnsibleThe repository pin in Copilot CLI? It only counts where the working directory has been trusted, and that happens on each person's machine
The centralised rules administration in Cursor? It's about a plan
So — Every lever is in the hands of whoever has the device management or a billing relationship, not in the hands of the dev who's seen three people in their team editing the generated migrations with their LLM for the last couple of weeks and wants to have it resolved.
What we should ask ourselves instead is: what can we put in the repository that will be useful for whoever shows up with whatever tool they have, without needing anyone's agreement?
The vendors started reading each other's files
Vendors started respecting each other's files — recently, there's been a convergence around filenames, not around tools.
AGENTS.md — we'd say it's read by 5 out of 6:
Codex — we've checked with
codex debug prompt-inputwhich outputs what the model sees as the prompt input in JSON, no call being made; there's the contents ofAGENTS.mdin thereCopilot CLI — it's mentioned in the help text, where they talk about the flag that disables custom instructions from
AGENTS.mdand related filesCursor — docs mention it for any subdirectory, saying it's used when the agent is working on files under it
Antigravity — parses
GEMINI.mdorAGENTS.mdat the root of the workspace on startupKimi Code — the same
Claude Code is the only exception here; they say explicitly in their docs they read CLAUDE.md, not AGENTS.md.
The same for the skills directories — they seem to work the same way:
We've created a
SKILL.mdfile with the same content and put it in three project directories, creating scratch directories for each of them, and rancopilot skill list. It showed us.github/skills/,.agents/skills/and.claude/skills/as project skillsCodex found the file in
.agents/skills/and.codex/skills/Cursor's docs are actually the most explicit here — they list
.claude/skills/and.codex/skills/under compatibility, saying it's because other vendors put their skills thereAnd GitHub go even further in their Copilot CLI configuration reference — they list
.claude/settings.jsonthere under cross-tool support as shared repository settings
But — It's not perfect, and the gaps define what you commit to your repo:
.agents/skills/— that's the most universal directory, read by 5 out of 6 (by everyone but Claude Code).claude/skills/— this one is accessible for 3: Claude Code, Copilot CLI and Cursor; not for Codex
If you create a scratch dir with a single AGENTS.md in it and put a marker file only in .claude/skills/, the marker won't appear in Codex's prompt input; the AGENTS.md from the same directory will be there.
One file of facts, and adapters with no facts in them
So: one file with the facts, and zero-one adapters per tool that needs a different filename.
Let's commit AGENTS.md to the root of the project, and create one adapter per tool that expects a different name.
For example — for Claude Code, you can create CLAUDE.md with an import pointing at AGENTS.md:
@AGENTS.mdIf you create a scratch dir and put AGENTS.md in it saying the project codename is BLUEHERON, and then run a headless version of Claude Code under it, asking it what the project name is, it'll answer with "BLUEHERON". If you create another scratch dir with a different project name, and no adapter, it'll answer with "NONE".
A symlink works too (ln -s AGENTS.md CLAUDE.md) but might require admin rights or Developer Mode on Windows, so the import is usually more bulletproof in mixed teams.
Just remember — an adapter can't contain any actual instructions; the day you append a real one to CLAUDE.md there are two sources of truth about your project, with the conflict being invisible and showing up only as two colleagues whose agents behave differently. Anything below the import should be about that tool, not your project.
Make sure you commit adapters for tools nobody uses yet — one line per file. Otherwise somebody will clone the repo with an unconfigured harness, won't find anything, will create their own file in their own place and the divergence will remain silent until it contradicts you in a review.
Facts travel, defaults negotiate, enforcement doesn't travel at all
There are three tiers of things that can be put into the repo:
Facts —
AGENTS.md, skills, rules; no per-person configuration; the only thing standing between "I committed my instructions" and "My colleague started a session" is their tool asking them if they want to trust an unfamiliar directory; works after agit pulland is almost free — and it's where most of the value isDefaults — allow-lists, model pin, MCP server list, hooks; an offer rather than an instruction, and any harness that supports a repo-level configuration file gates it:
Codex loads
.codex/config.tomlonly for trusted projects — we've created a scratch repo with it and rancodex doctor, and it showed us the user-level file but not the one from the project directoryClaude Code puts hooks and the settings that run them behind the workspace trust prompt
Copilot CLI has 14 keys in the repository level configuration and, as they put it, "any other keys — including keys that are valid in the user configuration file — are silently ignored"; model and reasoning effort pins work only when the working directory is trusted
If there's no repo-level config file at all — there's no tier two
Enforcement — doesn't travel:
Copilot CLI has a
disableAllHookskey that disables hooks in the repo and the user configuration fileClaude Code has
--safe-modeand--bareflags that start a session with hooks andCLAUDE.mddiscovery disabledCodex refuses to run a hook from the repo until you trust it
So be prepared some part of your team will run without your defaults at all times; make sure it doesn't cost them anything.
That's a feature, not a bug; it's sensible for pieces of software that run shell commands on other people's laptops. If sth needs to hold — make sure it's checked somewhere where devs aren't — in CI, as a required status check or as a server-side hook.
So — facts in the repo, gates in CI, let go of trying to enforce the middle layer.
Ship it as a fix, not as a policy
This is a "fix" for an incident, not a policy. Standards that work are PRs that resolve sth that's already happened:
If you create a PR that introduces an AI agent setup standard — it's a discussion; if you create one with 4 lines in
AGENTS.mdthe morning after your agent edited a generated migration by hand — it's one question, "Will this prevent it?", and mergeAlso — such a PR is honest about where it comes from; every line in a good instructions file is the residue of sth that went wrong once
It's about a repo and a person, and an incident. Not about a team and a vote. Just like the linter config — your setup will build itself through review.
Leave the escape hatch in the file, and write it down
Leave the "personal override" route in the file, but document it:
.claude/settings.local.jsonandCLAUDE.local.md.github/copilot/settings.local.json~/.codex/config.tomlPersonal skills directories under
~/.agents/skills/
And list these paths in your AGENTS.md, right next to the shared configuration.
People will adopt standards that come with opt-outs; they'll ignore ones that don't. The latter is worse, because they were ignoring it for a reason.
But be careful — if you run Claude Code under a repo and it creates .claude/settings.local.json in there, it adds that line to the global git excludes file on your machine, not to the repo's .gitignore. So make sure to add this line to the repo's .gitignore on day one so nobody can commit their personal overrides into other people's sessions.
In Claude Code
Configuration files specific to Claude Code — the files that are tracked within the repository and therefore available for everyone who clones it:
CLAUDE.md(or.claude/CLAUDE.md)Per-subject files placed in
.claude/rules/Skills stored in
.claude/skills/.claude/settings.json— as the documentation puts it, "settings that are checked into source control and shared with your team"
Individual-only files that are not versioned:
~/.claude/settings.json.claude/settings.local.jsonCLAUDE.local.md
If you're working on a project with a team using multiple tools, it might make sense to create CLAUDE.md with an import of AGENTS.md and append some Claude-specific instructions underneath. Like this (a small Markdown file):
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.Which tells Claude that it should be in plan mode for any changes made to the src/billing directory.
In general, before placing anything in version control, it's important to keep in mind:
Everything that runs — hooks, as well as some configuration parameters defined in the documentation, like
autoMemoryDirectory— is enabled only after the user approves the workspace trust prompt that appears in the folder; if it's a team setup, the hook stays inert until a team member does thisIf you import a file from another location (an import in a project-level file that resolves outside the working directory), the documentation says that the first time this happens, an approval prompt appears. If you deny it, "the imports stay disabled and the dialog doesn't appear again"; if a teammate does that, there's no error anywhere, they're just missing a part of the instructions
You can check what was actually loaded using
/context(in its Memory files section), which is more reliable than asking Claude about it, and the first thing we'd try if one of your teammates were to tell you that the setup doesn't work
In Codex CLI
What is version-controlled: anything that's in the repo — both AGENTS.md at the root of the repository, and in subfolders, as well as skills under either .agents/skills/ or .codex/skills/. We checked both with a canary: a SKILL.md file in either place showed up, with its resolved path, in the input prompt the LLM sees.
What's set at the user level: ~/.codex/config.toml, and, if you use multiple profiles (with --profile), ~/.codex/<name>.config.toml.
A middle layer: .codex/config.toml which is committable but only read in projects that are set as trusted. We created a scratch repo with one and ran codex doctor, and the Configuration section listed just ~/.codex/config.toml there, as we hadn't trusted that project yet.
Same goes for hooks — a committed one won't run until that person trusts the project. That's what --dangerously-bypass-hook-trust is for, described in the help as running enabled hooks "without requiring persisted hook trust for this invocation".
We even have a tool for that — codex debug prompt-input which prints (in JSON) what will be sent as an input prompt to the LLM. No request, no cost. You can even pipe it through a formatter and filter by AGENTS.md if you want, to see the differences in terms of what your setup (or your teammate's) actually sources versus what you assume it sources, which is the most cost-efficient way for 2 team members to do it.
codex debug prompt-input | python3 -m json.tool | grep -i agents.mdIn GitHub Copilot CLI
The version-controlled parts:
AGENTS.md.github/copilot-instructions.md(created bycopilot initbased on the files found in the repo).github/skills/.github/agents/.github/hooks/.github/copilot/settings.json(repo-wide config)
These are in the .github folder, which is a standard place for repo-level configuration in GitHub.
The user-specific parts, under ~/.copilot:
settings.jsonmcp-config.jsonpermissions-config.jsonskills/
Plus .github/copilot/settings.local.json, which the documentation tells you to add to .gitignore yourself.
Generally, the skill discovery works based on three directories that are part of the repository, two of which belong to other vendors:
.github/skills/.agents/skills/.claude/skills/
The copilot skill --help command lists exactly these three, and we confirmed all of them — the same canary SKILL.md, dropped into each in turn, came back as a project skill every time. Also, the same documentation mentions .claude/settings.json and .claude/settings.local.json under cross-tool support.
But…
If you define a file at the repo level with settings, it can only have 14 keys:
modeleffortLevelcontextTierhooksdisableAllHooksdisabledMcpServersdisabledSkillsdeniedUrlsrespectGitignoreand a handful more
Anything else is silently ignored. Even if it's a key that's valid at the user level, in settings.json.
The model / effortLevel / contextTier keys only work in trusted working directories.
In Cursor
It's worth being precise about what's per-user and what's shared here. We can put rules under the .cursor/rules directory with the .mdc extension and they're actually versioned (as the official docs say), and it's a good practice to commit them so everybody can benefit from them.
But there's also the AGENTS.md file which is picked up by Cursor if it's in the root of the repository or in any nested directory, so if we have an agent working in such a directory it gets the configuration defined in that file.
Similar for .cursor/mcp.json and .cursor/hooks.json files.
The same goes for skills which we can place under .cursor/skills or .agents/skills directories; as per the docs it also reads .claude/skills and .codex/skills as compatibility directories, because Claude and Codex put skills there.
What's more, there's this "trust" thing in the Cursor CLI, it has a --trust flag which is supposed to trust the workspace you're working on without asking — this suggests that if we were to clone a fresh repository it wouldn't be trusted and the configuration which is already there would do nothing until each person clears that.
And then… even if we create a rule it doesn't mean that it will actually run — there are four rule types and only alwaysApply: true is unconditional, the others need to meet some other conditions:
it must match a file path using a glob pattern
the agent must think that the description of this rule is relevant
you have to @-mention the rule explicitly
That means that the shared convention defined in the rule using a glob won't apply until somebody opens a file which matches this glob. And then "why did it ignore our convention" is a very hard question to answer. For example:
---
alwaysApply: true
---It's good to keep either team-wide standards in always-apply rules or in the AGENTS.md file, and per-directory conventions in globbed rules (but we'd say we should use them for guidance which is specific only to a particular area of the repository).
In the Team and Enterprise plans you can also manage rules centrally from the Cursor dashboard — it's the real way to enforce a configuration, and it's gated on the plan, not on what a developer thinks the repo needs.
In Antigravity CLI
Defining what is shared and what is per-user on this harness.
What's shareable (in the repo):
Either of these files, at the root of the project:
GEMINI.mdAGENTS.md
Skills defined in the
.agents/skills/folder
What's per-user: all the other configuration stuff that the CLI settings docs cover, stored as a regular JSON file at ~/.gemini/antigravity-cli/settings.json. That page describes no project-level settings file to commit next to it.
Which means that if another person joins on the same harness they will have your notes and skills but their own permission settings (for example they'll have their own allowNonWorkspaceAccess setting which is responsible for letting the agent read and write files outside of project folders and is off by default unless they turn it on).
Also, the above-mentioned folder is called .gemini/skills/ in some projects so you need to either move or rename it to .agents/skills/ for it to be detected by the agent (we could say that in a cross-tool world this path is the same as the one you'd pick anyway, and the official docs say to use .agents/skills/, so we're not losing anything here).
In theory you can choose either GEMINI.md or AGENTS.md here. But we'd recommend AGENTS.md as this file is also being read by Codex, Copilot CLI, Cursor and Kimi Code; in the docs the workspace file is described as one or the other, so you can have both but then you'll need to update both whenever you want to change sth and there's no information which one is preferred.
In Kimi Code CLI
If you want to make the information available across the entire project, create AGENTS.md at the top level of the repository (or .kimi-code/AGENTS.md, if you want to keep it only for Kimi). Skills go in one of these locations:
.kimi-code/skillsor.agents/skills
Personally, we would go with the second one as then you have a single folder which is read by every harness except Claude Code. So it's five.
In case of general instructions for yourself, you can create AGENTS.md in your $HOME/.kimi-code directory (or in $KIMI_CODE_HOME if you've changed it). Or, if you want to have some specific instructions for multiple tools, you can create AGENTS.md in $HOME/.agents.
But… There's one thing to be aware of. The docs describe how to define hooks only via the [[hooks]] section of the user-level config.toml file — there's no such thing for the repo level. That means that on this harness, we can't attach a gate to a PR so your teammates can benefit from it. It must go to CI. Which makes it the clearest case in the whole set for the tier split: put the facts in the repository and the enforcement where the repository can reach every developer.
There's also kimi doctor, and it's worth reading what it actually checked. Ours printed one OK line per file:
$ kimi doctor
Kimi doctor
OK config.toml /Users/you/.kimi-code/config.toml
OK tui.toml /Users/you/.kimi-code/tui.toml
All checked config files are valid.Both of those live under ~/.kimi-code, which means that it's a per-machine check rather than a repo-level one.
And before you invite a teammate to this project, ask them to run kimi --version. The rebuilt CLI reports 0.31.x and stores its data in the $HOME/.kimi-code directory by default. There's also an older program called kimi-cli — the one the kimi migrate command imports data from — and everything above assumes the former.
What you are actually aiming for
It's not about uniformity — you can't enforce it if you don't have a mandate, and somebody showing up with an unconfigured harness isn't a problem. It's about:
Facts loaded, whatever they were driving
The gate that really matters being in CI, where nobody can turn it off
Nothing assuming a single tool
And space for everybody to keep their own settings
Unless you really need sth — a path that must never be read, or some compliance requirement — then stop here and deploy managed settings, because that's what they're there for.
But the most expensive mistake is this middle ground — a rule everybody says is important but put in a file any of these tools can ignore.