@chimix/agentscan 1.3.0 → 2.3.1
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.
- package/CHANGELOG.md +384 -0
- package/README.md +369 -171
- package/dist/cli.js +11164 -9974
- package/package.json +5 -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-
|
|
9
|
-
<img src="https://img.shields.io/badge/tests-
|
|
8
|
+
<img src="https://img.shields.io/badge/checks-163-111111?style=flat-square" alt="163 checks">
|
|
9
|
+
<img src="https://img.shields.io/badge/tests-695%20passing-111111?style=flat-square" alt="695 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-
|
|
15
|
+
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-2.3.1-111111?style=flat-square" alt="Changelog"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
19
|
-
<strong>
|
|
20
|
-
<sub>An offline linter for Claude Code, Command Code, Grok Build, Windsurf, portable Agent Skills, nested AGENTS.md, Copilot CLI hooks, and the MCP / hooks / rules surfaces
|
|
19
|
+
<strong>163 checks · offline · 0 network calls on <code>check</code> · provenance on every rule</strong><br>
|
|
20
|
+
<sub>An offline linter for Claude Code, Claude Desktop, Command Code, Grok Build, Windsurf, Kiro, Qwen Code, Factory Droid, Amp, Zed, Goose, 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
|
---
|
|
@@ -65,7 +65,7 @@ No AI, no network on `check`. Read the config, read the disk, compare:
|
|
|
65
65
|
```
|
|
66
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
|
|
68
|
+
3. Check 163 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
|
```
|
|
@@ -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
|
|
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
|
|
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`, `~/.
|
|
189
|
+
| `--global` | Also scan user skills (`$CLAUDE_CONFIG_DIR/skills` or `~/.claude/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
|
|
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": "
|
|
234
|
+
"version": "2.0.0",
|
|
235
235
|
"root": "/path/to/project",
|
|
236
236
|
"factsSummary": {
|
|
237
237
|
"packageManager": "bun",
|
|
@@ -259,18 +259,27 @@ Same tree → same sorted findings, with stable unique `id`s.
|
|
|
259
259
|
|
|
260
260
|
### `--global`
|
|
261
261
|
|
|
262
|
-
Adds
|
|
263
|
-
`~/.agents/skills`, `$
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
262
|
+
Adds user skill dirs (`$CLAUDE_CONFIG_DIR/skills` or `~/.claude/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
|
|
272
277
|
never opens `auth.json`, `mcp-tokens.json`, Codex `auth.json`, or Grok
|
|
273
|
-
`auth.json` / `mcp_credentials.json`.
|
|
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.
|
|
274
283
|
|
|
275
284
|
Lockfile checks stay project-scoped — a project lockfile cannot pin a skill that
|
|
276
285
|
lives in your home directory, so reporting one as "not in the lockfile" could
|
|
@@ -350,7 +359,7 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
|
|
|
350
359
|
|
|
351
360
|
```json
|
|
352
361
|
{
|
|
353
|
-
"skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".
|
|
362
|
+
"skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".commandcode/skills", ".grok/skills", ".windsurf/skills"],
|
|
354
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"],
|
|
355
364
|
"policyFiles": ["AGENTS.md", "CLAUDE.md"],
|
|
356
365
|
"ignoreSkills": [],
|
|
@@ -427,113 +436,201 @@ Every check lives in `src/checks/` and runs on every `check`. `agentscan rules`
|
|
|
427
436
|
lists all of them with their ids; `agentscan explain <id>` details any finding.
|
|
428
437
|
|
|
429
438
|
Most validate one discovered item against its own file on disk. Budget and
|
|
430
|
-
hygiene checks are **info** (hidden unless `--verbose`).
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
|
458
|
-
|
|
459
|
-
| `
|
|
460
|
-
| `
|
|
461
|
-
| `
|
|
462
|
-
| `
|
|
463
|
-
| `
|
|
464
|
-
| `
|
|
465
|
-
| `
|
|
466
|
-
| `
|
|
467
|
-
| `
|
|
468
|
-
| `claude.hook.
|
|
469
|
-
| `claude.hook.
|
|
470
|
-
| `claude.hook.
|
|
471
|
-
| `claude.hook.
|
|
472
|
-
| `
|
|
473
|
-
| `
|
|
474
|
-
| `
|
|
475
|
-
| `
|
|
476
|
-
| `
|
|
477
|
-
| `
|
|
478
|
-
| `copilot.hook.
|
|
479
|
-
| `copilot.hook.
|
|
480
|
-
| `copilot.hook.
|
|
481
|
-
| `copilot.hook.
|
|
482
|
-
| `
|
|
483
|
-
| `
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
486
|
-
| `
|
|
487
|
-
| `
|
|
488
|
-
| `
|
|
489
|
-
| `
|
|
490
|
-
| `
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
496
|
-
| `
|
|
497
|
-
| `
|
|
498
|
-
| `
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
501
|
-
| `
|
|
502
|
-
| `
|
|
503
|
-
| `
|
|
504
|
-
| `
|
|
505
|
-
| `
|
|
506
|
-
| `claude.
|
|
507
|
-
| `claude.
|
|
508
|
-
| `
|
|
509
|
-
| `
|
|
510
|
-
| `
|
|
511
|
-
| `
|
|
512
|
-
| `
|
|
513
|
-
| `
|
|
514
|
-
| `
|
|
515
|
-
| `
|
|
516
|
-
| `
|
|
517
|
-
| `
|
|
518
|
-
| `
|
|
519
|
-
| `
|
|
520
|
-
| `
|
|
521
|
-
| `
|
|
522
|
-
| `
|
|
523
|
-
| `
|
|
524
|
-
| `
|
|
525
|
-
| `
|
|
526
|
-
| `
|
|
527
|
-
| `
|
|
528
|
-
| `
|
|
529
|
-
| `
|
|
530
|
-
| `
|
|
531
|
-
| `
|
|
532
|
-
| `
|
|
533
|
-
| `
|
|
534
|
-
| `
|
|
535
|
-
|
|
536
|
-
|
|
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/info | 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/warning | 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 non-empty markdown line |
|
|
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` | error | spec-required | [code.claude.com/docs/en/sub-agents](https://code.claude.com/docs/en/sub-agents) | Agent frontmatter name is one Claude Code skips |
|
|
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` | info | 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` | warning | spec-required | [docs.devin.ai/desktop/cascade/hooks](https://docs.devin.ai/desktop/cascade/hooks) | Windsurf hook registered under an event name outside the legacy Cascade event table |
|
|
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.agent.missing-field` | error | spec-required | [learn.chatgpt.com/docs/agent-configuration/subagents](https://learn.chatgpt.com/docs/agent-configuration/subagents) | Codex custom agent file does not define string name, description, and developer_instructions |
|
|
596
|
+
| `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 |
|
|
597
|
+
| `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 |
|
|
598
|
+
| `gemini.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Gemini hook points at a script that does not exist, so it never runs |
|
|
599
|
+
| `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 |
|
|
600
|
+
| `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 |
|
|
601
|
+
| `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 |
|
|
602
|
+
| `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 |
|
|
603
|
+
| `cursor.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Cursor hook points at a script that does not exist, so it never runs |
|
|
604
|
+
| `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 |
|
|
605
|
+
| `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 |
|
|
606
|
+
| `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 |
|
|
607
|
+
| `kiro.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Kiro command hook points at a script that does not exist, so it never runs |
|
|
608
|
+
| `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 |
|
|
609
|
+
| `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 |
|
|
610
|
+
| `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 |
|
|
611
|
+
| `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 |
|
|
612
|
+
| `zed.mcp.no-launch` | error | spec-required | [zed.dev/docs/ai/mcp](https://zed.dev/docs/ai/mcp) | Zed context server declares neither command nor url |
|
|
613
|
+
| `amp.mcp.no-launch` | warning | internal-consistency | _agentscan inference_ | Amp MCP server declares neither command nor url |
|
|
614
|
+
| `claude-desktop.mcp.no-launch` | warning | internal-consistency | _agentscan inference_ | Claude Desktop MCP server declares no command |
|
|
615
|
+
| `claude-desktop.extension.missing-field` | warning | spec-required | [raw.githubusercontent.com/anthropics/mcpb/main/MANIFEST.md](https://raw.githubusercontent.com/anthropics/mcpb/main/MANIFEST.md) | Claude Desktop extension manifest lacks a required field or names an unknown server type |
|
|
616
|
+
| `qwen.mcp.no-launch` | error | spec-required | [qwenlm.github.io/qwen-code-docs/en/users/features/mcp](https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/) | Qwen Code MCP server has neither command, url, nor httpUrl |
|
|
617
|
+
| `qwen.hook.unknown-event` | error | spec-required | [qwenlm.github.io/qwen-code-docs/en/users/features/hooks](https://qwenlm.github.io/qwen-code-docs/en/users/features/hooks/) | Qwen Code hook registered under an event name that is never dispatched |
|
|
618
|
+
| `qwen.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Qwen Code hook points at a script that does not exist, so it never runs |
|
|
619
|
+
| `qwen.hook.command-without-command` | error | spec-required | [qwenlm.github.io/qwen-code-docs/en/users/features/hooks](https://qwenlm.github.io/qwen-code-docs/en/users/features/hooks/) | Qwen Code command hook is missing the command field |
|
|
620
|
+
| `qwen.hook.http-without-url` | error | spec-required | [qwenlm.github.io/qwen-code-docs/en/users/features/hooks](https://qwenlm.github.io/qwen-code-docs/en/users/features/hooks/) | Qwen Code http hook is missing the url field |
|
|
621
|
+
| `qwen.hook.unknown-handler-type` | error | spec-required | [qwenlm.github.io/qwen-code-docs/en/users/features/hooks](https://qwenlm.github.io/qwen-code-docs/en/users/features/hooks/) | Qwen Code hook type is missing or is not command, http, or prompt |
|
|
622
|
+
| `factory.mcp.no-launch` | error | spec-required | [docs.factory.com/harness/mcp](https://docs.factory.com/harness/mcp) | Factory Droid MCP server has neither command nor url |
|
|
623
|
+
| `factory.hook.unknown-event` | error | spec-required | [docs.factory.com/harness/hooks](https://docs.factory.com/harness/hooks) | Factory Droid hook registered under an event name that is never dispatched |
|
|
624
|
+
| `factory.hook.missing-script` | error | internal-consistency | _agentscan inference_ | Factory Droid hook points at a script that does not exist, so it never runs |
|
|
625
|
+
| `factory.hook.invalid-group` | error | spec-required | [docs.factory.com/harness/hooks](https://docs.factory.com/harness/hooks) | Factory Droid hook group has no hooks array |
|
|
626
|
+
| `factory.hook.command-without-command` | error | spec-required | [docs.factory.com/harness/hooks](https://docs.factory.com/harness/hooks) | Factory Droid command hook is missing the command field |
|
|
627
|
+
| `factory.hook.unknown-handler-type` | error | spec-required | [docs.factory.com/harness/hooks](https://docs.factory.com/harness/hooks) | Factory Droid hook type is missing or is not command |
|
|
628
|
+
| `factory.droid.invalid-name` | error | spec-required | [docs.factory.com/harness/subagents](https://docs.factory.com/harness/subagents) | Factory custom droid name is missing or does not match ^[a-z0-9-_]+$ |
|
|
629
|
+
| `factory.droid.empty-body` | error | spec-required | [docs.factory.com/harness/subagents](https://docs.factory.com/harness/subagents) | Factory custom droid has an empty system prompt body |
|
|
630
|
+
| `goose.mcp.no-launch` | error | spec-required | [goose-docs.ai/docs/guides/config-files](https://goose-docs.ai/docs/guides/config-files/) | Goose extension declares type sse, which Goose does not support |
|
|
631
|
+
<!-- rules:end -->
|
|
632
|
+
|
|
633
|
+
### Coverage in 2.0.0
|
|
537
634
|
|
|
538
635
|
Each cell is one coverage dimension. There is no `full` / `partial` label: a
|
|
539
636
|
documented global or static location that is not opened stays **unread**.
|
|
@@ -548,29 +645,28 @@ Dimensions:
|
|
|
548
645
|
|
|
549
646
|
| Ecosystem | project discovery | global discovery | schema | precedence | conformance |
|
|
550
647
|
|-----------|-------------------|------------------|--------|------------|-------------|
|
|
551
|
-
| Agent Skills | `.agents/skills` (and Cursor / Codex / Command Code skill trees) | `--global` `~/.agents/skills
|
|
648
|
+
| Agent Skills | `.agents/skills` (and Cursor / Codex / Command Code skill trees). Default `skills/` uses the Claude schema, not this contract | `--global` `~/.agents/skills` (Codex reads its user skills here) | required `name` / `description` plus optional `compatibility` / `metadata` / `allowed-tools`; 500-line info | n/a | `agent-skills` fixture |
|
|
552
649
|
| AGENTS.md | nested walk-up | n/a | no required fields | nearest-wins | tests |
|
|
553
|
-
| Claude Code | project settings, skills, agents, `CLAUDE.md`, `.mcp.json` / `.claude/mcp.json` |
|
|
650
|
+
| 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 |
|
|
554
651
|
| 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 |
|
|
555
|
-
| Codex | `.codex/config.toml
|
|
556
|
-
| VS Code | `.github/hooks` without `version: 1
|
|
557
|
-
| Copilot CLI | `.github/hooks` with `version: 1` | `--global` `~/.copilot/hooks` with `version: 1
|
|
558
|
-
| Cursor | nested `.cursor/skills
|
|
652
|
+
| Codex | `.codex/hooks.json` and inline `[hooks]` in `.codex/config.toml` on the ancestor walk; skills from `.agents/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 |
|
|
653
|
+
| 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` |
|
|
654
|
+
| Copilot CLI | `.github/hooks` with `version: 1`; inline `hooks` in `.github/copilot/settings.json` and `settings.local.json`; `.github/mcp.json` (`mcpServers`) | `--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 |
|
|
655
|
+
| 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`; `~/.cursor/skills`; unread MDM/team hooks; 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 |
|
|
559
656
|
| 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 |
|
|
560
|
-
| Antigravity | `.agents/mcp_config.json` |
|
|
561
|
-
| Gemini | `.gemini/settings.json` |
|
|
562
|
-
| Windsurf | `.devin/rules/*.md` (preferred), `.windsurf/rules/*.md` (fallback), `.windsurfrules`; portable `AGENTS.md` / `agents.md` | `--global` `~/.codeium/windsurf/mcp_config.json`
|
|
563
|
-
|
|
|
564
|
-
|
|
|
565
|
-
|
|
|
566
|
-
|
|
|
657
|
+
| 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 |
|
|
658
|
+
| 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 |
|
|
659
|
+
| 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 |
|
|
660
|
+
| 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 |
|
|
661
|
+
| 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 |
|
|
662
|
+
| Cline | `.cline/skills`, `.clinerules/skills` | `--global` `~/.cline/skills`; unread hooks (no quoted event list), unread agents | Agent Skills | n/a | unit tests |
|
|
663
|
+
| Roo | `.roo/skills` | `--global` `~/.roo/skills` | Agent Skills | n/a | unit tests |
|
|
664
|
+
| Kilo | `.kilo/skills` | `--global` `~/.kilo/skills`; unread extra paths in `kilo.jsonc` | Agent Skills | n/a | unit tests |
|
|
567
665
|
| OpenCode | `opencode.json(c)` V1 and V2 | none | V2 local `command` must be an argv array | n/a | `opencode-json` fixture |
|
|
568
|
-
| Junie |
|
|
666
|
+
| 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 |
|
|
569
667
|
| Continue | `.continue/config.yaml`, `.continue/mcpServers/*` | none | launch `command` / `url` / `uses`; standalone YAML `name` / `version` / `schema` | n/a | `continue-yaml`, `continue-mcpservers` |
|
|
570
668
|
|
|
571
|
-
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. Portable `AGENTS.md` / `agents.md` stay `sourceProvider: unknown` — this scanner does not claim exclusive Windsurf ownership.
|
|
572
|
-
|
|
573
|
-
Old 0.7 rule ids still work in `ignoreRules` and `explain` (for example `hook.missing-script` aliases `claude.hook.missing-script`).
|
|
669
|
+
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.
|
|
574
670
|
|
|
575
671
|
Agent definitions are checked for structure only. A Claude agent's frontmatter
|
|
576
672
|
`name` is not compared to the filename; it must be lowercase letters and
|
|
@@ -591,21 +687,32 @@ illustration, not a pointer.
|
|
|
591
687
|
### Where it looks for hooks
|
|
592
688
|
|
|
593
689
|
The Claude hooks reference lists seven places a hook can be registered. agentscan
|
|
594
|
-
reads the
|
|
595
|
-
|
|
690
|
+
reads the project settings files, in-tree plugin and command-file hooks,
|
|
691
|
+
`~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`)
|
|
692
|
+
under `--global`, plus VS Code workspace hooks and Command Code settings hooks:
|
|
596
693
|
|
|
597
694
|
| Registered in | Script paths resolve against |
|
|
598
695
|
|---|---|
|
|
599
696
|
| `.claude/settings.json` · `.claude/settings.local.json` | project root, `${CLAUDE_PROJECT_DIR}` |
|
|
697
|
+
| `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`) under `--global` | scanned project root, `${CLAUDE_PROJECT_DIR}` |
|
|
600
698
|
| A plugin's `hooks/hooks.json` (a directory with `.claude-plugin/plugin.json`, or the scan root itself) | the plugin root, `${CLAUDE_PLUGIN_ROOT}` |
|
|
601
|
-
| `SKILL.md` frontmatter | the skill's own directory, then the project root |
|
|
699
|
+
| `SKILL.md` frontmatter (including in-tree plugin skills) | the skill's own directory, then the project root |
|
|
700
|
+
| `.claude/commands/*.md` frontmatter | the command file's directory, then the project root |
|
|
602
701
|
| `.claude/agents/*.md` frontmatter | the agent file's directory, then the project root |
|
|
603
702
|
| `.github/hooks/*.json` without `version: 1` (native VS Code) | project root and the hooks directory |
|
|
604
703
|
| `.github/hooks/*.json` with `version: 1` (Copilot CLI) | project root, `cwd`, host `bash` / `powershell` / `command` |
|
|
605
|
-
|
|
|
704
|
+
| `.github/copilot/settings.json` · `.github/copilot/settings.local.json` (Copilot CLI inline) | project root and the settings directory; always `copilot-cli` |
|
|
705
|
+
| `~/.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` |
|
|
606
706
|
| `.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` |
|
|
607
707
|
| `.grok/hooks/*.json` | project root and the hooks directory |
|
|
608
708
|
| `$GROK_HOME/hooks` or `~/.grok/hooks` under `--global` | same Grok 14-event / command-or-http split |
|
|
709
|
+
| `.github/agents/*.agent.md` frontmatter | vscode-native (command-only); project root and the agent file's directory |
|
|
710
|
+
| `.kiro/hooks/*.json` | project root; array-of-hooks, not Claude event-keyed objects |
|
|
711
|
+
| `~/.kiro/hooks/` under `--global` | scanned project root |
|
|
712
|
+
| `.cursor/hooks.json` | project root |
|
|
713
|
+
| `~/.cursor/hooks.json` under `--global` | scanned project root |
|
|
714
|
+
| `.gemini/settings.json` `hooks` | project root, `$GEMINI_PROJECT_DIR` |
|
|
715
|
+
| `~/.gemini/settings.json` `hooks` under `--global` | scanned project root, `$GEMINI_PROJECT_DIR` |
|
|
609
716
|
|
|
610
717
|
`${CLAUDE_PLUGIN_ROOT}` is expanded **only** for a hook that came from a plugin.
|
|
611
718
|
In a settings file it names nothing, so the path is skipped rather than guessed
|
|
@@ -641,8 +748,8 @@ first.
|
|
|
641
748
|
should add a `next-*` skill". That needed a hand-maintained registry, and the
|
|
642
749
|
version that existed fired on none of the 17 projects measured, so it was
|
|
643
750
|
removed. Skill provenance comes from `skills-lock.json` instead.
|
|
644
|
-
- **No
|
|
645
|
-
the `computedHash` in `skills-lock.json` is the obvious
|
|
751
|
+
- **No skill-content hash check.** Comparing a skill's content against
|
|
752
|
+
the `computedHash` in `skills-lock.json` is the obvious source of one; the
|
|
646
753
|
hash algorithm used by the installing tool is not documented here, and it is
|
|
647
754
|
not reproducible from `SKILL.md` bytes alone, so it is not implemented.
|
|
648
755
|
- **Runtime split.** Source development and the composite Action use Bun; the
|
|
@@ -661,39 +768,97 @@ first.
|
|
|
661
768
|
still inspect every readable MCP file.
|
|
662
769
|
- **Codex admin and profile layers are unread.** System `/etc/codex/config.toml`,
|
|
663
770
|
managed preferences, `requirements.toml`, `$CODEX_HOME/<name>.config.toml`
|
|
664
|
-
profiles, plugin MCP, and `trust_level` are not opened.
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
771
|
+
profiles, plugin hooks and MCP, and `trust_level` are not opened. Project
|
|
772
|
+
`.codex/hooks.json` and inline `[hooks]` in `.codex/config.toml` are
|
|
773
|
+
inventoried on the ancestor walk even when that layer is untrusted. The
|
|
774
|
+
same two files under `$CODEX_HOME` (or `~/.codex`) are opened only with
|
|
775
|
+
`--global`. `auth.json` is never opened. Same-name user and project MCP
|
|
776
|
+
servers are both inventoried — the published page does not quote a
|
|
668
777
|
replace-entirely rule. User-file `project_doc_*` knobs are not applied.
|
|
778
|
+
Relative hook commands resolve against the scanned directory, not the git
|
|
779
|
+
root. SessionEnd and Interrupt timeouts outside 1–3 seconds are findings;
|
|
780
|
+
other events have no quoted maximum.
|
|
669
781
|
- **Command Code local MCP is unread.** `~/.commandcode/projects/{project}/mcp.json`
|
|
670
782
|
is keyed by a working-directory slug that the sessions page does not publish.
|
|
671
783
|
Until it does, that path is skipped rather than guessed. Bundled Command Code
|
|
672
784
|
skills and `--skill` launch flags are not project files and are not scanned.
|
|
673
|
-
`mods
|
|
785
|
+
Command Code `mods` settings are not read, and mod TypeScript is never
|
|
786
|
+
executed or imported.
|
|
674
787
|
`auth.json` and `mcp-tokens.json` are never opened. Command Code project
|
|
675
788
|
root is the git root (or cwd outside a git repo); a child `.cursor` does
|
|
676
789
|
not hide `<git-root>/.commandcode/`. Spec/runtime Command Code checks skip
|
|
677
790
|
shadowed lower-precedence definitions; secrets still inspect every readable
|
|
678
791
|
MCP file.
|
|
679
|
-
- **
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
missing script, is not reported.
|
|
792
|
+
- **Two Claude hook locations stay unread.** Managed policy settings and
|
|
793
|
+
installed marketplace plugins under `~/.claude/plugins` sit outside the
|
|
794
|
+
scanned project, and the docs say a plugin's install directory changes
|
|
795
|
+
on every update. A hook registered in one of those, pointing at a
|
|
796
|
+
missing script, is not reported. User settings, skills, agents,
|
|
797
|
+
`CLAUDE.md`, rules, and commands under `$CLAUDE_CONFIG_DIR` or
|
|
798
|
+
`~/.claude` are scanned only under `--global`. User `~/.claude.json`
|
|
799
|
+
MCP is scanned only under `--global`: top-level `mcpServers` and
|
|
800
|
+
`projects.<scanned-path>.mcpServers`. Other projects' local servers
|
|
801
|
+
are skipped. A file without those keys is skipped, not
|
|
802
|
+
`config.unreadable`. In-tree plugin skills, agents, commands, and
|
|
803
|
+
`.mcp.json` are read; plugin agent frontmatter hooks are ignored
|
|
804
|
+
because Claude skips them.
|
|
805
|
+
- **Default `skills/` is Claude schema.** A repo-root `skills/` directory is
|
|
806
|
+
discovered, but `name` / directory-match from the Agent Skills spec are not
|
|
807
|
+
applied there. `.agents/skills`, `.cursor/skills`,
|
|
808
|
+
`.commandcode/skills`, `.kiro/skills`, `.cline/skills`, `.clinerules/skills`, `.roo/skills`,
|
|
809
|
+
`.kilo/skills`, and `.junie/skills` use the Agent Skills contract. Junie
|
|
810
|
+
does not require `description` and does not require `name` to match the
|
|
811
|
+
folder.
|
|
684
812
|
- **Copilot CLI policy hooks are unread.** `/etc/github-copilot/policy.d` and the
|
|
685
813
|
Windows policy directory are machine-wide admin files. User
|
|
686
|
-
`~/.copilot/hooks`
|
|
687
|
-
`.
|
|
688
|
-
|
|
689
|
-
|
|
814
|
+
`~/.copilot/hooks` and `~/.copilot/settings.json` (or `$COPILOT_HOME/…`)
|
|
815
|
+
are scanned only under `--global`. Plugin `hooks.json` stays unread.
|
|
816
|
+
Copilot also reads repository `.claude/settings.json`; this scanner still
|
|
817
|
+
lints that file as Claude (required nested groups and required `type`).
|
|
818
|
+
Copilot-only event names there stay `claude.hook.unknown-event`.
|
|
819
|
+
- **Gemini system settings are unread.** `~/.gemini/settings.json` (hooks and
|
|
820
|
+
MCP together) is scanned only under `--global`. `/etc/gemini-cli/settings.json`
|
|
821
|
+
and extension-provided hooks stay unread. `mcp-oauth-tokens.json` is never
|
|
822
|
+
opened.
|
|
823
|
+
- **Cursor managed hooks are unread.** `~/.cursor/hooks.json` is scanned only
|
|
824
|
+
under `--global`. MDM paths (`/etc/cursor/hooks.json` and the macOS /
|
|
825
|
+
Windows equivalents) and dashboard-synced team hooks stay unread. Cursor's
|
|
826
|
+
third-party import of Claude hook files stays on the Claude profile.
|
|
827
|
+
- **Antigravity global files.** `~/.gemini/config/mcp_config.json`,
|
|
828
|
+
`hooks.json`, `rules/*.md`, `skills/`, `AGENTS.md`, and `GEMINI.md`, plus
|
|
829
|
+
`~/.gemini/AGENTS.md` and `~/.gemini/GEMINI.md`, are scanned only under
|
|
830
|
+
`--global`. Project `.agents/mcp_config.json`, `.agents/hooks.json`,
|
|
831
|
+
`GEMINI.md`, `.agents/GEMINI.md`, `.agents/rules/*.md`, and legacy
|
|
832
|
+
`.agent/rules/*.md` are read on the ancestor walk. Workspace `AGENTS.md`
|
|
833
|
+
stays the portable policy file, so the 24,000-byte rule check does not
|
|
834
|
+
apply to it. That check counts raw bytes, before `@[label](path)`
|
|
835
|
+
expansion, and the 20,000-token aggregate is not checked. `rules.json`
|
|
836
|
+
paths are not followed. `url` is an alias of `serverUrl`. `httpUrl` alone
|
|
837
|
+
is still not a launch field. Plugin hooks, plugin rules, plugin skills,
|
|
838
|
+
`~/.gemini/antigravity-cli/settings.json`,
|
|
839
|
+
`~/.gemini/antigravity-cli/skills/`, `~/.gemini/skills/`, and
|
|
840
|
+
`~/.gemini/antigravity/skills/` stay unread. Hook `timeout` has no quoted
|
|
841
|
+
range, so it is not checked. Global skills use the Agent Skills contract,
|
|
842
|
+
which requires `name` even though the Antigravity skills page says `name`
|
|
843
|
+
defaults to the folder.
|
|
844
|
+
- **Cline hooks stay unread.** Cline's hooks page did not quote an event
|
|
845
|
+
identifier table. Cline agents are unread for the same reason. Kiro agents
|
|
846
|
+
are inventoried from `.kiro/agents/**/*.json` and `*.md` on the ancestor
|
|
847
|
+
walk, and from `~/.kiro/agents/` under `--global`. The name is the path
|
|
848
|
+
relative to the agents directory without the extension. A nearer workspace
|
|
849
|
+
agent shadows a farther one and a global one with the same name. No field
|
|
850
|
+
is required, so there is no `kiro.agent.*` check.
|
|
851
|
+
- **VS Code marketplace plugin hooks are unread.** In-tree Claude plugins
|
|
852
|
+
stay Claude. VS Code's `hooks.json` / `hooks/hooks.json` inside an
|
|
853
|
+
installed plugin package is not a project path.
|
|
690
854
|
- **Windsurf Devin CLI MCP and auto memories are unread.** Cascade MCP is
|
|
691
855
|
global-only (`~/.codeium/windsurf/mcp_config.json`, `--global`). The Devin
|
|
692
856
|
Local agent uses unpublished CLI config files — that path is not guessed.
|
|
693
857
|
Auto-generated files under `~/.codeium/windsurf/memories/` other than
|
|
694
|
-
`global_rules.md` are not opened. System / enterprise rule
|
|
695
|
-
(`/etc/devin/rules`, `/etc/windsurf/rules
|
|
696
|
-
|
|
858
|
+
`global_rules.md` are not opened. System / enterprise rule, hook, and
|
|
859
|
+
skill directories (`/etc/devin/rules`, `/etc/windsurf/rules`,
|
|
860
|
+
`/etc/windsurf/hooks.json`, `/etc/windsurf/skills/`) stay unread. There
|
|
861
|
+
is no quoted project MCP path. JetBrains `~/.codeium/hooks.json` is unread.
|
|
697
862
|
- **Bounded reads.** A `SKILL.md` is read to 64 KB and a policy file to 100 KB,
|
|
698
863
|
so `skill.broken-reference` and `policyLines` see only that much of a larger
|
|
699
864
|
file. This is reported — `scan.truncated`, at `info` — rather than left
|
|
@@ -707,6 +872,37 @@ Notes for every published version are in [CHANGELOG.md](CHANGELOG.md).
|
|
|
707
872
|
GitHub already has pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
|
|
708
873
|
and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
|
|
709
874
|
|
|
875
|
+
**2.3.1** (14 November 2026) stops reading Codex's `[hooks.state]` trust
|
|
876
|
+
record as a hook group.
|
|
877
|
+
|
|
878
|
+
**2.3.0** (14 November 2026) adds Claude Desktop, Amp, Zed, Qwen Code,
|
|
879
|
+
Goose and Factory Droid; see the changelog.
|
|
880
|
+
|
|
881
|
+
**2.2.0** (14 November 2026) aligns every surface with the vendor docs
|
|
882
|
+
re-read that day and adds `codex.agent.missing-field`; see the changelog.
|
|
883
|
+
|
|
884
|
+
**2.1.0** (14 November 2026) cuts false findings on home-directory and
|
|
885
|
+
`--global` scans; see the changelog. No rule id changed.
|
|
886
|
+
|
|
887
|
+
**2.0.0** (12 November 2026) is breaking for configs that still name the 0.7
|
|
888
|
+
rule ids or the old threshold keys. `ignoreRules`, `ignoreFindings`, and
|
|
889
|
+
`explain` accept only the printed ids. `thresholds.skillDescriptionBytes`
|
|
890
|
+
and `thresholds.skills` are rejected; the listing budget is
|
|
891
|
+
`thresholds.skillListingChars`. It also ships the November 2026 vendor
|
|
892
|
+
contracts: Kiro agents, Antigravity rules, skills, and hooks, Codex hooks,
|
|
893
|
+
Cursor user MCP, Devin CLI hooks / MCP / skills, and the Copilot user MCP
|
|
894
|
+
file. **143 checks**, **647 tests**.
|
|
895
|
+
|
|
896
|
+
**1.4.0** (2 September 2026) closes two coverage gaps and ships the
|
|
897
|
+
2026-09-02 re-verification: Copilot CLI inline `hooks` in
|
|
898
|
+
`.github/copilot/settings.json` / `settings.local.json` and `--global`
|
|
899
|
+
`$COPILOT_HOME` / `~/.copilot/settings.json`; `mcp.literal-credential` on
|
|
900
|
+
MCP `headers` / `http_headers` / `auth` (field paths only). Also the
|
|
901
|
+
seven false-positive corrections, spec content hashes, verbatim
|
|
902
|
+
conformance fixtures, and Windsurf Cascade hooks / skills from #13.
|
|
903
|
+
**103 checks**, **532 tests**. Policy hooks, plugin `hooks.json`, and
|
|
904
|
+
`.claude/settings.json` as Copilot stay unread.
|
|
905
|
+
|
|
710
906
|
**1.3.0** (31 August 2026) adds native Windsurf / Cascade coverage: workspace
|
|
711
907
|
`.devin/rules` and `.windsurf/rules`, portable `agents.md`, and `--global`
|
|
712
908
|
`mcp_config.json` plus `global_rules.md`. Devin CLI MCP, auto memories,
|
|
@@ -760,7 +956,9 @@ Before publishing a release:
|
|
|
760
956
|
|
|
761
957
|
1. Confirm `package.json` and `src/version.ts` contain the same version.
|
|
762
958
|
2. Run `bun run build`, then verify `node dist/cli.js --version` prints it.
|
|
763
|
-
3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`.
|
|
959
|
+
3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`. If a page
|
|
960
|
+
hash changed, re-read that page, update its `docs/spec/` capture and the
|
|
961
|
+
conformance fixture, then `bun run spec:record`.
|
|
764
962
|
4. Refresh the test-count badge and version examples above from that release run.
|
|
765
963
|
5. Add a section to [CHANGELOG.md](CHANGELOG.md) for the version, then publish
|
|
766
964
|
the GitHub release from that text.
|