@chimix/agentscan 1.0.0 → 1.3.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 +234 -0
  2. package/README.md +207 -68
  3. package/dist/cli.js +9102 -6630
  4. package/package.json +4 -2
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-59-111111?style=flat-square" alt="59 checks">
9
- <img src="https://img.shields.io/badge/tests-345%20passing-111111?style=flat-square" alt="345 tests">
8
+ <img src="https://img.shields.io/badge/checks-99-111111?style=flat-square" alt="99 checks">
9
+ <img src="https://img.shields.io/badge/tests-457%20passing-111111?style=flat-square" alt="457 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.0.0-111111?style=flat-square" alt="Changelog"></a>
15
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-1.3.0-111111?style=flat-square" alt="Changelog"></a>
16
16
  </p>
17
17
 
18
18
  <p align="center">
19
- <strong>59 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, portable Agent Skills, nested AGENTS.md, and the MCP / hooks / rules surfaces that 1.0.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 — <code>full</code> only where discovery, spec-required fields, and a conformance fixture (or equivalent tests) all exist.</sub>
19
+ <strong>99 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, portable Agent Skills, nested AGENTS.md, Copilot CLI hooks, and the MCP / hooks / rules surfaces that 1.3.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>
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/ .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 59 checks, each labeled spec-required, vendor-recommendation,
68
+ 3. Check 99 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,10 +116,10 @@ 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/` directory,
120
- an `.mcp.json`, an `AGENTS.md`. A directory with agent config and no
121
- `package.json` is still scannable. On a directory with neither it exits with a
122
- clear error.
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
+ agent config and no `package.json` is still scannable. On a directory with
122
+ neither it exits with a clear error.
123
123
 
124
124
  ```bash
125
125
  # what it could possibly report, before you run it
@@ -186,7 +186,7 @@ Flags for `check`:
186
186
  | `--verbose` | Show KEEP + 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` and `~/.codex/skills` (see below) |
189
+ | `--global` | Also scan `~/.claude/skills`, `~/.codex/skills`, `$CODEX_HOME` / `~/.codex/config.toml`, `~/.commandcode/skills`, `~/.agents/skills`, and `$GROK_HOME` / `~/.grok` (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.0.0 — touchagency
202
+ agentscan v1.3.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.0.0",
234
+ "version": "1.3.0",
235
235
  "root": "/path/to/project",
236
236
  "factsSummary": {
237
237
  "packageManager": "bun",
@@ -259,10 +259,18 @@ Same tree → same sorted findings, with stable unique `id`s.
259
259
 
260
260
  ### `--global`
261
261
 
262
- Adds `~/.claude/skills` and `~/.codex/skills` to the structural checks: a
263
- `SKILL.md` that is malformed is malformed wherever it lives, and those findings
264
- carry a `source: global` evidence entry so you can tell them apart. Codex skills
265
- use the Agent Skills contract (`name` and `description` required).
262
+ Adds `~/.claude/skills`, `~/.codex/skills`, `~/.commandcode/skills`,
263
+ `~/.agents/skills`, `$CODEX_HOME` / `~/.codex` (`config.toml` MCP plus
264
+ `AGENTS.override.md` / `AGENTS.md`), and `$GROK_HOME` / `~/.grok` (skills,
265
+ hooks, `config.toml`) to the structural checks: a `SKILL.md` that is malformed
266
+ is malformed wherever it lives, and those findings carry a `source: global`
267
+ evidence entry so you can tell them apart. Codex and Command Code skills use
268
+ the Agent Skills contract (`name` and `description` required). Grok skills do
269
+ not — `name` and `description` are optional. `--global` also inventories user
270
+ Command Code agents (`~/.commandcode/agents`), MCP (`~/.commandcode/mcp.json`
271
+ and user settings `mcp.servers`), and memory (`~/.commandcode/AGENTS.md`). It
272
+ never opens `auth.json`, `mcp-tokens.json`, Codex `auth.json`, or Grok
273
+ `auth.json` / `mcp_credentials.json`.
266
274
 
267
275
  Lockfile checks stay project-scoped — a project lockfile cannot pin a skill that
268
276
  lives in your home directory, so reporting one as "not in the lockfile" could
@@ -342,8 +350,8 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
342
350
 
343
351
  ```json
