Which agent extension features actually work on which agent.
Your SKILL.md sets model: opus. On Claude Code that switches the model. On Grok Build the field parses fine, does nothing at all, and nobody tells you. Your allowed-tools list pre-approves tools on one agent and grants nothing on the other, which is a security difference, not a cosmetic one.
Spec linters cannot catch this. The file is valid. It just does not do what you think on the agent someone else is running.
npx agentcompat lint ..claude/skills/demo/SKILL.md
4 ignored `model` on Grok Build
Accepted by the parser and never applied. The session model is used instead, with no warning.
5 ignored `allowed-tools` on Grok Build
Parsed, but it neither grants nor restricts tools. A skill relying on it for a permission grant silently gets nothing.
.claude/hooks/start.sh
2 unsupported `CLAUDE_PROJECT_DIR` on Grok Build
Not set. The nearest equivalent carries a GROK_ prefix, so a hook written for one agent reads an empty value on the other.
agentcompat lint . # warn where this project relies on ignored behaviour
agentcompat lint . --agent grok-build # only care about one agent
agentcompat lint . --json # machine-readable
agentcompat table # print the matrix as markdown
agentcompat missing # every cell nobody has verified
agentcompat agents # the agents being tracked
agentcompat check # validate the data fileslint exits 1 when it finds something an agent silently ignores, so it can gate a pull request.
| Value | Means |
|---|---|
yes |
Works as documented |
no |
Not implemented. The agent errors, or there is no equivalent |
partial |
Works, but not fully or not the same way. See the note |
ignored |
Accepted without complaint and then does nothing. No error, no warning |
? |
Nobody has checked yet |
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
AGENTS.md |
? | ? | ? | ? | ? |
.claude/ extensions |
yes | yes | ? | ? | ? |
CLAUDE.md |
yes | yes | ? | ? | ? |
.claude/rules/ |
yes | yes | ? | ? | ? |
.claude/ extensionson Claude Code: Its own directory. (evidence).claude/ extensionson Grok Build: Auto-discovers Claude Code marketplaces, plugins, skills, MCP servers, agents and hooks with zero configuration. (evidence)CLAUDE.mdon Grok Build: Read alongside Grok's own files, with no configuration. (evidence)
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
CLAUDE_EFFORT |
yes | ? | ? | ? | ? |
CLAUDE_PLUGIN_DATA |
yes | no | ? | ? | ? |
CLAUDE_PLUGIN_ROOT |
yes | no | ? | ? | ? |
CLAUDE_PROJECT_DIR |
yes | no | ? | ? | ? |
GROK_HOOK_EVENT |
no | yes | ? | ? | ? |
GROK_HOOK_NAME |
no | yes | ? | ? | ? |
GROK_PLUGIN_DATA |
no | yes | ? | ? | ? |
GROK_PLUGIN_ROOT |
no | yes | ? | ? | ? |
GROK_SESSION_ID |
no | yes | ? | ? | ? |
GROK_WORKSPACE_ROOT |
no | yes | ? | ? | ? |
CLAUDE_PLUGIN_DATAon Grok Build: Not set. The nearest equivalent carries a GROK_ prefix, so a hook written for one agent reads an empty value on the other. (evidence)CLAUDE_PLUGIN_ROOTon Grok Build: Not set. The nearest equivalent carries a GROK_ prefix, so a hook written for one agent reads an empty value on the other. (evidence)CLAUDE_PROJECT_DIRon Grok Build: Not set. The nearest equivalent carries a GROK_ prefix, so a hook written for one agent reads an empty value on the other. (evidence)GROK_HOOK_EVENTon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)GROK_HOOK_NAMEon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)GROK_PLUGIN_DATAon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)GROK_PLUGIN_ROOTon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)GROK_SESSION_IDon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)GROK_WORKSPACE_ROOTon Claude Code: Not in the documented environment. Claude Code passes the same information as JSON on stdin instead. (evidence)
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
JSON payload on stdin |
yes | ? | ? | ? | ? |
JSON payload on stdinon Claude Code: Every command hook receives a JSON object on stdin with session_id, cwd, hook_event_name, tool_name and tool_input. (evidence)
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
source commit pinning |
? | yes | ? | ? | ? |
source commit pinningon Grok Build: The official marketplace requires a pinned commit SHA for remote sources. (evidence)
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
MCP server configuration |
yes | ? | ? | ? | yes |
MCP server configurationon Claude Code: Project and user scopes use mcpServers in .mcp.json or ~/.claude.json; stdio and HTTP transports are documented. (evidence)MCP server configurationon Gemini CLI: settings.json uses a top-level mcpServers object and supports stdio, SSE, and HTTP transports with user/project scope. (evidence)
| Feature | Claude Code | Grok Build | Cursor | Codex CLI | Gemini CLI |
|---|---|---|---|---|---|
agent |
yes | ? | ? | ? | ? |
allowed-tools |
yes | ignored | ? | ? | ? |
argument-hint |
yes | yes | ? | ? | ? |
arguments |
yes | ? | ? | ? | ? |
background |
yes | ? | ? | ? | ? |
compatibility |
ignored | ignored | ? | ? | ? |
context |
yes | ? | ? | ? | ? |
description |
yes | yes | ? | ? | ? |
disable-model-invocation |
yes | yes | ? | ? | ? |
disallowed-tools |
yes | ? | ? | ? | ? |
effort |
yes | ignored | ? | ? | ? |
hooks |
yes | ? | ? | ? | ? |
license |
ignored | ignored | ? | ? | ? |
metadata |
partial | partial | ? | ? | ? |
model |
yes | ignored | ? | ? | ? |
name |
yes | yes | ? | ? | ? |
paths |
yes | yes | ? | ? | ? |
shell |
yes | ? | ? | ? | ? |
user-invocable |
yes | partial | ? | ? | ? |
when_to_use |
yes | yes | ? | ? | ? |
allowed-toolson Claude Code: Pre-approves the listed tools for the turn that invokes the skill. (evidence)allowed-toolson Grok Build: Parsed, but it neither grants nor restricts tools. A skill relying on it for a permission grant silently gets nothing. (evidence)compatibilityon Claude Code: Accepted, capped at 500 characters, and never acted on. (evidence)compatibilityon Grok Build: Accepted and never applied. (evidence)efforton Claude Code: Overrides the session effort level. (evidence)efforton Grok Build: Accepted and never applied. (evidence)licenseon Claude Code: Accepted, and the documentation states Claude Code does not act on it. (evidence)licenseon Grok Build: Accepted and never applied. (evidence)metadataon Claude Code: Stored for your own tooling to read. Claude Code does not act on the contents and drops a value that is not a map. (evidence)metadataon Grok Build: Reads author and short-description out of it. (evidence)modelon Claude Code: Overrides the model for the rest of the turn. (evidence)modelon Grok Build: Accepted by the parser and never applied. The session model is used instead, with no warning. (evidence)user-invocableon Grok Build: Only the literal value true counts. Other truthy values are not recognised. (evidence)
62 of 185 cells are filled in (34%). Every ? is an open question, and filling one in is a pull request.
Most of this table is unknown, and that is the honest state of it. Nobody has systematically checked Cursor, Codex, or Gemini CLI, and doing so is the entire project.
One cell is one small pull request. Edit the support block for your agent in data/features/<id>.yml:
cursor:
status: ignored
note: "Accepted by the parser and never applied."
evidence: https://docs.cursor.com/some/page
checked: 2026-09-04
method: testedRules the validator enforces, so you cannot get them wrong quietly:
- Any status other than
unknownneedsevidence, a URL somebody can open. partial,ignored, andunsupportedneed anotesaying what actually happens.checkedis the date you confirmed it, because these agents change monthly.methodisdocs,tested, orsource.testedbeatsdocs, since documentation is often behind.
Run agentcompat missing for the full list of open cells, or read CONTRIBUTING.md.
Most compatibility tables have yes and no. The interesting failure here is neither.
An agent that rejects an unknown field teaches you something immediately. An agent that accepts it and does nothing teaches you nothing, and your skill quietly behaves differently for every user on the other agent. Four fields already behave this way, and the count will grow as each vendor extends the format.
That is why the status exists, and why lint exits 1 only for ignored.
Add an entry to data/agents.yml and every feature gains a ? for it. Nothing else needs editing, and no feature file has to be touched:
- id: windsurf
name: Windsurf
vendor: Codeium
docs: https://docs.windsurf.com- agentguard asks whether an extension is safe. This asks whether it works the same everywhere.
- Agent Skills is the shared spec several of these agents follow, and the reason fields like
licenseandcompatibilityappear in agents that ignore them.
It is not a test suite. Every claim here comes from documentation or from someone running the thing and writing down what happened. Documentation lies, versions move, and a cell marked checked: 2026-09-04 may already be wrong. The checked date and the evidence link are there so you can see how much to trust a row.
Apache-2.0