@chimix/agentscan 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +391 -0
  2. package/README.md +377 -154
  3. package/dist/cli.js +6422 -5421
  4. package/package.json +6 -3
package/README.md CHANGED
@@ -5,19 +5,19 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <img src="https://img.shields.io/badge/checks-87-111111?style=flat-square" alt="87 checks">
9
- <img src="https://img.shields.io/badge/tests-420%20passing-111111?style=flat-square" alt="420 tests">
8
+ <img src="https://img.shields.io/badge/checks-143-111111?style=flat-square" alt="143 checks">
9
+ <img src="https://img.shields.io/badge/tests-647%20passing-111111?style=flat-square" alt="647 tests">
10
10
  <img src="https://img.shields.io/badge/network-none-111111?style=flat-square" alt="No network">
11
11
  <img src="https://img.shields.io/badge/writes-none-111111?style=flat-square" alt="Writes nothing">
12
12
  <img src="https://img.shields.io/badge/runs%20on-node%20%C2%B7%20bun-111111?style=flat-square" alt="Node or Bun">
13
13
  <img src="https://img.shields.io/npm/v/@chimix/agentscan?style=flat-square&color=111111&label=npm" alt="npm version">
14
14
  <img src="https://img.shields.io/badge/license-MIT-111111?style=flat-square" alt="MIT license">
15
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-1.1.0-111111?style=flat-square" alt="Changelog"></a>
15
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-2.0.0-111111?style=flat-square" alt="Changelog"></a>
16
16
  </p>
17
17
 
18
18
  <p align="center">
19
- <strong>87 checks &middot; offline &middot; 0 network calls on <code>check</code> &middot; provenance on every rule</strong><br>
20
- <sub>An offline linter for Claude Code, Command Code, portable Agent Skills, nested AGENTS.md, Copilot CLI hooks, and the MCP / hooks / rules surfaces that 1.1.0 actually implements. Spec-required checks cite a published line in <a href="docs/spec/">docs/spec/</a>. Heuristics stay at <code>info</code> and say so. The coverage matrix below is the honesty contract — five dimensions, and a documented global location that is not scanned stays unread.</sub>
19
+ <strong>143 checks &middot; offline &middot; 0 network calls on <code>check</code> &middot; provenance on every rule</strong><br>
20
+ <sub>An offline linter for Claude Code, Command Code, Grok Build, Windsurf, Kiro, Cline, Roo, Kilo, Junie, portable Agent Skills, nested AGENTS.md, Copilot CLI hooks (including inline settings), and the MCP / hooks / rules surfaces the registry actually implements. Spec-required checks cite a published line in <a href="docs/spec/">docs/spec/</a>. Heuristics stay at <code>info</code> and say so. The coverage matrix below is the honesty contract — five dimensions, and a documented global location that is not scanned stays unread.</sub>
21
21
  </p>
22
22
 
23
23
  ---
@@ -63,9 +63,9 @@ of the filesystem**, where nothing else in your stack will ever tell you.
63
63
  No AI, no network on `check`. Read the config, read the disk, compare:
64
64
 
65
65
  ```
66
- 1. Discover .claude/ .commandcode/ .agents/ .vscode/ .cursor/ .codex/ .gemini/ .github/ .continue/ AGENTS.md skills-lock.json
66
+ 1. Discover .claude/ .commandcode/ .grok/ .agents/ .vscode/ .cursor/ .codex/ .gemini/ .github/ .continue/ AGENTS.md skills-lock.json
67
67
  2. Extract immutable facts — never re-read during checking
68
- 3. Check 87 checks, each labeled spec-required, vendor-recommendation,
68
+ 3. Check 143 checks, each labeled spec-required, vendor-recommendation,
69
69
  security, internal-consistency, or heuristic
70
70
  4. Report text · --json · --output prompt (handoff for a fixing agent)
71
71
  ```
@@ -116,8 +116,8 @@ too close to an unrelated `agent-scan`. The command you type stays `agentscan`.
116
116
  bun add -d @chimix/agentscan # then: bunx agentscan check
117
117
  ```
118
118
 
119
- Point it at a project that actually has agent config — a `.claude/` or
120
- `.commandcode/` directory, an `.mcp.json`, an `AGENTS.md`. A directory with
119
+ Point it at a project that actually has agent config — a `.claude/`,
120
+ `.commandcode/`, or `.grok/` directory, an `.mcp.json`, an `AGENTS.md`. A directory with
121
121
  agent config and no `package.json` is still scannable. On a directory with
122
122
  neither it exits with a clear error.
123
123
 
@@ -164,7 +164,7 @@ bunx agentscan check # text report (cwd)
164
164
  bunx agentscan check ./my-app # explicit root
165
165
  bunx agentscan check --json # machine-readable
166
166
  bunx agentscan check --quiet # summary only
167
- bunx agentscan check --verbose # include KEEP, info findings, and each finding's id
167
+ bunx agentscan check --verbose # include info findings and each finding's id
168
168
  bunx agentscan check --fail-on warning
169
169
  bunx agentscan check --global # also scan global skill dirs
170
170
 
@@ -183,10 +183,10 @@ Flags for `check`:
183
183
  | `--copy` | Also copy the report to the system clipboard |
184
184
  | `--no-color` | Never colour, even on a terminal (`NO_COLOR=1` does the same) |
185
185
  | `--quiet` | Summary line only |
186
- | `--verbose` | Show KEEP + info-severity findings, and print each finding's id |
186
+ | `--verbose` | Show info-severity findings, and print each finding's id |
187
187
  | `--fail-on <level>` | `never` (default) · `warning` · `error` |
188
188
  | `--fail-under <0-100>` | Fail when the score drops below this floor |
189
- | `--global` | Also scan `~/.claude/skills`, `~/.codex/skills`, `~/.commandcode/skills`, and `~/.agents/skills` (see below) |
189
+ | `--global` | Also scan user skills (`$CLAUDE_CONFIG_DIR/skills` or `~/.claude/skills`, `~/.codex/skills`, `~/.commandcode/skills`, `~/.agents/skills`, `$GROK_HOME/skills`, `~/.codeium/windsurf/skills`), Claude user settings / agents / `CLAUDE.md` / rules / commands and `$CLAUDE_CONFIG_DIR/.claude.json` or `~/.claude.json` (user + matching local `mcpServers`), Copilot `$COPILOT_HOME` / `~/.copilot/hooks` and `settings.json`, Codex `$CODEX_HOME` / `~/.codex/hooks.json` and `config.toml` (MCP plus inline `[hooks]`), Grok `$GROK_HOME` / `~/.grok` (hooks + `config.toml`), Command Code user MCP/agents/memory, Antigravity `~/.gemini/config/hooks.json`, `rules/`, and `skills/`, plus `~/.gemini/AGENTS.md` and `GEMINI.md`, Kiro `~/.kiro/agents`, and Windsurf `~/.codeium/windsurf` MCP + `global_rules.md` + `hooks.json` (see below) |
190
190
  | `--config <path>` | Config file path |
191
191
 
192
192
  **v1 does not write the tree** — no `apply`, no skill delete/install. Findings may *suggest* shell commands; you run them yourself.
@@ -199,7 +199,7 @@ Flags for `check`:
199
199
  │ ⌒ │ 6 error · 2 warning · 11 info hidden (--verbose)
200
200
  └─────┘
201
201
 
202
- agentscan v1.1.0 — touchagency
202
+ agentscan v2.0.0 — touchagency
203
203
 
204
204
  Scanned: 46 deps · 108 skills · 1 mcp · 2 agents · packageManager=bun
205
205
 
@@ -231,7 +231,7 @@ JSON shape (abridged):
231
231
 
232
232
  ```json