344
352
  {
345
- "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills"],
346
- "mcpPaths": [".mcp.json", ".claude/mcp.json", "mcp.json", ".vscode/mcp.json", ".cursor/mcp.json", ".agents/mcp_config.json", ".codex/config.toml"],
353
+ "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills"],
354
+ "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"],
347
355
  "policyFiles": ["AGENTS.md", "CLAUDE.md"],
348
356
  "ignoreSkills": [],
349
357
  "ignoreRules": [],
@@ -352,8 +360,8 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
352
360
  "includeGlobal": false,
353
361
  "requireLock": false,
354
362
  "thresholds": {
355
- "skillDescriptionBytes": 16000,
356
- "skills": 30,
363
+ "skillListingChars": 8000,
364
+ "skillListingMaxDescChars": 1536,
357
365
  "mcp": 5,
358
366
  "agentsMdLines": 150,
359
367
  "claudeMdLines": 200,
@@ -434,29 +442,69 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
434
442
  | `claude.agent.missing-frontmatter` | error | A Claude agent definition with no `---` block |
435
443
  | `claude.agent.missing-description` | error | Claude agent frontmatter has no `description` |
436
444
  | `claude.agent.missing-name` | error | Claude agent frontmatter has no `name` |
437
- | `claude.agent.invalid-name` | warning | Claude agent name is not lowercase letters, numbers, and hyphens |
445
+ | `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) |
438
446
  | `claude.agent.duplicate-name` | error | Multiple Claude agent files declare the same name |
439
447
  | `claude.mcp.no-launch` | error | A Claude MCP server with neither `command` nor `url` |
440
- | `claude.mcp.url-without-type` | error | A Claude remote MCP server with a `url` but no `type` — read as stdio and skipped |
448
+ | `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 |
449
+ | `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 |
441
450
  | `vscode.mcp.no-launch` | error | A VS Code MCP server (`servers`) with neither `command` nor `url` |
442
451
  | `cursor.mcp.no-launch` | error | A Cursor MCP server with neither `command` nor `url` |
443
452
  | `antigravity.mcp.no-launch` | error | An Antigravity MCP server with neither `command` nor `serverUrl` |
444
453
  | `codex.mcp.no-launch` | error | A Codex `[mcp_servers.*]` entry with neither `command` nor `url` |
445
454
  | `gemini.mcp.no-launch` | error | A Gemini MCP server with neither `command`, `url`, nor `httpUrl` |
455
+ | `gemini.mcp.underscore-alias` | warning | A Gemini MCP server alias contains `_`, which can make policies fail silently |
446
456
  | `opencode.mcp.no-launch` | error | An OpenCode MCP server with neither `command` nor `url` |
447
457
  | `opencode.mcp.missing-type` | error | An OpenCode MCP server missing `type: local` or `type: remote` |
448
458
  | `opencode.mcp.local-without-command` | error | An OpenCode `type: local` server with no `command` |
449
459
  | `opencode.mcp.remote-without-url` | error | An OpenCode `type: remote` server with no `url` |
450
460
  | `opencode.mcp.invalid-launch-for-type` | error | An OpenCode server whose launch field does not match its `type` |
461
+ | `opencode.mcp.command-not-array` | error | An OpenCode V2 local `command` that is not an argv array |
451
462
  | `continue.mcp.no-launch` | error | A Continue MCP server with neither `command`, `url`, nor `uses` |
463
+ | `continue.mcp.missing-block-metadata` | error | A standalone `.continue/mcpServers/*.yaml` block missing `name`, `version`, or `schema` (not applied to copied JSON) |
464
+ | `commandcode.mcp.no-launch` | error | A Command Code settings / user MCP server with neither `command` nor `url` |
465
+ | `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) |
466
+ | `commandcode.mcp.http-without-url` | error | A Command Code HTTP MCP server with no `url` |
467
+ | `commandcode.mcp.stdio-without-command` | error | A Command Code stdio MCP server with no `command` |
452
468
  | `claude.hook.command-without-command` | error | A Claude `type: command` hook with no `command` |
453
- | `claude.hook.invalid-group` | error | A Claude hook matcher group with no `hooks` array |
469
+ | `claude.hook.http-without-url` | error | A Claude `type: http` hook with no `url` |
470
+ | `claude.hook.mcp-tool-without-server-or-tool` | error | A Claude `type: mcp_tool` hook missing `server` or `tool` |
471
+ | `claude.hook.unknown-handler-type` | error | A Claude hook whose required `type` is missing or not one of command, http, mcp_tool, prompt, agent |
472
+ | `claude.hook.prompt-without-prompt` | error | A Claude `type: prompt` or `type: agent` hook with no `prompt` |
473
+ | `claude.hook.incompatible-handler` | error | A Claude handler type that the event does not support |
474
+ | `claude.hook.invalid-group` | error | A Claude hook matcher group with no nested `hooks` array (flat handler arrays are invalid) |
475
+ | `vscode.hook.invalid-group` | error | A VS Code hook group that is not a command handler array |
476
+ | `vscode.hook.command-without-command` | error | A native VS Code `type: command` hook with no `command` |
477
+ | `vscode.hook.unknown-handler-type` | error | A native VS Code hook whose `type` is not `command` |
478
+ | `copilot.hook.unknown-event` | error | A Copilot CLI hook registered under an event that is never dispatched |
479
+ | `copilot.hook.missing-script` | error | A Copilot CLI command hook whose script does not exist |
480
+ | `copilot.hook.command-without-command` | error | A Copilot CLI command hook with none of `bash`, `powershell`, or `command` |
481
+ | `copilot.hook.http-without-url` | error | A Copilot CLI `type: http` hook with no `url` |
482
+ | `copilot.hook.prompt-without-prompt` | error | A Copilot CLI `type: prompt` hook with no `prompt` |
483
+ | `copilot.hook.unknown-handler-type` | error | A Copilot CLI hook whose `type` is not command, http, or prompt |
484
+ | `copilot.hook.incompatible-handler` | error | A Copilot CLI prompt hook registered on an event other than `sessionStart` |
485
+ | `commandcode.hook.unknown-event` | error | A Command Code hook registered under an event that is never dispatched (four events only) |
486
+ | `commandcode.hook.missing-script` | error | A Command Code command hook whose script does not exist |
487
+ | `commandcode.hook.invalid-group` | error | A Command Code hook group missing the required nested `hooks` array, or a non-string `matcher` |
488
+ | `commandcode.hook.command-without-command` | error | A Command Code `type: command` hook whose `command` is missing, empty, or not a string |
489
+ | `commandcode.hook.unknown-handler-type` | error | A Command Code hook whose `type` is missing, not a string, or not `"command"` |
490
+ | `commandcode.hook.timeout-out-of-bounds` | error | A Command Code hook `timeout` outside 0–600 seconds |
491
+ | `commandcode.agent.reserved-name` | error | A Command Code custom agent named `explore`, `plan`, `review`, or `general` — ignored at load |
492
+ | `commandcode.agent.invalid-permission-mode` | error | A Command Code agent `permissionMode` that is not a documented value |
493
+ | `commandcode.agent.invalid-field-type` | error | A Command Code agent field whose type the spec does not accept |
494
+ | `grok.mcp.no-launch` | error | A Grok `[mcp_servers.*]` entry with neither `command` nor `url` |
495
+ | `grok.hook.unknown-event` | error | A Grok hook registered under an event that is never dispatched (14 events) |
496
+ | `grok.hook.missing-script` | error | A Grok command hook whose script does not exist |
497
+ | `grok.hook.invalid-group` | error | A Grok hook group missing the required nested `hooks` array, or a non-string `matcher` |
498
+ | `grok.hook.command-without-command` | error | A Grok `type: command` hook with no `command` |
499
+ | `grok.hook.http-without-url` | error | A Grok `type: http` hook with no `url` |
500
+ | `grok.hook.unknown-handler-type` | error | A Grok hook whose `type` is missing or not `command` / `http` |
501
+ | `grok.skill.missing-frontmatter` | warning | Grok `SKILL.md` with no `---` block |
454
502
  | `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
455
503
  | `security.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
456
504
  | `mcp.literal-env` | warning | Secret-named `env` values that are literals instead of interpolation |
457
505
  | `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
458
506
  | `claude.skill.missing-frontmatter` | warning | Claude `SKILL.md` with no `---` block |
459
- | `claude.skill.missing-description` | info | Claude frontmatter has no `description` (Recommended) |
507
+ | `claude.skill.missing-description` | info | Claude frontmatter has no `description` and no first markdown paragraph |
460
508
  | `agent-skills.skill.missing-frontmatter` | error | Portable skill `SKILL.md` has no YAML frontmatter |
461
509
  | `agent-skills.skill.missing-name` | error | Portable skill frontmatter has no required `name` |
462
510
  | `agent-skills.skill.missing-description` | error | Portable skill frontmatter has no required `description` |
@@ -464,11 +512,15 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
464
512
  | `agent-skills.skill.name-does-not-match-directory` | error | Portable skill `name` does not match its directory |
465
513
  | `agent-skills.skill.name-too-long` | error | Portable skill name exceeds 64 characters |
466
514
  | `agent-skills.skill.description-too-long` | error | Portable skill description exceeds 1024 characters |
515
+ | `agent-skills.skill.invalid-compatibility` | error | Portable skill `compatibility` is present but is not a 1–500 character string |
516
+ | `agent-skills.skill.invalid-metadata` | error | Portable skill `metadata` is present but is not `map<string, string>` |
517
+ | `agent-skills.skill.invalid-allowed-tools` | error | Portable skill `allowed-tools` is present but is not a string |
518
+ | `agent-skills.skill.body-too-large` | info | Portable skill `SKILL.md` exceeds the 500-line recommendation |
467
519
  | `skill.broken-reference` | warning | The body points at a bundled file that does not exist |
468
520
  | `skill.duplicate-description` | info | Two or more skills carry an identical description (heuristic) |
469
521
  | `skill.locked-not-installed` | warning | `skills-lock.json` pins a skill that is not on disk |
470
522
  | `skill.not-in-lock` | info | A skill on disk that the lockfile does not track (installer policy) |
471
- | `skill.description-budget` | info | Claude skill names + descriptions exceed the startup character budget (heuristic) |
523
+ | `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 |
472
524
  | `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
473
525
  | `budget.agents-md` | info | `AGENTS.md` past a secondary 150-line hint (heuristic; not an AGENTS.md requirement) |
474
526
  | `budget.claude-md` | info | `CLAUDE.md` past the official “target under 200 lines” recommendation |
@@ -476,45 +528,62 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
476
528
  | `budget.mcp` | info | More MCP servers than a tool-selection hint (>5) — heuristic proxy |
477
529
  | `codex.budget.instructions` | info | Codex root→cwd `AGENTS.md` chain exceeds the default 32 KiB |
478
530
  | `cursor.rule.too-large` | info | A `.cursor/rules/*.mdc` file exceeds the 500-line recommendation |
479
-
480
- ### Coverage in 1.0.0
481
-
482
- `full` means documented locations are discovered, spec-required fields are checked, and a conformance fixture (or equivalent tests) is green. `partial` means some locations or some checks. `none` means not implemented. Official pages that could not be captured are `none`, not guessed.
483
-
484
- | Ecosystem | instructions | skills | agents | hooks | MCP |
485
- |-----------|--------------|--------|--------|-------|-----|
486
- | Agent Skills | none | full | none | none | none |
487
- | AGENTS.md | partial (nested + nearest-wins; no required fields) | none | none | none | none |
488
- | Claude Code | partial (walk-up `CLAUDE.md`; 200-line target) | partial (native; `name` optional) | partial (walk-up `.claude/agents`) | partial (33 events; command handlers only) | full (`claude-json`) |
489
- | Codex | partial (32 KiB chain; no invented agents TOML) | full (Agent Skills; `.codex/skills`) | none | none | full (`codex-toml`) |
490
- | VS Code | partial (`.github` instruction files; no required fields) | none | partial (filename may be the name) | full (8 events) | full (`servers`) |
491
- | Cursor | none | full (Agent Skills; nested `SKILL.md`) | none | none | full |
492
- | Grok | none | none | none | none | none |
493
- | Antigravity | none | via Agent Skills | none | none | full (`serverUrl`) |
494
- | Gemini | none | none | none | none | full (`command` / `url` / `httpUrl`) |
495
- | Windsurf | none | none | none | none | none (official page is global-only) |
496
- | Kiro | none | none | none | none | none |
497
- | Cline | none | none | none | none | none |
498
- | Roo | none | none | none | none | none |
499
- | Kilo | none | none | none | none | none |
500
- | OpenCode | none | none | none | none | full (V1 and V2) |
501
- | Junie | none | none | none | none | none |
502
- | Continue | none | none | none | none | full (`config.yaml` + `.continue/mcpServers/*` + `uses`) |
503
-
504
- 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.
531
+ | `windsurf.rule.too-large` | info | A Windsurf workspace rule exceeds 12,000 characters |
532
+ | `windsurf.rule.global-too-large` | info | `~/.codeium/windsurf/memories/global_rules.md` exceeds 6,000 characters |
533
+ | `windsurf.rule.missing-trigger` | warning | A `.devin/rules` / `.windsurf/rules` file omits frontmatter `trigger` |
534
+ | `windsurf.mcp.no-launch` | error | Windsurf MCP server declares neither `command`, `serverUrl`, nor `url` |
535
+
536
+ ### Coverage in 1.3.0
537
+
538
+ Each cell is one coverage dimension. There is no `full` / `partial` label: a
539
+ documented global or static location that is not opened stays **unread**.
540
+
541
+ Dimensions:
542
+
543
+ - **project discovery** — documented in-repo files the scanner opens
544
+ - **global discovery** — documented user / machine locations; `--global` is named when that is the only path that opens them
545
+ - **schema** — spec-required (and labeled vendor-recommendation) field checks
546
+ - **precedence** — documented merge / override / shadowing
547
+ - **conformance** — official-shaped fixture or equivalent negative tests
548
+
549
+ | Ecosystem | project discovery | global discovery | schema | precedence | conformance |
550
+ |-----------|-------------------|------------------|--------|------------|-------------|
551
+ | 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 |
552
+ | 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` | 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 |
554
+ | 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`, `.codex/skills`, AGENTS chain | `--global` `$CODEX_HOME` / `~/.codex/config.toml` MCP; `AGENTS.override.md` then `AGENTS.md`; unread system, managed, requirements, profiles, plugins, trust | TOML MCP; `project_doc_max_bytes`; Agent Skills | override > `AGENTS.md` > fallbacks; one file per dir; root→cwd; `project_root_markers`; MCP user+project both inventoried | `codex-toml` fixture |
556
+ | 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` |
557
+ | 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 |
558
+ | 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 |
559
+ | 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` | none | `serverUrl` launch | n/a | `antigravity-json` fixture |
561
+ | 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 |
562
+ | Windsurf | `.devin/rules/*.md` (preferred), `.windsurf/rules/*.md` (fallback), `.windsurfrules`; portable `AGENTS.md` / `agents.md` | `--global` `~/.codeium/windsurf/mcp_config.json` and `~/.codeium/windsurf/memories/global_rules.md`; unread auto memories, Devin CLI MCP, system `/etc/devin/rules` | 12k / 6k character rules; workspace `trigger`; MCP `command` / `serverUrl` / `url` (no `type`) | n/a (no project MCP; both rule trees inventoried) | `windsurf-rules` fixture |
563
+ | Kiro | none | none | none | n/a | none |
564
+ | Cline | none | none | none | n/a | none |
565
+ | Roo | none | none | none | n/a | none |
566
+ | Kilo | none | none | none | n/a | none |
567
+ | OpenCode | `opencode.json(c)` V1 and V2 | none | V2 local `command` must be an argv array | n/a | `opencode-json` fixture |
568
+ | Junie | none | none | none | n/a | none |
569
+ | Continue | `.continue/config.yaml`, `.continue/mcpServers/*` | none | launch `command` / `url` / `uses`; standalone YAML `name` / `version` / `schema` | n/a | `continue-yaml`, `continue-mcpservers` |
570
+
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.
505
572
 
506
573
  Old 0.7 rule ids still work in `ignoreRules` and `explain` (for example `hook.missing-script` aliases `claude.hook.missing-script`).
507
574
 
508
- Agent definitions are checked for structure only. An agent's frontmatter `name`
509
- is not compared to the filename; it must simply be a valid lowercase identifier
510
- and unique. Model identifiers and tool names
511
- are likewise not validated: both would need a hardcoded list, which is the shape
512
- that already shipped a false error. See [plans/003](plans/003-validate-agent-definitions.md).
575
+ Agent definitions are checked for structure only. A Claude agent's frontmatter
576
+ `name` is not compared to the filename; it must be lowercase letters and
577
+ hyphens and unique. Command Code supplies `name` from the filename when
578
+ frontmatter omits it, and does not emit missing-name or missing-description.
579
+ Model identifiers and tool names are likewise not validated: both would need a
580
+ hardcoded list, which is the shape that already shipped a false error. See
581
+ [plans/003](plans/003-validate-agent-definitions.md).
513
582
 
514
583
  `skill.broken-reference` reads the body, not just the frontmatter. It looks for
515
584
  paths under the conventional bundled directories (`scripts/`, `references/`,
516
- `assets/`, `templates/`, `examples/`) and resolves each against the skill's own
517
- directory first, then the repo root — both bases are needed, because of 1674
585
+ `assets/`, `templates/`, `examples/`). Agent Skills references resolve from the
586
+ skill root only. Claude native skills also try the repo root — of 1674
518
587
  references measured across 17 projects, 1645 resolved skill-relative and 12 only
519
588
  at the root. Fenced code blocks are stripped first: a path in an example is
520
589
  illustration, not a pointer.
@@ -522,7 +591,8 @@ illustration, not a pointer.
522
591
  ### Where it looks for hooks
523
592
 
524
593
  The Claude hooks reference lists seven places a hook can be registered. agentscan
525
- reads the four that live inside the scanned project, plus VS Code workspace hooks:
594
+ reads the four that live inside the scanned project, plus VS Code workspace hooks
595
+ and Command Code settings hooks:
526
596
 
527
597
  | Registered in | Script paths resolve against |
528
598
  |---|---|
@@ -530,7 +600,12 @@ reads the four that live inside the scanned project, plus VS Code workspace hook
530
600
  | A plugin's `hooks/hooks.json` (a directory with `.claude-plugin/plugin.json`, or the scan root itself) | the plugin root, `${CLAUDE_PLUGIN_ROOT}` |
531
601
  | `SKILL.md` frontmatter | the skill's own directory, then the project root |
532
602
  | `.claude/agents/*.md` frontmatter | the agent file's directory, then the project root |
533
- | `.github/hooks/*.json` (VS Code) | project root and the hooks directory |
603
+ | `.github/hooks/*.json` without `version: 1` (native VS Code) | project root and the hooks directory |
604
+ | `.github/hooks/*.json` with `version: 1` (Copilot CLI) | project root, `cwd`, host `bash` / `powershell` / `command` |
605
+ | `~/.copilot/hooks` under `--global` | same split on `version: 1` |
606
+ | `.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
+ | `.grok/hooks/*.json` | project root and the hooks directory |
608
+ | `$GROK_HOME/hooks` or `~/.grok/hooks` under `--global` | same Grok 14-event / command-or-http split |
534
609
 
535
610
  `${CLAUDE_PLUGIN_ROOT}` is expanded **only** for a hook that came from a plugin.
536
611
  In a settings file it names nothing, so the path is skipped rather than guessed
@@ -543,17 +618,18 @@ A value is only resolved when it is path-like and the answer is certain; shell
543
618
  programs (`a && b`, `$(...)`, pipes), bare PATH binaries (`npx`, `uvx`), and
544
619
  unresolved env vars are skipped. A false "broken" on a working `npx` server
545
620
  would be worse than silence. `node -e "<code>"` is never treated as a path.
546
- Only `$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather
547
- than guessed at. Schema checks judge misconfiguration, not whether a correctly
548
- shaped entry will start at runtime.
621
+ Only `$CLAUDE_PROJECT_DIR`, `$COMMANDCODE_PROJECT_DIR`, and `$COMMANDCODE_CWD`
622
+ are expanded — other variables are left alone rather than guessed at. Schema
623
+ checks judge misconfiguration, not whether a correctly shaped entry will start
624
+ at runtime.
549
625
 
550
626
  Spec-required checks are written against published pages, not against what
551
627
  happens to appear in real projects. Two that were written the other way round
552
628
  shipped as false positives — a nine-name hook-event list where the spec has 31,
553
629
  and a `name` must equal the directory rule that Claude's spec explicitly
554
630
  contradicts. Both are gone. Heuristics that remain (`skill.duplicate-description`,
555
- the installer lock checks, and the size budgets other than `budget.claude-md`)
556
- are labeled in the registry and stay at `info`.
631
+ the installer lock checks, and the size budgets other than `budget.claude-md`
632
+ and `skill.description-budget`) are labeled in the registry and stay at `info`.
557
633
 
558
634
  Assumptions are recorded in **[docs/spec/](docs/spec/)** with source URL, read
559
635
  date, and provenance. When adding a spec-required check, add its spec line there
@@ -572,11 +648,52 @@ first.
572
648
  - **Runtime split.** Source development and the composite Action use Bun; the
573
649
  published `dist/cli.js` runs on Node 20.11+ or Bun. The source entrypoint uses
574
650
  Bun's `import.meta.dir` / `import.meta.main` and is not the Node entrypoint.
575
- - **Three hook locations are unread.** `~/.claude/settings.json`, managed policy
651
+ - **Grok compatibility and unpublished surfaces are unread.** Managed
652
+ (`~/.grok/managed_config.toml`, `/etc/grok/managed_config.toml`) and
653
+ requirements TOML are not opened. Plugins, marketplaces, personas, and
654
+ `[skills] paths` (no quoted resolution base) stay unread. `.grok/agents/`
655
+ is unread because the page names the directory but not a filename pattern
656
+ or required fields. Claude / Cursor / `.mcp.json` files keep their own
657
+ schemas — this scanner does not add `consumedBy: grok` to them. Grok’s
658
+ “skip gitignored instruction files” is unread: agentscan does not honor
659
+ `.gitignore`. `auth.json` and `mcp_credentials.json` are never opened.
660
+ Spec/runtime Grok MCP checks skip shadowed same-name servers; secrets
661
+ still inspect every readable MCP file.
662
+ - **Codex admin and profile layers are unread.** System `/etc/codex/config.toml`,
663
+ managed preferences, `requirements.toml`, `$CODEX_HOME/<name>.config.toml`
664
+ profiles, plugin MCP, and `trust_level` are not opened. User
665
+ `$CODEX_HOME/config.toml` (or `~/.codex/config.toml`) MCP is scanned only
666
+ under `--global`. `auth.json` is never opened. Same-name user and project
667
+ MCP servers are both inventoried — the published page does not quote a
668
+ replace-entirely rule. User-file `project_doc_*` knobs are not applied.
669
+ - **Command Code local MCP is unread.** `~/.commandcode/projects/{project}/mcp.json`
670
+ is keyed by a working-directory slug that the sessions page does not publish.
671
+ Until it does, that path is skipped rather than guessed. Bundled Command Code
672
+ skills and `--skill` launch flags are not project files and are not scanned.
673
+ `mods.paths` are inventoried; the TypeScript is never executed or imported.
674
+ `auth.json` and `mcp-tokens.json` are never opened. Command Code project
675
+ root is the git root (or cwd outside a git repo); a child `.cursor` does
676
+ not hide `<git-root>/.commandcode/`. Spec/runtime Command Code checks skip
677
+ shadowed lower-precedence definitions; secrets still inspect every readable
678
+ MCP file.
679
+ - **Three Claude hook locations are unread.** `~/.claude/settings.json`, managed policy
576
680
  settings, and installed marketplace plugins under `~/.claude/plugins` all sit
577
681
  outside the scanned project, and the docs say a plugin's install directory
578
682
  changes on every update. A hook registered in one of those, pointing at a
579
683
  missing script, is not reported.
684
+ - **Copilot CLI policy hooks are unread.** `/etc/github-copilot/policy.d` and the
685
+ Windows policy directory are machine-wide admin files. User
686
+ `~/.copilot/hooks` is scanned only under `--global`. Inline Copilot
687
+ `.github/copilot/settings.json` hooks are unread.
688
+ - **Gemini user settings are unread.** `~/.gemini/settings.json` is outside a
689
+ normal project scan.
690
+ - **Windsurf Devin CLI MCP and auto memories are unread.** Cascade MCP is
691
+ global-only (`~/.codeium/windsurf/mcp_config.json`, `--global`). The Devin
692
+ Local agent uses unpublished CLI config files — that path is not guessed.
693
+ Auto-generated files under `~/.codeium/windsurf/memories/` other than
694
+ `global_rules.md` are not opened. System / enterprise rule directories
695
+ (`/etc/devin/rules`, `/etc/windsurf/rules`) stay unread. There is no
696
+ quoted project MCP path.
580
697
  - **Bounded reads.** A `SKILL.md` is read to 64 KB and a policy file to 100 KB,
581
698
  so `skill.broken-reference` and `policyLines` see only that much of a larger
582
699
  file. This is reported — `scan.truncated`, at `info` — rather than left
@@ -590,6 +707,28 @@ Notes for every published version are in [CHANGELOG.md](CHANGELOG.md).
590
707
  GitHub already has pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
591
708
  and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
592
709
 
710
+ **1.3.0** (31 August 2026) adds native Windsurf / Cascade coverage: workspace
711
+ `.devin/rules` and `.windsurf/rules`, portable `agents.md`, and `--global`
712
+ `mcp_config.json` plus `global_rules.md`. Devin CLI MCP, auto memories,
713
+ system rules, and a project MCP path stay unread.
714
+
715
+ **1.2.1** (31 August 2026) opens Codex user MCP (`$CODEX_HOME/config.toml`
716
+ or `~/.codex/config.toml`) under `--global`. Same-name user and project
717
+ servers are both inventoried. System, managed, requirements, profiles,
718
+ plugins, and `trust_level` stay unread. `auth.json` is never opened.
719
+
720
+ **1.2.0** (31 August 2026) adds a native Grok Build provider: project
721
+ `.grok/*` plus `$GROK_HOME` / `~/.grok/*` under `--global`. Compatibility
722
+ with Claude or Cursor is not schema identity. Agents, managed/requirements
723
+ TOML, plugins, `[skills] paths`, and `.gitignore` skip stay unread.
724
+
725
+ **1.1.0** (31 August 2026) adds a native Command Code provider and finishes the
726
+ correctness pass across registered checks: hook schema profiles, Agent Skills
727
+ optional fields, Claude listing budget, Codex chain knobs, and Copilot CLI
728
+ `version: 1` files. Still offline on `check`. Shared `.mcp.json` is `mcp-json`
729
+ (Claude and Command Code), not Claude-only — that MCP hotfix is **1.0.1** in
730
+ the changelog and ships inside this package version.
731
+
593
732
  **1.0.0** (30 August 2026) is the first stable release: 59 checks, 345 tests,
594
733
  still offline on `check`. It adds portable Agent Skills (including Cursor and
595
734
  Codex skill trees), MCP profiles for VS Code / Cursor / Codex / Antigravity /