233
233
  {
234
- "version": "1.1.0",
234
+ "version": "2.0.0",
235
235
  "root": "/path/to/project",
236
236
  "factsSummary": {
237
237
  "packageManager": "bun",
@@ -259,14 +259,27 @@ Same tree → same sorted findings, with stable unique `id`s.
259
259
 
260
260
  ### `--global`
261
261
 
262
- Adds `~/.claude/skills`, `~/.codex/skills`, `~/.commandcode/skills`, and
263
- `~/.agents/skills` to the structural checks: a `SKILL.md` that is malformed is
264
- malformed wherever it lives, and those findings carry a `source: global`
265
- evidence entry so you can tell them apart. Codex and Command Code skills use
266
- the Agent Skills contract (`name` and `description` required). `--global` also
267
- inventories user Command Code agents (`~/.commandcode/agents`), MCP
268
- (`~/.commandcode/mcp.json` and user settings `mcp.servers`), and memory
269
- (`~/.commandcode/AGENTS.md`). It never opens `auth.json` or `mcp-tokens.json`.
262
+ Adds user skill dirs (`$CLAUDE_CONFIG_DIR/skills` or `~/.claude/skills`, `~/.codex/skills`,
263
+ `~/.commandcode/skills`, `~/.agents/skills`, `$GROK_HOME/skills` or
264
+ `~/.grok/skills`), Claude user settings / agents / `CLAUDE.md` / rules /
265
+ commands and `~/.claude.json` (top-level user `mcpServers` plus local
266
+ `projects.<scanned-path>.mcpServers`), Copilot `~/.copilot/hooks`, Codex `$CODEX_HOME` /
267
+ `~/.codex` (`hooks.json`, `config.toml` MCP and inline `[hooks]`, plus `AGENTS.override.md` / `AGENTS.md`),
268
+ Grok `$GROK_HOME` / `~/.grok` (hooks + `config.toml`), Antigravity
269
+ `~/.gemini/config/hooks.json`, `rules/`, and `skills/`, plus
270
+ `~/.gemini/AGENTS.md` and `GEMINI.md`, Kiro `~/.kiro/agents`, Command Code user
271
+ agents / MCP / memory, and Windsurf `~/.codeium/windsurf/mcp_config.json`,
272
+ `memories/global_rules.md`, `hooks.json`, and `skills` to the structural checks: a `SKILL.md` that
273
+ is malformed is malformed wherever it lives, and those findings carry a
274
+ `source: global` evidence entry so you can tell them apart. Codex and
275
+ Command Code skills use the Agent Skills contract (`name` and `description`
276
+ required). Grok skills do not — `name` and `description` are optional. It
277
+ never opens `auth.json`, `mcp-tokens.json`, Codex `auth.json`, or Grok
278
+ `auth.json` / `mcp_credentials.json`. A `~/.claude.json` without user or
279
+ matching local `mcpServers` is skipped, not reported as unreadable.
280
+ `CLAUDE_CONFIG_DIR` relocates the `~/.claude` directory and, when set,
281
+ `~/.claude.json` to `$CLAUDE_CONFIG_DIR/.claude.json`. Unset, that file
282
+ stays in the home directory.
270
283
 
271
284
  Lockfile checks stay project-scoped — a project lockfile cannot pin a skill that
272
285
  lives in your home directory, so reporting one as "not in the lockfile" could
@@ -346,7 +359,7 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
346
359
 
347
360
  ```json
348
361
  {
349
- "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills"],
362
+ "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills", ".grok/skills", ".windsurf/skills"],
350
363
  "mcpPaths": [".mcp.json", ".claude/mcp.json", "mcp.json", ".vscode/mcp.json", ".cursor/mcp.json", ".agents/mcp_config.json", ".codex/config.toml", ".gemini/settings.json", "opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc", ".continue/config.yaml", ".continue/mcpServers"],
351
364
  "policyFiles": ["AGENTS.md", "CLAUDE.md"],
352
365
  "ignoreSkills": [],
@@ -423,101 +436,181 @@ Every check lives in `src/checks/` and runs on every `check`. `agentscan rules`
423
436
  lists all of them with their ids; `agentscan explain <id>` details any finding.
424
437
 
425
438
  Most validate one discovered item against its own file on disk. Budget and
426
- hygiene checks are **info** (hidden unless `--verbose`). Each registry entry
427
- carries `provenance`: spec-required, vendor-recommendation, security,
428
- internal-consistency, or heuristic. `agentscan rules` lists them all.
429
-
430
- | id | Severity | Catches |
431
- |----|----------|---------|
432
- | `config.unreadable` | error | A config file that is not valid JSON, so whatever it declares is silently not in effect |
433
- | `scan.truncated` | info | A file past the scan cap, so the checks that read its body saw only a prefix — about this tool's reach, not about your project |
434
- | `claude.hook.missing-script` | error | A registered Claude command hook whose script does not exist — it never runs |
435
- | `claude.hook.unknown-event` | error | A Claude hook registered under an event name that is never dispatched |
436
- | `vscode.hook.missing-script` | error | A VS Code command hook whose script does not exist |
437
- | `vscode.hook.unknown-event` | error | A VS Code hook registered under an event name that is never dispatched |
438
- | `claude.agent.missing-frontmatter` | error | A Claude agent definition with no `---` block |
439
- | `claude.agent.missing-description` | error | Claude agent frontmatter has no `description` |
440
- | `claude.agent.missing-name` | error | Claude agent frontmatter has no `name` |
441
- | `claude.agent.invalid-name` | warning | Claude agent name is not lowercase letters and hyphens (error if it starts with `-` or contains `:`; filename is not compared) |
442
- | `claude.agent.duplicate-name` | error | Multiple Claude agent files declare the same name |
443
- | `claude.mcp.no-launch` | error | A Claude MCP server with neither `command` nor `url` |
444
- | `claude.mcp.url-without-type` | error | A Claude remote MCP server with a `url` but no `type` — read as stdio and skipped. On shared `.mcp.json`, a Command Code `transport` field satisfies the other consumer and this check does not fire |
445
- | `claude.mcp.reserved-name` | error | A Claude-consumed MCP server named with a reserved identifier (`workspace`, `claude-in-chrome`, `computer-use`, `Claude Preview`, `Claude Browser`) — skipped at load |
446
- | `vscode.mcp.no-launch` | error | A VS Code MCP server (`servers`) with neither `command` nor `url` |
447
- | `cursor.mcp.no-launch` | error | A Cursor MCP server with neither `command` nor `url` |
448
- | `antigravity.mcp.no-launch` | error | An Antigravity MCP server with neither `command` nor `serverUrl` |
449
- | `codex.mcp.no-launch` | error | A Codex `[mcp_servers.*]` entry with neither `command` nor `url` |
450
- | `gemini.mcp.no-launch` | error | A Gemini MCP server with neither `command`, `url`, nor `httpUrl` |
451
- | `gemini.mcp.underscore-alias` | warning | A Gemini MCP server alias contains `_`, which can make policies fail silently |
452
- | `opencode.mcp.no-launch` | error | An OpenCode MCP server with neither `command` nor `url` |
453
- | `opencode.mcp.missing-type` | error | An OpenCode MCP server missing `type: local` or `type: remote` |
454
- | `opencode.mcp.local-without-command` | error | An OpenCode `type: local` server with no `command` |
455
- | `opencode.mcp.remote-without-url` | error | An OpenCode `type: remote` server with no `url` |
456
- | `opencode.mcp.invalid-launch-for-type` | error | An OpenCode server whose launch field does not match its `type` |
457
- | `opencode.mcp.command-not-array` | error | An OpenCode V2 local `command` that is not an argv array |
458
- | `continue.mcp.no-launch` | error | A Continue MCP server with neither `command`, `url`, nor `uses` |
459
- | `continue.mcp.missing-block-metadata` | error | A standalone `.continue/mcpServers/*.yaml` block missing `name`, `version`, or `schema` (not applied to copied JSON) |
460
- | `commandcode.mcp.no-launch` | error | A Command Code settings / user MCP server with neither `command` nor `url` |
461
- | `commandcode.mcp.invalid-transport` | error | A Command Code MCP `transport` / `type` that is not `http` or `stdio` (Claude `sse` / `ws` on shared `.mcp.json` is skipped) |
462
- | `commandcode.mcp.http-without-url` | error | A Command Code HTTP MCP server with no `url` |
463
- | `commandcode.mcp.stdio-without-command` | error | A Command Code stdio MCP server with no `command` |
464
- | `claude.hook.command-without-command` | error | A Claude `type: command` hook with no `command` |
465
- | `claude.hook.http-without-url` | error | A Claude `type: http` hook with no `url` |
466
- | `claude.hook.mcp-tool-without-server-or-tool` | error | A Claude `type: mcp_tool` hook missing `server` or `tool` |
467
- | `claude.hook.unknown-handler-type` | error | A Claude hook whose required `type` is missing or not one of command, http, mcp_tool, prompt, agent |
468
- | `claude.hook.prompt-without-prompt` | error | A Claude `type: prompt` or `type: agent` hook with no `prompt` |
469
- | `claude.hook.incompatible-handler` | error | A Claude handler type that the event does not support |
470
- | `claude.hook.invalid-group` | error | A Claude hook matcher group with no nested `hooks` array (flat handler arrays are invalid) |
471
- | `vscode.hook.invalid-group` | error | A VS Code hook group that is not a command handler array |
472
- | `vscode.hook.command-without-command` | error | A native VS Code `type: command` hook with no `command` |
473
- | `vscode.hook.unknown-handler-type` | error | A native VS Code hook whose `type` is not `command` |
474
- | `copilot.hook.unknown-event` | error | A Copilot CLI hook registered under an event that is never dispatched |
475
- | `copilot.hook.missing-script` | error | A Copilot CLI command hook whose script does not exist |
476
- | `copilot.hook.command-without-command` | error | A Copilot CLI command hook with none of `bash`, `powershell`, or `command` |
477
- | `copilot.hook.http-without-url` | error | A Copilot CLI `type: http` hook with no `url` |
478
- | `copilot.hook.prompt-without-prompt` | error | A Copilot CLI `type: prompt` hook with no `prompt` |
479
- | `copilot.hook.unknown-handler-type` | error | A Copilot CLI hook whose `type` is not command, http, or prompt |
480
- | `copilot.hook.incompatible-handler` | error | A Copilot CLI prompt hook registered on an event other than `sessionStart` |
481
- | `commandcode.hook.unknown-event` | error | A Command Code hook registered under an event that is never dispatched (four events only) |
482
- | `commandcode.hook.missing-script` | error | A Command Code command hook whose script does not exist |
483
- | `commandcode.hook.invalid-group` | error | A Command Code hook group missing the required nested `hooks` array, or a non-string `matcher` |
484
- | `commandcode.hook.command-without-command` | error | A Command Code `type: command` hook whose `command` is missing, empty, or not a string |
485
- | `commandcode.hook.unknown-handler-type` | error | A Command Code hook whose `type` is missing, not a string, or not `"command"` |
486
- | `commandcode.hook.timeout-out-of-bounds` | error | A Command Code hook `timeout` outside 0–600 seconds |
487
- | `commandcode.agent.reserved-name` | error | A Command Code custom agent named `explore`, `plan`, `review`, or `general` — ignored at load |
488
- | `commandcode.agent.invalid-permission-mode` | error | A Command Code agent `permissionMode` that is not a documented value |
489
- | `commandcode.agent.invalid-field-type` | error | A Command Code agent field whose type the spec does not accept |
490
- | `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
491
- | `security.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
492
- | `mcp.literal-env` | warning | Secret-named `env` values that are literals instead of interpolation |
493
- | `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
494
- | `claude.skill.missing-frontmatter` | warning | Claude `SKILL.md` with no `---` block |
495
- | `claude.skill.missing-description` | info | Claude frontmatter has no `description` and no first markdown paragraph |
496
- | `agent-skills.skill.missing-frontmatter` | error | Portable skill `SKILL.md` has no YAML frontmatter |
497
- | `agent-skills.skill.missing-name` | error | Portable skill frontmatter has no required `name` |
498
- | `agent-skills.skill.missing-description` | error | Portable skill frontmatter has no required `description` |
499
- | `agent-skills.skill.invalid-name` | error | Portable skill name is not `[a-z0-9]` + hyphens |
500
- | `agent-skills.skill.name-does-not-match-directory` | error | Portable skill `name` does not match its directory |
501
- | `agent-skills.skill.name-too-long` | error | Portable skill name exceeds 64 characters |
502
- | `agent-skills.skill.description-too-long` | error | Portable skill description exceeds 1024 characters |
503
- | `agent-skills.skill.invalid-compatibility` | error | Portable skill `compatibility` is present but is not a 1–500 character string |
504
- | `agent-skills.skill.invalid-metadata` | error | Portable skill `metadata` is present but is not `map<string, string>` |
505
- | `agent-skills.skill.invalid-allowed-tools` | error | Portable skill `allowed-tools` is present but is not a string |
506
- | `agent-skills.skill.body-too-large` | info | Portable skill `SKILL.md` exceeds the 500-line recommendation |
507
- | `skill.broken-reference` | warning | The body points at a bundled file that does not exist |
508
- | `skill.duplicate-description` | info | Two or more skills carry an identical description (heuristic) |
509
- | `skill.locked-not-installed` | warning | `skills-lock.json` pins a skill that is not on disk |
510
- | `skill.not-in-lock` | info | A skill on disk that the lockfile does not track (installer policy) |
511
- | `skill.description-budget` | info | Claude skill listing text (description or first paragraph, plus `when_to_use`) exceeds the 1% context-window budget, with an 8000-character fallback and a 1536-character per-entry cap |
512
- | `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
513
- | `budget.agents-md` | info | `AGENTS.md` past a secondary 150-line hint (heuristic; not an AGENTS.md requirement) |
514
- | `budget.claude-md` | info | `CLAUDE.md` past the official “target under 200 lines” recommendation |
515
- | `budget.agents` | info | More agent definitions than a focused set (>8) — heuristic proxy |
516
- | `budget.mcp` | info | More MCP servers than a tool-selection hint (>5) — heuristic proxy |
517
- | `codex.budget.instructions` | info | Codex root→cwd `AGENTS.md` chain exceeds the default 32 KiB |
518
- | `cursor.rule.too-large` | info | A `.cursor/rules/*.mdc` file exceeds the 500-line recommendation |
519
-
520
- ### Coverage in 1.1.0
439
+ hygiene checks are **info** (hidden unless `--verbose`).
440
+
441
+ Every rule states where its assertion comes from, and every report says so:
442
+ a `provenance` label (spec-required, vendor-recommendation, security,
443
+ internal-consistency, or heuristic) and either the vendor page it was read
444
+ from, with the date, or an explicit note that no vendor page states it.
445
+
446
+ ```
447
+ ERROR rule:cursor.hook.unknown-event
448
+ "bogusEventName" is not a hook event that gets dispatched
449
+ bogusEventName @ .cursor/hooks.json · sessionStart, sessionEnd, ...
450
+ spec-required · cursor.com/docs/hooks · read 2026-09-03
451
+
452
+ ERROR rule:cursor.hook.missing-script
453
+ Hook points at a script that does not exist
454
+ afterFileEdit @ .cursor/hooks.json · ./hooks/format.sh
455
+ internal-consistency · no vendor page · agentscan inference
456
+ ```
457
+
458
+ `agentscan rules` lists every rule with its severity, provenance and source;
459
+ `agentscan rules --json` adds the full URL and the `docs/spec` capture holding
460
+ the quoted lines; `agentscan explain <id>` prints both for one finding, and
461
+ `--json` carries them on every finding. Every cited page is one
462
+ `bun run spec:check` re-fetches and hash-compares, so a vendor rewriting it
463
+ fails CI instead of quietly turning a quoted rule into a false positive.
464
+
465
+ <!-- rules:start -->
466
+ | id | Severity | Provenance | Source | Catches |
467
+ |----|----------|------------|--------|---------|
468
+ | `config.unreadable` | error | internal-consistency | _parser result_ | Config file is not valid JSON or cannot be read |
469
+ | `scan.truncated` | info | internal-consistency | _scan cap_ | File is larger than the scan cap, so body checks read only a prefix of it |
470
+ | `claude.hook.unknown-event` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Hook registered under an event name that is never dispatched |
471
+ | `claude.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Hook points at a script that does not exist, so it never runs |
472
+ | `vscode.hook.unknown-event` | error | spec-required | [code.visualstudio.com/docs/agents/reference/hooks-reference](https://code.visualstudio.com/docs/agents/reference/hooks-reference) | VS Code hook registered under an event name that is never dispatched |
473
+ | `vscode.hook.missing-script` | error | internal-consistency | _agentscan inference_ | VS Code hook points at a script that does not exist, so it never runs |
474
+ | `claude.hook.invalid-group` | error | internal-consistency | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude hook group has a matcher but no hooks array |
475
+ | `claude.hook.command-without-command` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude command hook is missing the command field |
476
+ | `claude.hook.http-without-url` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude http hook is missing the url field |
477
+ | `claude.hook.mcp-tool-without-server-or-tool` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude MCP tool hook is missing required server and tool fields |
478
+ | `claude.hook.unknown-handler-type` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude hook uses a handler type that is not command, http, mcp_tool, prompt, or agent, or omits the required type |
479
+ | `claude.hook.prompt-without-prompt` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude prompt or agent hook is missing the required prompt field |
480
+ | `claude.hook.incompatible-handler` | error | spec-required | [code.claude.com/docs/en/hooks](https://code.claude.com/docs/en/hooks) | Claude hook handler type is not valid for its event |
481
+ | `vscode.hook.invalid-group` | error | internal-consistency | [code.visualstudio.com/docs/agents/reference/hooks-reference](https://code.visualstudio.com/docs/agents/reference/hooks-reference) | VS Code hook group has a matcher but no hooks array |
482
+ | `vscode.hook.command-without-command` | error | spec-required | [code.visualstudio.com/docs/agents/reference/hooks-reference](https://code.visualstudio.com/docs/agents/reference/hooks-reference) | VS Code command hook is missing the command field |
483
+ | `vscode.hook.unknown-handler-type` | error | spec-required | [code.visualstudio.com/docs/agents/reference/hooks-reference](https://code.visualstudio.com/docs/agents/reference/hooks-reference) | VS Code hook uses a handler type other than the required type command |
484
+ | `copilot.hook.unknown-event` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI hook registered under an event name that is never dispatched |
485
+ | `copilot.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Copilot CLI hook points at a script that does not exist, so it never runs |
486
+ | `copilot.hook.command-without-command` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI command hook declares none of bash, powershell, command, or exec |
487
+ | `copilot.hook.http-without-url` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI http hook is missing the url field |
488
+ | `copilot.hook.prompt-without-prompt` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI prompt hook is missing the prompt field |
489
+ | `copilot.hook.unknown-handler-type` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI hook uses a handler type that is not command, http, or prompt |
490
+ | `copilot.hook.incompatible-handler` | error | spec-required | [docs.github.com/en/copilot/reference/hooks-reference](https://docs.github.com/en/copilot/reference/hooks-reference) | Copilot CLI prompt hook is registered on an event other than sessionStart |
491
+ | `skill.missing-skill-md` | warning | internal-consistency | [agentskills.io/specification](https://agentskills.io/specification) | Skill directory has no SKILL.md |
492
+ | `claude.skill.missing-frontmatter` | warning | spec-required | [code.claude.com/docs/en/skills](https://code.claude.com/docs/en/skills) | SKILL.md has no YAML frontmatter block |
493
+ | `claude.skill.missing-description` | info | vendor-recommendation | [code.claude.com/docs/en/skills](https://code.claude.com/docs/en/skills) | SKILL.md frontmatter has no description and no first markdown paragraph |
494
+ | `agent-skills.skill.missing-frontmatter` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill SKILL.md has no YAML frontmatter block |
495
+ | `agent-skills.skill.missing-name` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill frontmatter has no required name |
496
+ | `agent-skills.skill.missing-description` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill frontmatter has no required description |
497
+ | `agent-skills.skill.invalid-name` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill name is not a valid identifier |
498
+ | `agent-skills.skill.name-does-not-match-directory` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill name does not match its directory |
499
+ | `agent-skills.skill.name-too-long` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill name exceeds 64 characters |
500
+ | `agent-skills.skill.description-too-long` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill description exceeds 1024 characters |
501
+ | `agent-skills.skill.invalid-compatibility` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill compatibility is not a string of 1–500 characters |
502
+ | `agent-skills.skill.invalid-metadata` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill metadata is not a map of string to string |
503
+ | `agent-skills.skill.invalid-allowed-tools` | error | spec-required | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill allowed-tools is not a string |
504
+ | `agent-skills.skill.body-too-large` | info | vendor-recommendation | [agentskills.io/specification](https://agentskills.io/specification) | Portable skill SKILL.md exceeds the 500-line recommendation |
505
+ | `skill.not-in-lock` | info | heuristic | _lockfile hygiene, not a requirement_ | Skill on disk is not tracked by skills-lock.json |
506
+ | `skill.locked-not-installed` | warning | internal-consistency | _agentscan inference_ | skills-lock.json pins a skill that is not installed |
507
+ | `skill.broken-reference` | warning | internal-consistency | [agentskills.io/specification](https://agentskills.io/specification) | SKILL.md points at a bundled file that does not exist |
508
+ | `skill.duplicate-description` | info | heuristic | _selection hint, not a requirement_ | Two or more skills share an identical description |
509
+ | `skill.description-budget` | info | vendor-recommendation | [code.claude.com/docs/en/skills](https://code.claude.com/docs/en/skills) | Claude skill names + descriptions exceed the listing character budget |
510
+ | `skill.no-lockfile` | info | heuristic | _lockfile hygiene, not a requirement_ | Skills present with no skills-lock.json (requireLock only) |
511
+ | `claude.agent.missing-frontmatter` | error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent definition has no YAML frontmatter block |
512
+ | `claude.agent.missing-description` | error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent frontmatter has no description |
513
+ | `claude.agent.missing-name` | error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent frontmatter has no name |
514
+ | `claude.agent.duplicate-name` | error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent names are duplicated |
515
+ | `claude.agent.invalid-name` | warning/error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent frontmatter name is not a valid identifier |
516
+ | `claude.mcp.no-launch` | error | spec-required | [code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) | MCP server declares neither command nor url |
517
+ | `vscode.mcp.no-launch` | error | spec-required | [code.visualstudio.com/docs/agents/reference/mcp-configuration](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) | VS Code MCP server declares neither command nor url |
518
+ | `cursor.mcp.no-launch` | error | spec-required | [cursor.com/docs/context/mcp](https://cursor.com/docs/context/mcp) | Cursor MCP server declares neither command nor url |
519
+ | `antigravity.mcp.no-launch` | error | spec-required | [antigravity.google/docs/mcp](https://antigravity.google/docs/mcp) | Antigravity MCP server declares neither command, serverUrl, nor url |
520
+ | `antigravity.hook.unknown-event` | error | spec-required | [antigravity.google/docs/hooks](https://antigravity.google/docs/hooks) | Antigravity hook registered under an event name that is never dispatched |
521
+ | `antigravity.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Antigravity hook points at a script that does not exist, so it never runs |
522
+ | `antigravity.hook.invalid-group` | error | spec-required | [antigravity.google/docs/hooks](https://antigravity.google/docs/hooks) | Antigravity hook group is missing a hooks array, or matcher is not a string |
523
+ | `antigravity.hook.command-without-command` | error | spec-required | [antigravity.google/docs/hooks](https://antigravity.google/docs/hooks) | Antigravity command hook declares no command |
524
+ | `antigravity.hook.unknown-handler-type` | error | spec-required | [antigravity.google/docs/hooks](https://antigravity.google/docs/hooks) | Antigravity hook handler type is not command |
525
+ | `codex.mcp.no-launch` | error | spec-required | [learn.chatgpt.com/docs/extend/mcp](https://learn.chatgpt.com/docs/extend/mcp) | Codex MCP server declares neither command nor url |
526
+ | `gemini.mcp.no-launch` | error | spec-required | [github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md) | Gemini MCP server declares neither command, url, nor httpUrl |
527
+ | `gemini.mcp.underscore-alias` | warning | vendor-recommendation | [github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md) | Gemini MCP server alias contains an underscore, which can make policies fail silently |
528
+ | `opencode.mcp.no-launch` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode MCP server declares neither command nor url |
529
+ | `opencode.mcp.missing-type` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode MCP server is missing type local or remote |
530
+ | `opencode.mcp.local-without-command` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode local MCP server declares no command |
531
+ | `opencode.mcp.remote-without-url` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode remote MCP server declares no url |
532
+ | `opencode.mcp.invalid-launch-for-type` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode MCP server launch field does not match its type |
533
+ | `opencode.mcp.command-not-array` | error | spec-required | [opencode.ai/v2/docs/mcp-servers](https://opencode.ai/v2/docs/mcp-servers) | OpenCode V2 local MCP command is not an argv array |
534
+ | `continue.mcp.no-launch` | error | spec-required | [docs.continue.dev/customize/deep-dives/mcp](https://docs.continue.dev/customize/deep-dives/mcp) | Continue MCP server declares neither command, url, nor uses |
535
+ | `continue.mcp.missing-block-metadata` | error | spec-required | [docs.continue.dev/customize/deep-dives/mcp](https://docs.continue.dev/customize/deep-dives/mcp) | Standalone Continue YAML MCP block is missing name, version, or schema |
536
+ | `commandcode.mcp.no-launch` | error | spec-required | [commandcode.ai/docs/mcp](https://commandcode.ai/docs/mcp) | Command Code MCP server declares neither command nor url |
537
+ | `commandcode.mcp.invalid-transport` | error | spec-required | [commandcode.ai/docs/mcp](https://commandcode.ai/docs/mcp) | Command Code MCP transport is not http or stdio |
538
+ | `commandcode.mcp.http-without-url` | error | spec-required | [commandcode.ai/docs/mcp](https://commandcode.ai/docs/mcp) | Command Code HTTP MCP server declares no url |
539
+ | `commandcode.mcp.stdio-without-command` | error | spec-required | [commandcode.ai/docs/mcp](https://commandcode.ai/docs/mcp) | Command Code stdio MCP server declares no command |
540
+ | `commandcode.hook.unknown-event` | error | spec-required | [commandcode.ai/docs/hooks](https://commandcode.ai/docs/hooks) | Command Code hook registered under an event that is never dispatched |
541
+ | `commandcode.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Command Code hook points at a script that does not exist, so it never runs |
542
+ | `commandcode.hook.invalid-group` | error | spec-required | [commandcode.ai/docs/hooks](https://commandcode.ai/docs/hooks) | Command Code hook group is missing the required nested hooks array, or matcher is not a string |
543
+ | `commandcode.hook.command-without-command` | error | spec-required | [commandcode.ai/docs/hooks](https://commandcode.ai/docs/hooks) | Command Code command hook is missing the command field |
544
+ | `commandcode.hook.unknown-handler-type` | error | spec-required | [commandcode.ai/docs/hooks](https://commandcode.ai/docs/hooks) | Command Code hook uses a handler type other than the required string "command" |
545
+ | `commandcode.hook.timeout-out-of-bounds` | error | spec-required | [commandcode.ai/docs/hooks](https://commandcode.ai/docs/hooks) | Command Code hook timeout is outside 0–600 seconds |
546
+ | `commandcode.agent.reserved-name` | error | spec-required | [commandcode.ai/docs/agents](https://commandcode.ai/docs/agents) | Command Code custom agent uses a reserved name and is ignored |
547
+ | `commandcode.agent.invalid-permission-mode` | error | spec-required | [commandcode.ai/docs/agents](https://commandcode.ai/docs/agents) | Command Code agent permissionMode is not a documented value |
548
+ | `commandcode.agent.invalid-field-type` | error | spec-required | [commandcode.ai/docs/agents](https://commandcode.ai/docs/agents) | Command Code agent field has a type the spec does not accept |
549
+ | `grok.mcp.no-launch` | error | spec-required | [docs.x.ai/build/features/mcp-servers](https://docs.x.ai/build/features/mcp-servers) | Grok MCP server declares neither command nor url |
550
+ | `grok.hook.unknown-event` | error | spec-required | [docs.x.ai/build/features/hooks](https://docs.x.ai/build/features/hooks) | Grok hook registered under an event that is never dispatched |
551
+ | `grok.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Grok hook points at a script that does not exist, so it never runs |
552
+ | `grok.hook.invalid-group` | error | spec-required | [docs.x.ai/build/features/hooks](https://docs.x.ai/build/features/hooks) | Grok hook group is missing the required nested hooks array, or matcher is not a string |
553
+ | `grok.hook.command-without-command` | error | spec-required | [docs.x.ai/build/features/hooks](https://docs.x.ai/build/features/hooks) | Grok command hook is missing the command field |
554
+ | `grok.hook.http-without-url` | error | spec-required | [docs.x.ai/build/features/hooks](https://docs.x.ai/build/features/hooks) | Grok http hook is missing the url field |
555
+ | `grok.hook.unknown-handler-type` | error | spec-required | [docs.x.ai/build/features/hooks](https://docs.x.ai/build/features/hooks) | Grok hook uses a handler type other than command or http, or omits the required type |
556
+ | `grok.skill.missing-frontmatter` | error | spec-required | [docs.x.ai/build/features/skills-plugins-marketplaces](https://docs.x.ai/build/features/skills-plugins-marketplaces) | Grok SKILL.md has no YAML frontmatter block |
557
+ | `mcp.command-missing` | error | internal-consistency | _cross-provider path assertion; docs/spec/mcp.md calls it internal-consistency_ | MCP server command is a path that does not exist on disk |
558
+ | `claude.mcp.url-without-type` | error | spec-required | [code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) | Remote MCP server has a url but no transport type |
559
+ | `claude.mcp.reserved-name` | error | spec-required | [code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) | Claude MCP server uses a reserved name that Claude skips at load |
560
+ | `security.hardcoded-secret` | error | security | _cross-provider token-shape judgement, not a quoted rule_ | MCP config contains a token-shaped literal |
561
+ | `mcp.literal-env` | warning | security | [code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp) | MCP env value is a literal secret-shaped key instead of interpolation |
562
+ | `mcp.literal-credential` | warning | security | _cross-provider header judgement; no single vendor page states it_ | MCP headers, http_headers, or auth value is a literal secret instead of interpolation |
563
+ | `budget.agents-md` | info | heuristic | _size hint, not a requirement_ | AGENTS.md longer than the heuristic line budget (>150 lines) |
564
+ | `budget.claude-md` | info | vendor-recommendation | [code.claude.com/docs/en/memory](https://code.claude.com/docs/en/memory) | CLAUDE.md longer than the documented size target (~200 lines) |
565
+ | `budget.agents` | info | heuristic | _size hint, not a requirement_ | More agent definitions than a focused set (>8 files in .claude/agents) |
566
+ | `budget.mcp` | info | heuristic | _size hint, not a requirement_ | Project MCP server count above the point where tool selection may degrade |
567
+ | `codex.budget.instructions` | info | vendor-recommendation | [learn.chatgpt.com/docs/agent-configuration/agents-md](https://learn.chatgpt.com/docs/agent-configuration/agents-md) | Codex AGENTS.md chain exceeds project_doc_max_bytes (32 KiB default) |
568
+ | `cursor.rule.too-large` | info | vendor-recommendation | [cursor.com/docs/rules](https://cursor.com/docs/rules) | Cursor project rule exceeds the 500-line recommendation |
569
+ | `windsurf.rule.too-large` | info | vendor-recommendation | [docs.windsurf.com/windsurf/cascade/memories](https://docs.windsurf.com/windsurf/cascade/memories) | Windsurf workspace rule exceeds the 12,000-character limit |
570
+ | `windsurf.rule.global-too-large` | info | vendor-recommendation | [docs.windsurf.com/windsurf/cascade/memories](https://docs.windsurf.com/windsurf/cascade/memories) | Windsurf global_rules.md exceeds the 6,000-character limit |
571
+ | `windsurf.rule.missing-trigger` | warning | spec-required | [docs.windsurf.com/windsurf/cascade/memories](https://docs.windsurf.com/windsurf/cascade/memories) | Windsurf workspace rule is missing frontmatter trigger |
572
+ | `antigravity.rule.missing-trigger` | warning | spec-required | [antigravity.google/docs/rules](https://antigravity.google/docs/rules) | Antigravity modular rule is missing a frontmatter trigger |
573
+ | `antigravity.rule.invalid-trigger` | warning | spec-required | [antigravity.google/docs/rules](https://antigravity.google/docs/rules) | Antigravity modular rule trigger is not always_on, model_decision, glob, or manual |
574
+ | `antigravity.rule.missing-description` | warning | spec-required | [antigravity.google/docs/rules](https://antigravity.google/docs/rules) | Antigravity model_decision rule is missing a description |
575
+ | `antigravity.rule.missing-globs` | warning | spec-required | [antigravity.google/docs/rules](https://antigravity.google/docs/rules) | Antigravity glob rule is missing globs |
576
+ | `antigravity.rule.too-large` | warning | spec-required | [antigravity.google/docs/rules](https://antigravity.google/docs/rules) | Antigravity rule exceeds 24,000 bytes |
577
+ | `windsurf.mcp.no-launch` | error | spec-required | [docs.windsurf.com/windsurf/cascade/mcp](https://docs.windsurf.com/windsurf/cascade/mcp) | Windsurf MCP server declares neither command, serverUrl, nor url |
578
+ | `devin.mcp.no-launch` | error | spec-required | [docs.devin.ai/cli/extensibility/mcp/configuration](https://docs.devin.ai/cli/extensibility/mcp/configuration) | Devin MCP server declares neither command nor url |
579
+ | `windsurf.hook.unknown-event` | error | spec-required | [docs.devin.ai/desktop/cascade/hooks](https://docs.devin.ai/desktop/cascade/hooks) | Windsurf hook registered under an event name that is never dispatched |
580
+ | `windsurf.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Windsurf hook points at a script that does not exist, so it never runs |
581
+ | `windsurf.hook.command-without-command` | error | spec-required | [docs.devin.ai/desktop/cascade/hooks](https://docs.devin.ai/desktop/cascade/hooks) | Windsurf hook declares neither command nor powershell |
582
+ | `devin.hook.unknown-event` | error | spec-required | [docs.devin.ai/cli/extensibility/hooks/overview](https://docs.devin.ai/cli/extensibility/hooks/overview) | Devin hook registered under an event name that is never dispatched |
583
+ | `devin.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Devin hook points at a script that does not exist, so it never runs |
584
+ | `devin.hook.invalid-group` | error | spec-required | [docs.devin.ai/cli/extensibility/hooks/overview](https://docs.devin.ai/cli/extensibility/hooks/overview) | Devin hook group is missing a hooks array |
585
+ | `devin.hook.command-without-command` | error | spec-required | [docs.devin.ai/cli/extensibility/hooks/overview](https://docs.devin.ai/cli/extensibility/hooks/overview) | Devin command hook declares no command |
586
+ | `devin.hook.prompt-without-prompt` | error | spec-required | [docs.devin.ai/cli/extensibility/hooks/overview](https://docs.devin.ai/cli/extensibility/hooks/overview) | Devin prompt hook declares no prompt |
587
+ | `devin.hook.unknown-handler-type` | error | spec-required | [docs.devin.ai/cli/extensibility/hooks/overview](https://docs.devin.ai/cli/extensibility/hooks/overview) | Devin hook handler type is not command or prompt |
588
+ | `codex.hook.unknown-event` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex hook registered under an event name that is never dispatched |
589
+ | `codex.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Codex hook points at a script that does not exist, so it never runs |
590
+ | `codex.hook.invalid-group` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex hook group is missing a hooks array, or matcher is not a string |
591
+ | `codex.hook.command-without-command` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex command hook declares no command |
592
+ | `codex.hook.mcp-tool-without-server-or-tool` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex MCP tool hook declares no server or tool |
593
+ | `codex.hook.unknown-handler-type` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex hook handler type is not command, mcp_tool, prompt, or agent |
594
+ | `codex.hook.incompatible-handler` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex prompt or agent handler is parsed but skipped, or SessionEnd uses mcp_tool |
595
+ | `codex.hook.timeout-out-of-bounds` | error | spec-required | [learn.chatgpt.com/docs/hooks](https://learn.chatgpt.com/docs/hooks) | Codex SessionEnd or Interrupt hook timeout is outside 1–3 seconds |
596
+ | `gemini.hook.unknown-event` | error | spec-required | [geminicli.com/docs/hooks](https://geminicli.com/docs/hooks/) | Gemini hook registered under an event that is never dispatched |
597
+ | `gemini.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Gemini hook points at a script that does not exist, so it never runs |
598
+ | `gemini.hook.invalid-group` | error | spec-required | [geminicli.com/docs/hooks](https://geminicli.com/docs/hooks/) | Gemini hook group is missing the required nested hooks array, or matcher is not a string |
599
+ | `gemini.hook.command-without-command` | error | spec-required | [geminicli.com/docs/hooks](https://geminicli.com/docs/hooks/) | Gemini command hook is missing the command field |
600
+ | `gemini.hook.unknown-handler-type` | error | spec-required | [geminicli.com/docs/hooks](https://geminicli.com/docs/hooks/) | Gemini hook omits the required type or uses a value other than command |
601
+ | `cursor.hook.unknown-event` | error | spec-required | [cursor.com/docs/hooks](https://cursor.com/docs/hooks) | Cursor hook registered under an event that is never dispatched |
602
+ | `cursor.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Cursor hook points at a script that does not exist, so it never runs |
603
+ | `cursor.hook.command-without-command` | error | spec-required | [cursor.com/docs/hooks](https://cursor.com/docs/hooks) | Cursor hook is missing the required command field |
604
+ | `cursor.hook.unknown-handler-type` | error | spec-required | [cursor.com/docs/hooks](https://cursor.com/docs/hooks) | Cursor hook declares a type other than command or prompt |
605
+ | `kiro.hook.unknown-event` | error | spec-required | [kiro.dev/docs/ide/whats-new-v1/hooks](https://kiro.dev/docs/ide/whats-new-v1/hooks/) | Kiro hook registered under a trigger that is never dispatched |
606
+ | `kiro.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Kiro command hook points at a script that does not exist, so it never runs |
607
+ | `kiro.hook.command-without-command` | error | spec-required | [kiro.dev/docs/hooks](https://kiro.dev/docs/hooks/) | Kiro command hook is missing the command field |
608
+ | `kiro.hook.prompt-without-prompt` | error | spec-required | [kiro.dev/docs/hooks](https://kiro.dev/docs/hooks/) | Kiro agent hook is missing the required prompt field |
609
+ | `kiro.hook.unknown-handler-type` | error | spec-required | [kiro.dev/docs/hooks](https://kiro.dev/docs/hooks/) | Kiro hook action type is missing or is not command or agent |
610
+ | `kiro.hook.invalid-group` | error | spec-required | [kiro.dev/docs/hooks](https://kiro.dev/docs/hooks/) | Kiro hook file is missing version v1 or a hooks array |
611
+ <!-- rules:end -->
612
+
613
+ ### Coverage in 2.0.0
521
614
 
522
615
  Each cell is one coverage dimension. There is no `full` / `partial` label: a
523
616
  documented global or static location that is not opened stays **unread**.
@@ -532,29 +625,28 @@ Dimensions:
532
625
 
533
626
  | Ecosystem | project discovery | global discovery | schema | precedence | conformance |
534
627
  |-----------|-------------------|------------------|--------|------------|-------------|
535
- | Agent Skills | `.agents/skills` (and Cursor / Codex / Command Code skill trees) | `--global` `~/.agents/skills`, `~/.codex/skills` | required `name` / `description` plus optional `compatibility` / `metadata` / `allowed-tools`; 500-line info | n/a | `agent-skills` fixture |
628
+ | Agent Skills | `.agents/skills` (and Cursor / Codex / Command Code skill trees). Default `skills/` uses the Claude schema, not this contract | `--global` `~/.agents/skills`, `~/.codex/skills` | required `name` / `description` plus optional `compatibility` / `metadata` / `allowed-tools`; 500-line info | n/a | `agent-skills` fixture |
536
629
  | AGENTS.md | nested walk-up | n/a | no required fields | nearest-wins | tests |
537
- | Claude Code | project settings, skills, agents, `CLAUDE.md`, `.mcp.json` / `.claude/mcp.json` | unread `~/.claude/settings.json`, managed policy, marketplace plugins | 33 events; required handler `type`; MCP reserved names; first-paragraph skill description; listing budget | walk-up `CLAUDE.md` / `.claude/agents` | `claude-json` fixture |
630
+ | Claude Code | project settings, skills, agents, commands, `CLAUDE.md`, `.mcp.json` / `.claude/mcp.json`; in-tree plugin hooks / skills / agents / commands / `.mcp.json` | `--global` `$CLAUDE_CONFIG_DIR` or `~/.claude` (skills, settings, agents, `CLAUDE.md`, rules, commands) and `$CLAUDE_CONFIG_DIR/.claude.json` or `~/.claude.json` (user + matching local `mcpServers`); unread managed policy, marketplace plugins, other projects' local MCP | 33 events; required handler `type`; MCP reserved names; first-paragraph skill description; listing budget | walk-up `CLAUDE.md` / `.claude/agents` / `.claude/commands`; user+project+local layers inventoried | `claude-json` fixture |
538
631
  | Command Code | git-root project files; per-directory `AGENTS.md` else `.commandcode/AGENTS.md` | `--global` `~/.commandcode/*`; unread `projects/{slug}/mcp.json` | 4 events; command handlers; Agent Skills; MCP `transport` / `type` | settings merge; project+user hooks coexist; skill/MCP shadow | `commandcode` fixture |
539
- | Codex | `.codex/config.toml`, `.codex/skills`, AGENTS chain | `--global` `~/.codex/AGENTS.override.md` then `AGENTS.md` | TOML MCP; `project_doc_max_bytes`; Agent Skills | override > `AGENTS.md` > fallbacks; one file per dir; root→cwd; `project_root_markers` | `codex-toml` fixture |
540
- | VS Code | `.github/hooks` without `version: 1`, instruction files, `.github/agents`, `.vscode/mcp.json` | `--global` `~/.copilot/hooks` without `version: 1`; unread policy dirs | 8 events; command-only | workspace over user | `vscode-hooks`, `vscode-json` |
541
- | Copilot CLI | `.github/hooks` with `version: 1` | `--global` `~/.copilot/hooks` with `version: 1`; unread `/etc/github-copilot/policy.d` | camelCase + PascalCase map; `bash` / `powershell` / `command`; `cwd`; `timeoutSec`; prompt on `sessionStart` | documented sources coexist; policy unread | `copilot-hooks` fixture |
542
- | Cursor | nested `.cursor/skills`, `.cursor/mcp.json`, `.cursor/rules` | unread documented Cursor user/global paths | Agent Skills; MCP launch; 500-line rules | n/a | `cursor-json` fixture |
543
- | Grok | none | none | none | n/a | none |
544
- | Antigravity | `.agents/mcp_config.json` | none | `serverUrl` launch | n/a | `antigravity-json` fixture |
545
- | Gemini | `.gemini/settings.json` | unread `~/.gemini/settings.json` unless `--global` is wired for it (it is not) | `command` / `url` / `httpUrl`; underscore-alias warning | n/a | `gemini-json` fixture |
546
- | Windsurf | none | unread (official page is global-only) | none | n/a | none |
547
- | Kiro | none | none | none | n/a | none |
548
- | Cline | none | none | none | n/a | none |
549
- | Roo | none | none | none | n/a | none |
550
- | Kilo | none | none | none | n/a | none |
632
+ | Codex | `.codex/hooks.json` and inline `[hooks]` in `.codex/config.toml` on the ancestor walk; `.codex/skills`; AGENTS chain | `--global` `$CODEX_HOME` / `~/.codex` `hooks.json` and `config.toml` (MCP and `[hooks]`); `AGENTS.override.md` then `AGENTS.md`; unread system, managed, requirements, profiles, plugins, trust | 14 hook events; `command` / `mcp_tool`; `prompt` and `agent` parsed but skipped; SessionEnd rejects `mcp_tool`; SessionEnd and Interrupt timeout 1–3; TOML MCP; `project_doc_max_bytes`; Agent Skills | both hook files in a layer are inventoried; trust is not modeled; override > `AGENTS.md` > fallbacks; one file per dir; root→cwd; `project_root_markers`; MCP user+project both inventoried | `codex-hooks`, `codex-toml` fixtures |
633
+ | VS Code | `.github/hooks` without `version: 1` (legacy Local), instruction files, `.github/agents` (frontmatter hooks as vscode-native), `.vscode/mcp.json` `servers` | `--global` `~/.copilot/hooks` without `version: 1`; unread policy dirs; unread marketplace plugin `hooks.json` (install path unpublished) | 8 PascalCase events; command-only. New Copilot hooks are the Copilot row | workspace over user | `vscode-hooks`, `vscode-json` |
634
+ | Copilot CLI | `.github/hooks` with `version: 1`; inline `hooks` in `.github/copilot/settings.json` and `settings.local.json` | `--global` `$COPILOT_HOME` / `~/.copilot/hooks` with `version: 1`, `settings.json`, and `mcp-config.json` (`mcpServers`); unread `/etc/github-copilot/policy.d` | camelCase + PascalCase map; `bash` / `powershell` / `command` / `exec` / `http`; `cwd`; `timeoutSec`; prompt on `sessionStart`; user MCP `command` / `url` | documented sources coexist; policy unread; Copilot also reads `.claude/settings.json` but this scanner lints that file as Claude. Workspace `.mcp.json` stays the shared profile | `copilot-hooks` fixture |
635
+ | Cursor | nested `.cursor/skills`; `.cursor/mcp.json`, `.cursor/hooks.json` and `.cursor/rules` on the ancestor walk only (not descendant package trees from repo root) | `--global` `~/.cursor/hooks.json` and `~/.cursor/mcp.json`; unread MDM/team hooks; unread `~/.cursor/skills/` until a page quotes that directory; unread third-party Claude import as Cursor schema | Agent Skills; MCP launch; 500-line rules; 21 hook events, optional `type` (`command` / `prompt`), required `command`. Missing `version` is not a finding | n/a | `cursor-json` fixture |
636
+ | Grok | `.grok/config.toml` walk-up; `.grok/hooks/*.json`; `.grok/skills`; `.grok/rules/*.md`; `Agents.md` / `AGENT.md` | `--global` `$GROK_HOME` or `~/.grok` config/hooks/skills; unread managed, requirements, plugins, `[skills] paths`, agents, credentials | `command` / `url` MCP (no `type`); 14 events; command/http; frontmatter required, name/description optional; no rules cap | closer project wins; project same-name replaces user | `grok-toml` fixture |
637
+ | Antigravity | `.agents/mcp_config.json`; `.agents/hooks.json`, `GEMINI.md`, `.agents/GEMINI.md`, `.agents/rules/*.md`, and legacy `.agent/rules/*.md` on the ancestor walk. Workspace `AGENTS.md` stays the AGENTS.md row. `.agents/skills` stays the Agent Skills row | `--global` `~/.gemini/config` `mcp_config.json`, `hooks.json`, `rules/*.md`, `skills/`, `AGENTS.md`, and `GEMINI.md`, plus `~/.gemini/AGENTS.md` and `GEMINI.md`. Unread plugin hooks, plugin rules, plugin skills, `~/.gemini/antigravity-cli/settings.json`, `~/.gemini/antigravity-cli/skills/`, `~/.gemini/skills/`, and `~/.gemini/antigravity/skills/` | `command` / `serverUrl` (`url` is an alias; `httpUrl` is not a launch field); 6 hook events; direct handlers or matcher groups; `type` defaults to `command`; modular rule `trigger`; 24,000-byte rule limit | n/a | `antigravity-json`, `antigravity-hooks` fixtures |
638
+ | Gemini | `.gemini/settings.json` — MCP and `hooks` | `--global` `~/.gemini/settings.json` (hooks and MCP together); unread `/etc/gemini-cli/settings.json`, extension hooks, and `mcp-oauth-tokens.json` | `command` / `url` / `httpUrl`; underscore-alias warning; 11 hook events, required `type` (`command` only), nested groups, `$GEMINI_PROJECT_DIR` | n/a | `gemini-json` fixture |
639
+ | Windsurf | `.devin/rules/*.md` (preferred), `.windsurf/rules/*.md` (fallback), `.windsurfrules`; `.windsurf/hooks.json`; `.windsurf/skills`; portable `AGENTS.md` / `agents.md` | `--global` `~/.codeium/windsurf/mcp_config.json`, `memories/global_rules.md`, `hooks.json`, and `skills`; unread auto memories, system rules/hooks/skills, JetBrains `~/.codeium/hooks.json` | 12k / 6k character rules; workspace `trigger`; MCP `command` / `serverUrl` / `url` (no `type`); 12 snake_case hook events; `command` / `powershell`; Agent Skills on `.windsurf/skills` | n/a (no project MCP on this row; both rule trees inventoried). Devin CLI hooks and MCP are the next row | `windsurf-rules` fixture |
640
+ | Devin CLI | `.devin/hooks.v1.json`; `"hooks"` in `.devin/config.json` and `config.local.json`; `.devin/mcp_config.json` and `mcp_config.local.json`; `mcpServers` left in those config files; `.devin/skills` | `--global` `~/.config/devin/config.json`, `mcp_config.json`, and `skills`; unread Windows `%APPDATA%\devin` | 8 events (`PostCompaction`, not `PostCompact`); `command` / `prompt`; MCP `command` / `url` (`transport` `http` or `sse` is not a check); Agent Skills | Claude settings Devin also loads stay Claude findings | unit tests |
641
+ | Kiro | `.kiro/hooks/*.json`, `.kiro/skills`, `.kiro/agents/**/*.json` and `*.md` on the ancestor walk | `--global` `~/.kiro/hooks/`, `~/.kiro/skills`, `~/.kiro/agents/` | 13 PascalCase triggers, including `SessionEnd`, `PreCompact`, `PostCompact`; `command` / `agent`; Agent Skills. Agents are inventory only: the name is the relative path without the extension, and no field is required | nearer workspace agent shadows a farther one and a global one with the same name | unit tests |
642
+ | Cline | `.cline/skills` | `--global` `~/.cline/skills`; unread hooks (no quoted event list), unread agents | Agent Skills | n/a | unit tests |
643
+ | Roo | `.roo/skills` | `--global` `~/.roo/skills` | Agent Skills | n/a | unit tests |
644
+ | Kilo | `.kilo/skills` | `--global` `~/.kilo/skills`; unread extra paths in `kilo.jsonc` | Agent Skills | n/a | unit tests |
551
645
  | OpenCode | `opencode.json(c)` V1 and V2 | none | V2 local `command` must be an argv array | n/a | `opencode-json` fixture |
552
- | Junie | none | none | none | n/a | none |
646
+ | Junie | `.junie/skills` | `--global` `~/.junie/skills`; unread `--skill-location` / extension skills | Agent Skills with optional `description`; `name` need not match the folder | n/a | unit tests |
553
647
  | Continue | `.continue/config.yaml`, `.continue/mcpServers/*` | none | launch `command` / `url` / `uses`; standalone YAML `name` / `version` / `schema` | n/a | `continue-yaml`, `continue-mcpservers` |
554
648
 
555
- Cursor project rules (`.cursor/rules/**/*.mdc`) are a separate surface: discovery plus `cursor.rule.too-large` at info. Claude `.claude/rules/**/*.md` is inventoried and has no published line budget.
556
-
557
- Old 0.7 rule ids still work in `ignoreRules` and `explain` (for example `hook.missing-script` aliases `claude.hook.missing-script`).
649
+ Cursor project rules (`.cursor/rules/**/*.mdc`) are a separate surface: discovery plus `cursor.rule.too-large` at info. Claude `.claude/rules/**/*.md` is inventoried and has no published line budget. Windsurf workspace rules use a 12,000-character budget (`windsurf.rule.too-large`); do not apply the Cursor 500-line check. Antigravity modular rules require `trigger` and a raw file over 24,000 bytes is `antigravity.rule.too-large`. Portable `AGENTS.md` / `agents.md` stay `sourceProvider: unknown` — this scanner does not claim exclusive Windsurf or Antigravity ownership.
558
650
 
559
651
  Agent definitions are checked for structure only. A Claude agent's frontmatter
560
652
  `name` is not compared to the filename; it must be lowercase letters and
@@ -575,19 +667,32 @@ illustration, not a pointer.
575
667
  ### Where it looks for hooks
576
668
 
577
669
  The Claude hooks reference lists seven places a hook can be registered. agentscan
578
- reads the four that live inside the scanned project, plus VS Code workspace hooks
579
- and Command Code settings hooks:
670
+ reads the project settings files, in-tree plugin and command-file hooks,
671
+ `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`)
672
+ under `--global`, plus VS Code workspace hooks and Command Code settings hooks:
580
673
 
581
674
  | Registered in | Script paths resolve against |
582
675
  |---|---|
583
676
  | `.claude/settings.json` · `.claude/settings.local.json` | project root, `${CLAUDE_PROJECT_DIR}` |
677
+ | `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`) under `--global` | scanned project root, `${CLAUDE_PROJECT_DIR}` |
584
678
  | A plugin's `hooks/hooks.json` (a directory with `.claude-plugin/plugin.json`, or the scan root itself) | the plugin root, `${CLAUDE_PLUGIN_ROOT}` |
585
- | `SKILL.md` frontmatter | the skill's own directory, then the project root |
679
+ | `SKILL.md` frontmatter (including in-tree plugin skills) | the skill's own directory, then the project root |
680
+ | `.claude/commands/*.md` frontmatter | the command file's directory, then the project root |
586
681
  | `.claude/agents/*.md` frontmatter | the agent file's directory, then the project root |
587
682
  | `.github/hooks/*.json` without `version: 1` (native VS Code) | project root and the hooks directory |
588
683
  | `.github/hooks/*.json` with `version: 1` (Copilot CLI) | project root, `cwd`, host `bash` / `powershell` / `command` |
589
- | `~/.copilot/hooks` under `--global` | same split on `version: 1` |
684
+ | `.github/copilot/settings.json` · `.github/copilot/settings.local.json` (Copilot CLI inline) | project root and the settings directory; always `copilot-cli` |
685
+ | `~/.copilot/hooks` and `~/.copilot/settings.json` (or `$COPILOT_HOME/…`) under `--global` | same Copilot CLI / native VS Code split on hook files; settings hooks are always `copilot-cli` |
590
686
  | `.commandcode/settings.json` · `.commandcode/settings.local.json` (and user settings under `--global`) | Command Code project root (git root, or cwd outside a git repo), `$COMMANDCODE_PROJECT_DIR`, `$COMMANDCODE_CWD` |
687
+ | `.grok/hooks/*.json` | project root and the hooks directory |
688
+ | `$GROK_HOME/hooks` or `~/.grok/hooks` under `--global` | same Grok 14-event / command-or-http split |
689
+ | `.github/agents/*.agent.md` frontmatter | vscode-native (command-only); project root and the agent file's directory |
690
+ | `.kiro/hooks/*.json` | project root; array-of-hooks, not Claude event-keyed objects |
691
+ | `~/.kiro/hooks/` under `--global` | scanned project root |
692
+ | `.cursor/hooks.json` | project root |
693
+ | `~/.cursor/hooks.json` under `--global` | scanned project root |
694
+ | `.gemini/settings.json` `hooks` | project root, `$GEMINI_PROJECT_DIR` |
695
+ | `~/.gemini/settings.json` `hooks` under `--global` | scanned project root, `$GEMINI_PROJECT_DIR` |
591
696
 
592
697
  `${CLAUDE_PLUGIN_ROOT}` is expanded **only** for a hook that came from a plugin.
593
698
  In a settings file it names nothing, so the path is skipped rather than guessed
@@ -623,35 +728,117 @@ first.
623
728
  should add a `next-*` skill". That needed a hand-maintained registry, and the
624
729
  version that existed fired on none of the 17 projects measured, so it was
625
730
  removed. Skill provenance comes from `skills-lock.json` instead.
626
- - **No rule produces `refresh` or `keep`.** Comparing a skill's content against
627
- the `computedHash` in `skills-lock.json` is the obvious `refresh` source; the
731
+ - **No skill-content hash check.** Comparing a skill's content against
732
+ the `computedHash` in `skills-lock.json` is the obvious source of one; the
628
733
  hash algorithm used by the installing tool is not documented here, and it is
629
734
  not reproducible from `SKILL.md` bytes alone, so it is not implemented.
630
735
  - **Runtime split.** Source development and the composite Action use Bun; the
631
736
  published `dist/cli.js` runs on Node 20.11+ or Bun. The source entrypoint uses
632
737
  Bun's `import.meta.dir` / `import.meta.main` and is not the Node entrypoint.
738
+ - **Grok compatibility and unpublished surfaces are unread.** Managed
739
+ (`~/.grok/managed_config.toml`, `/etc/grok/managed_config.toml`) and
740
+ requirements TOML are not opened. Plugins, marketplaces, personas, and
741
+ `[skills] paths` (no quoted resolution base) stay unread. `.grok/agents/`
742
+ is unread because the page names the directory but not a filename pattern
743
+ or required fields. Claude / Cursor / `.mcp.json` files keep their own
744
+ schemas — this scanner does not add `consumedBy: grok` to them. Grok’s
745
+ “skip gitignored instruction files” is unread: agentscan does not honor
746
+ `.gitignore`. `auth.json` and `mcp_credentials.json` are never opened.
747
+ Spec/runtime Grok MCP checks skip shadowed same-name servers; secrets
748
+ still inspect every readable MCP file.
749
+ - **Codex admin and profile layers are unread.** System `/etc/codex/config.toml`,
750
+ managed preferences, `requirements.toml`, `$CODEX_HOME/<name>.config.toml`
751
+ profiles, plugin hooks and MCP, and `trust_level` are not opened. Project
752
+ `.codex/hooks.json` and inline `[hooks]` in `.codex/config.toml` are
753
+ inventoried on the ancestor walk even when that layer is untrusted. The
754
+ same two files under `$CODEX_HOME` (or `~/.codex`) are opened only with
755
+ `--global`. `auth.json` is never opened. Same-name user and project MCP
756
+ servers are both inventoried — the published page does not quote a
757
+ replace-entirely rule. User-file `project_doc_*` knobs are not applied.
758
+ Relative hook commands resolve against the scanned directory, not the git
759
+ root. SessionEnd and Interrupt timeouts outside 1–3 seconds are findings;
760
+ other events have no quoted maximum.
633
761
  - **Command Code local MCP is unread.** `~/.commandcode/projects/{project}/mcp.json`
634
762
  is keyed by a working-directory slug that the sessions page does not publish.
635
763
  Until it does, that path is skipped rather than guessed. Bundled Command Code
636
764
  skills and `--skill` launch flags are not project files and are not scanned.
637
- `mods.paths` are inventoried; the TypeScript is never executed or imported.
765
+ Command Code `mods` settings are not read, and mod TypeScript is never
766
+ executed or imported.
638
767
  `auth.json` and `mcp-tokens.json` are never opened. Command Code project
639
768
  root is the git root (or cwd outside a git repo); a child `.cursor` does
640
769
  not hide `<git-root>/.commandcode/`. Spec/runtime Command Code checks skip
641
770
  shadowed lower-precedence definitions; secrets still inspect every readable
642
771
  MCP file.
643
- - **Three Claude hook locations are unread.** `~/.claude/settings.json`, managed policy
644
- settings, and installed marketplace plugins under `~/.claude/plugins` all sit
645
- outside the scanned project, and the docs say a plugin's install directory
646
- changes on every update. A hook registered in one of those, pointing at a
647
- missing script, is not reported.
772
+ - **Two Claude hook locations stay unread.** Managed policy settings and
773
+ installed marketplace plugins under `~/.claude/plugins` sit outside the
774
+ scanned project, and the docs say a plugin's install directory changes
775
+ on every update. A hook registered in one of those, pointing at a
776
+ missing script, is not reported. User settings, skills, agents,
777
+ `CLAUDE.md`, rules, and commands under `$CLAUDE_CONFIG_DIR` or
778
+ `~/.claude` are scanned only under `--global`. User `~/.claude.json`
779
+ MCP is scanned only under `--global`: top-level `mcpServers` and
780
+ `projects.<scanned-path>.mcpServers`. Other projects' local servers
781
+ are skipped. A file without those keys is skipped, not
782
+ `config.unreadable`. In-tree plugin skills, agents, commands, and
783
+ `.mcp.json` are read; plugin agent frontmatter hooks are ignored
784
+ because Claude skips them.
785
+ - **Default `skills/` is Claude schema.** A repo-root `skills/` directory is
786
+ discovered, but `name` / directory-match from the Agent Skills spec are not
787
+ applied there. `.agents/skills`, `.cursor/skills`, `.codex/skills`,
788
+ `.commandcode/skills`, `.kiro/skills`, `.cline/skills`, `.roo/skills`,
789
+ `.kilo/skills`, and `.junie/skills` use the Agent Skills contract. Junie
790
+ does not require `description` and does not require `name` to match the
791
+ folder.
648
792
  - **Copilot CLI policy hooks are unread.** `/etc/github-copilot/policy.d` and the
649
793
  Windows policy directory are machine-wide admin files. User
650
- `~/.copilot/hooks` is scanned only under `--global`. Inline Copilot
651
- `.github/copilot/settings.json` hooks are unread.
652
- - **Gemini user settings are unread.** `~/.gemini/settings.json` is outside a
653
- normal project scan. Windsurf's official MCP page is global-only and is not
654
- scanned.
794
+ `~/.copilot/hooks` and `~/.copilot/settings.json` (or `$COPILOT_HOME/…`)
795
+ are scanned only under `--global`. Plugin `hooks.json` stays unread.
796
+ Copilot also reads repository `.claude/settings.json`; this scanner still
797
+ lints that file as Claude (required nested groups and required `type`).
798
+ Copilot-only event names there stay `claude.hook.unknown-event`.
799
+ - **Gemini system settings are unread.** `~/.gemini/settings.json` (hooks and
800
+ MCP together) is scanned only under `--global`. `/etc/gemini-cli/settings.json`
801
+ and extension-provided hooks stay unread. `mcp-oauth-tokens.json` is never
802
+ opened.
803
+ - **Cursor managed hooks are unread.** `~/.cursor/hooks.json` is scanned only
804
+ under `--global`. MDM paths (`/etc/cursor/hooks.json` and the macOS /
805
+ Windows equivalents) and dashboard-synced team hooks stay unread. Cursor's
806
+ third-party import of Claude hook files stays on the Claude profile.
807
+ - **Antigravity global files.** `~/.gemini/config/mcp_config.json`,
808
+ `hooks.json`, `rules/*.md`, `skills/`, `AGENTS.md`, and `GEMINI.md`, plus
809
+ `~/.gemini/AGENTS.md` and `~/.gemini/GEMINI.md`, are scanned only under
810
+ `--global`. Project `.agents/mcp_config.json`, `.agents/hooks.json`,
811
+ `GEMINI.md`, `.agents/GEMINI.md`, `.agents/rules/*.md`, and legacy
812
+ `.agent/rules/*.md` are read on the ancestor walk. Workspace `AGENTS.md`
813
+ stays the portable policy file, so the 24,000-byte rule check does not
814
+ apply to it. That check counts raw bytes, before `@[label](path)`
815
+ expansion, and the 20,000-token aggregate is not checked. `rules.json`
816
+ paths are not followed. `url` is an alias of `serverUrl`. `httpUrl` alone
817
+ is still not a launch field. Plugin hooks, plugin rules, plugin skills,
818
+ `~/.gemini/antigravity-cli/settings.json`,
819
+ `~/.gemini/antigravity-cli/skills/`, `~/.gemini/skills/`, and
820
+ `~/.gemini/antigravity/skills/` stay unread. Hook `timeout` has no quoted
821
+ range, so it is not checked. Global skills use the Agent Skills contract,
822
+ which requires `name` even though the Antigravity skills page says `name`
823
+ defaults to the folder.
824
+ - **Cline hooks stay unread.** Cline's hooks page did not quote an event
825
+ identifier table. Cline agents are unread for the same reason. Kiro agents
826
+ are inventoried from `.kiro/agents/**/*.json` and `*.md` on the ancestor
827
+ walk, and from `~/.kiro/agents/` under `--global`. The name is the path
828
+ relative to the agents directory without the extension. A nearer workspace
829
+ agent shadows a farther one and a global one with the same name. No field
830
+ is required, so there is no `kiro.agent.*` check.
831
+ - **VS Code marketplace plugin hooks are unread.** In-tree Claude plugins
832
+ stay Claude. VS Code's `hooks.json` / `hooks/hooks.json` inside an
833
+ installed plugin package is not a project path.
834
+ - **Windsurf Devin CLI MCP and auto memories are unread.** Cascade MCP is
835
+ global-only (`~/.codeium/windsurf/mcp_config.json`, `--global`). The Devin
836
+ Local agent uses unpublished CLI config files — that path is not guessed.
837
+ Auto-generated files under `~/.codeium/windsurf/memories/` other than
838
+ `global_rules.md` are not opened. System / enterprise rule, hook, and
839
+ skill directories (`/etc/devin/rules`, `/etc/windsurf/rules`,
840
+ `/etc/windsurf/hooks.json`, `/etc/windsurf/skills/`) stay unread. There
841
+ is no quoted project MCP path. JetBrains `~/.codeium/hooks.json` is unread.
655
842
  - **Bounded reads.** A `SKILL.md` is read to 64 KB and a policy file to 100 KB,
656
843
  so `skill.broken-reference` and `policyLines` see only that much of a larger
657
844
  file. This is reported — `scan.truncated`, at `info` — rather than left
@@ -665,6 +852,40 @@ Notes for every published version are in [CHANGELOG.md](CHANGELOG.md).
665
852
  GitHub already has pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
666
853
  and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
667
854
 
855
+ **2.0.0** (12 November 2026) is breaking for configs that still name the 0.7
856
+ rule ids or the old threshold keys. `ignoreRules`, `ignoreFindings`, and
857
+ `explain` accept only the printed ids. `thresholds.skillDescriptionBytes`
858
+ and `thresholds.skills` are rejected; the listing budget is
859
+ `thresholds.skillListingChars`. It also ships the November 2026 vendor
860
+ contracts: Kiro agents, Antigravity rules, skills, and hooks, Codex hooks,
861
+ Cursor user MCP, Devin CLI hooks / MCP / skills, and the Copilot user MCP
862
+ file. **143 checks**, **647 tests**.
863
+
864
+ **1.4.0** (2 September 2026) closes two coverage gaps and ships the
865
+ 2026-09-02 re-verification: Copilot CLI inline `hooks` in
866
+ `.github/copilot/settings.json` / `settings.local.json` and `--global`
867
+ `$COPILOT_HOME` / `~/.copilot/settings.json`; `mcp.literal-credential` on
868
+ MCP `headers` / `http_headers` / `auth` (field paths only). Also the
869
+ seven false-positive corrections, spec content hashes, verbatim
870
+ conformance fixtures, and Windsurf Cascade hooks / skills from #13.
871
+ **103 checks**, **532 tests**. Policy hooks, plugin `hooks.json`, and
872
+ `.claude/settings.json` as Copilot stay unread.
873
+
874
+ **1.3.0** (31 August 2026) adds native Windsurf / Cascade coverage: workspace
875
+ `.devin/rules` and `.windsurf/rules`, portable `agents.md`, and `--global`
876
+ `mcp_config.json` plus `global_rules.md`. Devin CLI MCP, auto memories,
877
+ system rules, and a project MCP path stay unread.
878
+
879
+ **1.2.1** (31 August 2026) opens Codex user MCP (`$CODEX_HOME/config.toml`
880
+ or `~/.codex/config.toml`) under `--global`. Same-name user and project
881
+ servers are both inventoried. System, managed, requirements, profiles,
882
+ plugins, and `trust_level` stay unread. `auth.json` is never opened.
883
+
884
+ **1.2.0** (31 August 2026) adds a native Grok Build provider: project
885
+ `.grok/*` plus `$GROK_HOME` / `~/.grok/*` under `--global`. Compatibility
886
+ with Claude or Cursor is not schema identity. Agents, managed/requirements
887
+ TOML, plugins, `[skills] paths`, and `.gitignore` skip stay unread.
888
+
668
889
  **1.1.0** (31 August 2026) adds a native Command Code provider and finishes the
669
890
  correctness pass across registered checks: hook schema profiles, Agent Skills
670
891
  optional fields, Claude listing budget, Codex chain knobs, and Copilot CLI
@@ -703,7 +924,9 @@ Before publishing a release:
703
924
 
704
925
  1. Confirm `package.json` and `src/version.ts` contain the same version.
705
926
  2. Run `bun run build`, then verify `node dist/cli.js --version` prints it.
706
- 3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`.
927
+ 3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`. If a page
928
+ hash changed, re-read that page, update its `docs/spec/` capture and the
929
+ conformance fixture, then `bun run spec:record`.
707
930
  4. Refresh the test-count badge and version examples above from that release run.
708
931
  5. Add a section to [CHANGELOG.md](CHANGELOG.md) for the version, then publish
709
932
  the GitHub release from that text.