@chimix/agentscan 1.0.0 → 1.1.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 +143 -0
  2. package/README.md +147 -65
  3. package/dist/cli.js +10028 -8119
  4. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,149 @@ read-only in every version below.
7
7
  GitHub already has release pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
8
8
  and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
9
9
  0.9.0 was planned as a separate train and shipped inside 1.0.0.
10
+ 1.0.1 is the shared-MCP hotfix; it is documented below and ships inside 1.1.0
11
+ (the published package version). A separate 1.0.1 npm tag would need an
12
+ intermediate commit that this train does not cut.
13
+
14
+ ## 1.1.0 — 2026-08-31
15
+
16
+ Native Command Code provider, then a correctness pass over every registered
17
+ check against 2026-08-31 official docs. Includes 1.0.1. **87 checks**, **420
18
+ tests**.
19
+
20
+ ### Added
21
+
22
+ - Hook schema profiles: `claude` | `vscode-native` | `copilot-cli` |
23
+ `commandcode`. `.github/hooks` with `version: 1` is Copilot CLI.
24
+ - Claude hook schema: required `type` (not inferred from `command`/`url`);
25
+ `mcp_tool` needs `server` + `tool`; `prompt`/`agent` need `prompt`;
26
+ documented event/handler compatibility; nested matcher groups only.
27
+ - Copilot CLI: camelCase events mapped onto VS Code names; `bash` /
28
+ `powershell` / `command`; `cwd`; `timeoutSec` then `timeout`. Prompt
29
+ hooks only on `sessionStart`. User `~/.copilot/hooks` under `--global`.
30
+ - Agent Skills optional constrained fields: `compatibility` 1..500,
31
+ `metadata` map<string,string>, `allowed-tools` string. Info for
32
+ `SKILL.md` >500 lines. File references resolve from the skill root only.
33
+ - Claude skill listing budget: 1% context window, 8000-character fallback,
34
+ per-entry cap 1536, listing text is description (or first markdown
35
+ paragraph) plus `when_to_use`. First paragraph satisfies missing
36
+ description.
37
+ - OpenCode V2: local `command` must be an argv array
38
+ (`opencode.mcp.command-not-array`).
39
+ - Continue standalone YAML blocks require `name` / `version` / `schema`
40
+ (`continue.mcp.missing-block-metadata`). Copied JSON is not checked.
41
+ - Gemini underscore alias warning (`gemini.mcp.underscore-alias`).
42
+ - Claude reserved MCP names including `workspace`
43
+ (`claude.mcp.reserved-name`).
44
+ - Codex chain knobs: `AGENTS.override.md` > `AGENTS.md` >
45
+ `project_doc_fallback_filenames`; one file per directory; root→cwd;
46
+ `project_doc_max_bytes`; `project_root_markers`.
47
+ - Coverage matrix uses five dimensions (project discovery, global
48
+ discovery, schema, precedence, conformance) instead of `full` /
49
+ `partial`.
50
+ - Conformance fixture `tests/fixtures/conformance/copilot-hooks/`.
51
+ - Spec capture `docs/spec/copilot-hooks.md`. Alias
52
+ `claude.hook.mcp-tool-without-name` →
53
+ `claude.hook.mcp-tool-without-server-or-tool`.
54
+
55
+ ### Changed
56
+
57
+ - Nested `<dir>/.commandcode/AGENTS.md` is Command Code memory when that
58
+ directory has no `AGENTS.md` (official memory table).
59
+ - Native VS Code remains command-only with its documented eight events.
60
+ `vscode.hook.http-without-url` and `vscode.hook.mcp-tool-without-name`
61
+ are removed.
62
+ - Claude agent names: lowercase letters and hyphens (no digits); filename
63
+ is not compared to `name`.
64
+ - `skill.description-budget` provenance is vendor-recommendation, not a
65
+ 16000-byte heuristic.
66
+
67
+ ### Added (Command Code provider)
68
+
69
+ - Provider `commandcode` from official pages under `docs/spec/commandcode-*.md`
70
+ (read 2026-08-31). Unknown stays skip: no invented local-project MCP slug,
71
+ no bundled-skill scan, no `--skill` flags, no model-id list, no mods
72
+ execution.
73
+ - Skills: `.commandcode/skills` only at the Command Code project root
74
+ (Agent Skills contract), `.agents/skills` walk-up (max 10 hops from cwd,
75
+ stopping at home), extra dirs from the highest-precedence settings
76
+ `skills` array (replace, not merge; relative paths resolve against the git
77
+ root). User `~/.commandcode/skills` and `~/.agents/skills` under
78
+ `--global`.
79
+ - Memory: per directory, first existing of `AGENTS.md` or
80
+ `.commandcode/AGENTS.md` (project and nested). User
81
+ `~/.commandcode/AGENTS.md` under `--global`. Unresolved `@path` is not a
82
+ hard error. Codex's instruction chain excludes `.commandcode/AGENTS.md`.
83
+ - Agents: `<project>/.commandcode/agents/*.md` at the Command Code project
84
+ root only (git root, or cwd outside a git repo); filename supplies
85
+ `name`; no `claude.agent.*` on these files. Checks:
86
+ `commandcode.agent.reserved-name` (`explore` / `plan` / `review` /
87
+ `general`), `invalid-permission-mode`, `invalid-field-type` covering every
88
+ documented custom-agent typed field (`reasoningEffort` is not one of them).
89
+ `maxTurns` must be a positive integer.
90
+ - Hooks: four events (`PreToolUse`, `PostToolUse`, `Stop`, `SessionStart`);
91
+ nested `hooks` array required; `type` must be the string `"command"`;
92
+ `command` must be a string (not an argv array); `matcher` must be a string
93
+ when present. Timeout 0–600. A matcher on `Stop` / `SessionStart` is
94
+ runtime-dead, not a schema error. Project and user hooks for the same event
95
+ coexist; only an exact duplicate command string is deduplicated (project
96
+ wins). `settings.local.json` follows settings merge (an event array
97
+ replaces the project array for that event). `COMMANDCODE_PROJECT_DIR` and
98
+ `COMMANDCODE_CWD` expand against the Command Code project root.
99
+ - Settings: Command Code project root is the git root (a child `.cursor`
100
+ does not hide `<git-root>/.commandcode/`). `model` inventoried, not
101
+ validated. Inline `mcp.servers` (array or object map; unnamed array items
102
+ are inventory-only). User `~/.commandcode/mcp.json` under `--global`.
103
+ Spec/runtime checks skip shadowed lower-precedence definitions; security
104
+ checks still inspect every readable MCP file.
105
+ - Slash commands: inventory `.commandcode/commands` at the project root and
106
+ the user dir — no required-field checks.
107
+ - Mods: inventory `mods.paths` — never execute or import TypeScript.
108
+ - Conformance fixture `tests/fixtures/conformance/commandcode/`.
109
+ - `spec:check` tracks captured surfaces and diffs Claude, VS Code, Copilot
110
+ CLI, and Command Code hook events against live pages.
111
+
112
+ ### Also changed
113
+
114
+ - Default `skillPaths` includes `.commandcode/skills`.
115
+ - `--global` / `includeGlobal` also scans `~/.commandcode/skills`,
116
+ `~/.agents/skills`, user Command Code agents, MCP, memory, Copilot user
117
+ hooks, and Codex global AGENTS files.
118
+ - Shared `.mcp.json` is `mcp-json`, not Claude-only.
119
+
120
+ ## 1.0.1 — 2026-08-31
121
+
122
+ Shared `.mcp.json` is not Claude-only. Command Code writes it for `--scope
123
+ project` with `transport` (and `type` as an alias). A Claude-keyed
124
+ `url`-without-`type` check on that path is a false positive on valid
125
+ Command Code HTTP config.
126
+
127
+ ### Fixed
128
+
129
+ - `.mcp.json` uses schema profile `mcp-json`, `consumedBy: ["claude",
130
+ "commandcode"]`, `sourceProvider: "unknown"`. `.claude/mcp.json` and
131
+ project `mcp.json` stay `claude-json`.
132
+ - Parse `transport` first; `type` is an alias. Store which field was
133
+ present.
134
+ - `claude.mcp.url-without-type` on `mcp-json` fires only when `url` is set
135
+ and transport is unset. On `claude-json` it still fires when the field is
136
+ `transport` or missing. `{ "transport": "http", "url": "…" }` does not
137
+ emit this.
138
+ - Command Code transport defects on `mcp-json` and `commandcode-json`:
139
+ `commandcode.mcp.invalid-transport`, `http-without-url`,
140
+ `stdio-without-command`. Valid transports are `http` and `stdio`. Claude
141
+ `sse` / `ws` on shared `.mcp.json` is not a Command Code invalid-transport.
142
+ - Empty launch on `mcp-json` still emits `claude.mcp.no-launch` (both
143
+ consumers need a launch field). `commandcode-json` empty launch emits
144
+ `commandcode.mcp.no-launch`.
145
+ - `looksLikeServerEntry` recognises `transport`. Absolute MCP paths under
146
+ `--global` are not joined as relative.
147
+
148
+ ### Security
149
+
150
+ - Never open `~/.commandcode/auth.json` or `mcp-tokens.json`.
151
+ - Local `~/.commandcode/projects/{project}/mcp.json` is unread: the slug
152
+ encoding is unpublished. Prefer skip over inventing it.
10
153
 
11
154
  ## 1.0.0 — 2026-08-30
12
155
 
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-87-111111?style=flat-square" alt="87 checks">
9
+ <img src="https://img.shields.io/badge/tests-420%20passing-111111?style=flat-square" alt="420 tests">
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.1.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>87 checks &middot; offline &middot; 0 network calls on <code>check</code> &middot; provenance on every rule</strong><br>
20
+ <sub>An offline linter for Claude Code, Command Code, portable Agent Skills, nested AGENTS.md, Copilot CLI hooks, and the MCP / hooks / rules surfaces that 1.1.0 actually implements. Spec-required checks cite a published line in <a href="docs/spec/">docs/spec/</a>. Heuristics stay at <code>info</code> and say so. The coverage matrix below is the honesty contract — five dimensions, and a documented global location that is not scanned stays unread.</sub>
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/ .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 87 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/` or
120
+ `.commandcode/` 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`, `~/.commandcode/skills`, and `~/.agents/skills` (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.1.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.1.0",
235
235
  "root": "/path/to/project",
236
236
  "factsSummary": {
237
237
  "packageManager": "bun",
@@ -259,10 +259,14 @@ 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`, and
263
+ `~/.agents/skills` to the structural checks: a `SKILL.md` that is malformed is
264
+ malformed wherever it lives, and those findings carry a `source: global`
265
+ evidence entry so you can tell them apart. Codex and Command Code skills use
266
+ the Agent Skills contract (`name` and `description` required). `--global` also
267
+ inventories user Command Code agents (`~/.commandcode/agents`), MCP
268
+ (`~/.commandcode/mcp.json` and user settings `mcp.servers`), and memory
269
+ (`~/.commandcode/AGENTS.md`). It never opens `auth.json` or `mcp-tokens.json`.
266
270
 
267
271
  Lockfile checks stay project-scoped — a project lockfile cannot pin a skill that
268
272
  lives in your home directory, so reporting one as "not in the lockfile" could
@@ -342,8 +346,8 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
342
346
 
343
347
  ```json
344
348
  {
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"],
349
+ "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills", ".codex/skills", ".commandcode/skills"],
350
+ "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
351
  "policyFiles": ["AGENTS.md", "CLAUDE.md"],
348
352
  "ignoreSkills": [],
349
353
  "ignoreRules": [],
@@ -352,8 +356,8 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
352
356
  "includeGlobal": false,
353
357
  "requireLock": false,
354
358
  "thresholds": {
355
- "skillDescriptionBytes": 16000,
356
- "skills": 30,
359
+ "skillListingChars": 8000,
360
+ "skillListingMaxDescChars": 1536,
357
361
  "mcp": 5,
358
362
  "agentsMdLines": 150,
359
363
  "claudeMdLines": 200,
@@ -434,29 +438,61 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
434
438
  | `claude.agent.missing-frontmatter` | error | A Claude agent definition with no `---` block |
435
439
  | `claude.agent.missing-description` | error | Claude agent frontmatter has no `description` |
436
440
  | `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 |
441
+ | `claude.agent.invalid-name` | warning | Claude agent name is not lowercase letters and hyphens (error if it starts with `-` or contains `:`; filename is not compared) |
438
442
  | `claude.agent.duplicate-name` | error | Multiple Claude agent files declare the same name |
439
443
  | `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 |
444
+ | `claude.mcp.url-without-type` | error | A Claude remote MCP server with a `url` but no `type` — read as stdio and skipped. On shared `.mcp.json`, a Command Code `transport` field satisfies the other consumer and this check does not fire |
445
+ | `claude.mcp.reserved-name` | error | A Claude-consumed MCP server named with a reserved identifier (`workspace`, `claude-in-chrome`, `computer-use`, `Claude Preview`, `Claude Browser`) — skipped at load |
441
446
  | `vscode.mcp.no-launch` | error | A VS Code MCP server (`servers`) with neither `command` nor `url` |
442
447
  | `cursor.mcp.no-launch` | error | A Cursor MCP server with neither `command` nor `url` |
443
448
  | `antigravity.mcp.no-launch` | error | An Antigravity MCP server with neither `command` nor `serverUrl` |
444
449
  | `codex.mcp.no-launch` | error | A Codex `[mcp_servers.*]` entry with neither `command` nor `url` |
445
450
  | `gemini.mcp.no-launch` | error | A Gemini MCP server with neither `command`, `url`, nor `httpUrl` |
451
+ | `gemini.mcp.underscore-alias` | warning | A Gemini MCP server alias contains `_`, which can make policies fail silently |
446
452
  | `opencode.mcp.no-launch` | error | An OpenCode MCP server with neither `command` nor `url` |
447
453
  | `opencode.mcp.missing-type` | error | An OpenCode MCP server missing `type: local` or `type: remote` |
448
454
  | `opencode.mcp.local-without-command` | error | An OpenCode `type: local` server with no `command` |
449
455
  | `opencode.mcp.remote-without-url` | error | An OpenCode `type: remote` server with no `url` |
450
456
  | `opencode.mcp.invalid-launch-for-type` | error | An OpenCode server whose launch field does not match its `type` |
457
+ | `opencode.mcp.command-not-array` | error | An OpenCode V2 local `command` that is not an argv array |
451
458
  | `continue.mcp.no-launch` | error | A Continue MCP server with neither `command`, `url`, nor `uses` |
459
+ | `continue.mcp.missing-block-metadata` | error | A standalone `.continue/mcpServers/*.yaml` block missing `name`, `version`, or `schema` (not applied to copied JSON) |
460
+ | `commandcode.mcp.no-launch` | error | A Command Code settings / user MCP server with neither `command` nor `url` |
461
+ | `commandcode.mcp.invalid-transport` | error | A Command Code MCP `transport` / `type` that is not `http` or `stdio` (Claude `sse` / `ws` on shared `.mcp.json` is skipped) |
462
+ | `commandcode.mcp.http-without-url` | error | A Command Code HTTP MCP server with no `url` |
463
+ | `commandcode.mcp.stdio-without-command` | error | A Command Code stdio MCP server with no `command` |
452
464
  | `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 |
465
+ | `claude.hook.http-without-url` | error | A Claude `type: http` hook with no `url` |
466
+ | `claude.hook.mcp-tool-without-server-or-tool` | error | A Claude `type: mcp_tool` hook missing `server` or `tool` |
467
+ | `claude.hook.unknown-handler-type` | error | A Claude hook whose required `type` is missing or not one of command, http, mcp_tool, prompt, agent |
468
+ | `claude.hook.prompt-without-prompt` | error | A Claude `type: prompt` or `type: agent` hook with no `prompt` |
469
+ | `claude.hook.incompatible-handler` | error | A Claude handler type that the event does not support |
470
+ | `claude.hook.invalid-group` | error | A Claude hook matcher group with no nested `hooks` array (flat handler arrays are invalid) |
471
+ | `vscode.hook.invalid-group` | error | A VS Code hook group that is not a command handler array |
472
+ | `vscode.hook.command-without-command` | error | A native VS Code `type: command` hook with no `command` |
473
+ | `vscode.hook.unknown-handler-type` | error | A native VS Code hook whose `type` is not `command` |
474
+ | `copilot.hook.unknown-event` | error | A Copilot CLI hook registered under an event that is never dispatched |
475
+ | `copilot.hook.missing-script` | error | A Copilot CLI command hook whose script does not exist |
476
+ | `copilot.hook.command-without-command` | error | A Copilot CLI command hook with none of `bash`, `powershell`, or `command` |
477
+ | `copilot.hook.http-without-url` | error | A Copilot CLI `type: http` hook with no `url` |
478
+ | `copilot.hook.prompt-without-prompt` | error | A Copilot CLI `type: prompt` hook with no `prompt` |
479
+ | `copilot.hook.unknown-handler-type` | error | A Copilot CLI hook whose `type` is not command, http, or prompt |
480
+ | `copilot.hook.incompatible-handler` | error | A Copilot CLI prompt hook registered on an event other than `sessionStart` |
481
+ | `commandcode.hook.unknown-event` | error | A Command Code hook registered under an event that is never dispatched (four events only) |
482
+ | `commandcode.hook.missing-script` | error | A Command Code command hook whose script does not exist |
483
+ | `commandcode.hook.invalid-group` | error | A Command Code hook group missing the required nested `hooks` array, or a non-string `matcher` |
484
+ | `commandcode.hook.command-without-command` | error | A Command Code `type: command` hook whose `command` is missing, empty, or not a string |
485
+ | `commandcode.hook.unknown-handler-type` | error | A Command Code hook whose `type` is missing, not a string, or not `"command"` |
486
+ | `commandcode.hook.timeout-out-of-bounds` | error | A Command Code hook `timeout` outside 0–600 seconds |
487
+ | `commandcode.agent.reserved-name` | error | A Command Code custom agent named `explore`, `plan`, `review`, or `general` — ignored at load |
488
+ | `commandcode.agent.invalid-permission-mode` | error | A Command Code agent `permissionMode` that is not a documented value |
489
+ | `commandcode.agent.invalid-field-type` | error | A Command Code agent field whose type the spec does not accept |
454
490
  | `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
455
491
  | `security.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
456
492
  | `mcp.literal-env` | warning | Secret-named `env` values that are literals instead of interpolation |
457
493
  | `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
458
494
  | `claude.skill.missing-frontmatter` | warning | Claude `SKILL.md` with no `---` block |
459
- | `claude.skill.missing-description` | info | Claude frontmatter has no `description` (Recommended) |
495
+ | `claude.skill.missing-description` | info | Claude frontmatter has no `description` and no first markdown paragraph |
460
496
  | `agent-skills.skill.missing-frontmatter` | error | Portable skill `SKILL.md` has no YAML frontmatter |
461
497
  | `agent-skills.skill.missing-name` | error | Portable skill frontmatter has no required `name` |
462
498
  | `agent-skills.skill.missing-description` | error | Portable skill frontmatter has no required `description` |
@@ -464,11 +500,15 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
464
500
  | `agent-skills.skill.name-does-not-match-directory` | error | Portable skill `name` does not match its directory |
465
501
  | `agent-skills.skill.name-too-long` | error | Portable skill name exceeds 64 characters |
466
502
  | `agent-skills.skill.description-too-long` | error | Portable skill description exceeds 1024 characters |
503
+ | `agent-skills.skill.invalid-compatibility` | error | Portable skill `compatibility` is present but is not a 1–500 character string |
504
+ | `agent-skills.skill.invalid-metadata` | error | Portable skill `metadata` is present but is not `map<string, string>` |
505
+ | `agent-skills.skill.invalid-allowed-tools` | error | Portable skill `allowed-tools` is present but is not a string |
506
+ | `agent-skills.skill.body-too-large` | info | Portable skill `SKILL.md` exceeds the 500-line recommendation |
467
507
  | `skill.broken-reference` | warning | The body points at a bundled file that does not exist |
468
508
  | `skill.duplicate-description` | info | Two or more skills carry an identical description (heuristic) |
469
509
  | `skill.locked-not-installed` | warning | `skills-lock.json` pins a skill that is not on disk |
470
510
  | `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) |
511
+ | `skill.description-budget` | info | Claude skill listing text (description or first paragraph, plus `when_to_use`) exceeds the 1% context-window budget, with an 8000-character fallback and a 1536-character per-entry cap |
472
512
  | `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
473
513
  | `budget.agents-md` | info | `AGENTS.md` past a secondary 150-line hint (heuristic; not an AGENTS.md requirement) |
474
514
  | `budget.claude-md` | info | `CLAUDE.md` past the official “target under 200 lines” recommendation |
@@ -477,44 +517,57 @@ internal-consistency, or heuristic. `agentscan rules` lists them all.
477
517
  | `codex.budget.instructions` | info | Codex root→cwd `AGENTS.md` chain exceeds the default 32 KiB |
478
518
  | `cursor.rule.too-large` | info | A `.cursor/rules/*.mdc` file exceeds the 500-line recommendation |
479
519
 
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`) |
520
+ ### Coverage in 1.1.0
521
+
522
+ Each cell is one coverage dimension. There is no `full` / `partial` label: a
523
+ documented global or static location that is not opened stays **unread**.
524
+
525
+ Dimensions:
526
+
527
+ - **project discovery** — documented in-repo files the scanner opens
528
+ - **global discovery** — documented user / machine locations; `--global` is named when that is the only path that opens them
529
+ - **schema** — spec-required (and labeled vendor-recommendation) field checks
530
+ - **precedence** — documented merge / override / shadowing
531
+ - **conformance** — official-shaped fixture or equivalent negative tests
532
+
533
+ | Ecosystem | project discovery | global discovery | schema | precedence | conformance |
534
+ |-----------|-------------------|------------------|--------|------------|-------------|
535
+ | Agent Skills | `.agents/skills` (and Cursor / Codex / Command Code skill trees) | `--global` `~/.agents/skills`, `~/.codex/skills` | required `name` / `description` plus optional `compatibility` / `metadata` / `allowed-tools`; 500-line info | n/a | `agent-skills` fixture |
536
+ | AGENTS.md | nested walk-up | n/a | no required fields | nearest-wins | tests |
537
+ | Claude Code | project settings, skills, agents, `CLAUDE.md`, `.mcp.json` / `.claude/mcp.json` | unread `~/.claude/settings.json`, managed policy, marketplace plugins | 33 events; required handler `type`; MCP reserved names; first-paragraph skill description; listing budget | walk-up `CLAUDE.md` / `.claude/agents` | `claude-json` fixture |
538
+ | Command Code | git-root project files; per-directory `AGENTS.md` else `.commandcode/AGENTS.md` | `--global` `~/.commandcode/*`; unread `projects/{slug}/mcp.json` | 4 events; command handlers; Agent Skills; MCP `transport` / `type` | settings merge; project+user hooks coexist; skill/MCP shadow | `commandcode` fixture |
539
+ | Codex | `.codex/config.toml`, `.codex/skills`, AGENTS chain | `--global` `~/.codex/AGENTS.override.md` then `AGENTS.md` | TOML MCP; `project_doc_max_bytes`; Agent Skills | override > `AGENTS.md` > fallbacks; one file per dir; root→cwd; `project_root_markers` | `codex-toml` fixture |
540
+ | VS Code | `.github/hooks` without `version: 1`, instruction files, `.github/agents`, `.vscode/mcp.json` | `--global` `~/.copilot/hooks` without `version: 1`; unread policy dirs | 8 events; command-only | workspace over user | `vscode-hooks`, `vscode-json` |
541
+ | Copilot CLI | `.github/hooks` with `version: 1` | `--global` `~/.copilot/hooks` with `version: 1`; unread `/etc/github-copilot/policy.d` | camelCase + PascalCase map; `bash` / `powershell` / `command`; `cwd`; `timeoutSec`; prompt on `sessionStart` | documented sources coexist; policy unread | `copilot-hooks` fixture |
542
+ | Cursor | nested `.cursor/skills`, `.cursor/mcp.json`, `.cursor/rules` | unread documented Cursor user/global paths | Agent Skills; MCP launch; 500-line rules | n/a | `cursor-json` fixture |
543
+ | Grok | none | none | none | n/a | none |
544
+ | Antigravity | `.agents/mcp_config.json` | none | `serverUrl` launch | n/a | `antigravity-json` fixture |
545
+ | Gemini | `.gemini/settings.json` | unread `~/.gemini/settings.json` unless `--global` is wired for it (it is not) | `command` / `url` / `httpUrl`; underscore-alias warning | n/a | `gemini-json` fixture |
546
+ | Windsurf | none | unread (official page is global-only) | none | n/a | none |
547
+ | Kiro | none | none | none | n/a | none |
548
+ | Cline | none | none | none | n/a | none |
549
+ | Roo | none | none | none | n/a | none |
550
+ | Kilo | none | none | none | n/a | none |
551
+ | OpenCode | `opencode.json(c)` V1 and V2 | none | V2 local `command` must be an argv array | n/a | `opencode-json` fixture |
552
+ | Junie | none | none | none | n/a | none |
553
+ | Continue | `.continue/config.yaml`, `.continue/mcpServers/*` | none | launch `command` / `url` / `uses`; standalone YAML `name` / `version` / `schema` | n/a | `continue-yaml`, `continue-mcpservers` |
503
554
 
504
555
  Cursor project rules (`.cursor/rules/**/*.mdc`) are a separate surface: discovery plus `cursor.rule.too-large` at info. Claude `.claude/rules/**/*.md` is inventoried and has no published line budget.
505
556
 
506
557
  Old 0.7 rule ids still work in `ignoreRules` and `explain` (for example `hook.missing-script` aliases `claude.hook.missing-script`).
507
558
 
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).
559
+ Agent definitions are checked for structure only. A Claude agent's frontmatter
560
+ `name` is not compared to the filename; it must be lowercase letters and
561
+ hyphens and unique. Command Code supplies `name` from the filename when
562
+ frontmatter omits it, and does not emit missing-name or missing-description.
563
+ Model identifiers and tool names are likewise not validated: both would need a
564
+ hardcoded list, which is the shape that already shipped a false error. See
565
+ [plans/003](plans/003-validate-agent-definitions.md).
513
566
 
514
567
  `skill.broken-reference` reads the body, not just the frontmatter. It looks for
515
568
  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
569
+ `assets/`, `templates/`, `examples/`). Agent Skills references resolve from the
570
+ skill root only. Claude native skills also try the repo root — of 1674
518
571
  references measured across 17 projects, 1645 resolved skill-relative and 12 only
519
572
  at the root. Fenced code blocks are stripped first: a path in an example is
520
573
  illustration, not a pointer.
@@ -522,7 +575,8 @@ illustration, not a pointer.
522
575
  ### Where it looks for hooks
523
576
 
524
577
  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:
578
+ reads the four that live inside the scanned project, plus VS Code workspace hooks
579
+ and Command Code settings hooks:
526
580
 
527
581
  | Registered in | Script paths resolve against |
528
582
  |---|---|
@@ -530,7 +584,10 @@ reads the four that live inside the scanned project, plus VS Code workspace hook
530
584
  | 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
585
  | `SKILL.md` frontmatter | the skill's own directory, then the project root |
532
586
  | `.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 |
587
+ | `.github/hooks/*.json` without `version: 1` (native VS Code) | project root and the hooks directory |
588
+ | `.github/hooks/*.json` with `version: 1` (Copilot CLI) | project root, `cwd`, host `bash` / `powershell` / `command` |
589
+ | `~/.copilot/hooks` under `--global` | same split on `version: 1` |
590
+ | `.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` |
534
591
 
535
592
  `${CLAUDE_PLUGIN_ROOT}` is expanded **only** for a hook that came from a plugin.
536
593
  In a settings file it names nothing, so the path is skipped rather than guessed
@@ -543,17 +600,18 @@ A value is only resolved when it is path-like and the answer is certain; shell
543
600
  programs (`a && b`, `$(...)`, pipes), bare PATH binaries (`npx`, `uvx`), and
544
601
  unresolved env vars are skipped. A false "broken" on a working `npx` server
545
602
  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.
603
+ Only `$CLAUDE_PROJECT_DIR`, `$COMMANDCODE_PROJECT_DIR`, and `$COMMANDCODE_CWD`
604
+ are expanded — other variables are left alone rather than guessed at. Schema
605
+ checks judge misconfiguration, not whether a correctly shaped entry will start
606
+ at runtime.
549
607
 
550
608
  Spec-required checks are written against published pages, not against what
551
609
  happens to appear in real projects. Two that were written the other way round
552
610
  shipped as false positives — a nine-name hook-event list where the spec has 31,
553
611
  and a `name` must equal the directory rule that Claude's spec explicitly
554
612
  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`.
613
+ the installer lock checks, and the size budgets other than `budget.claude-md`
614
+ and `skill.description-budget`) are labeled in the registry and stay at `info`.
557
615
 
558
616
  Assumptions are recorded in **[docs/spec/](docs/spec/)** with source URL, read
559
617
  date, and provenance. When adding a spec-required check, add its spec line there
@@ -572,11 +630,28 @@ first.
572
630
  - **Runtime split.** Source development and the composite Action use Bun; the
573
631
  published `dist/cli.js` runs on Node 20.11+ or Bun. The source entrypoint uses
574
632
  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
633
+ - **Command Code local MCP is unread.** `~/.commandcode/projects/{project}/mcp.json`
634
+ is keyed by a working-directory slug that the sessions page does not publish.
635
+ Until it does, that path is skipped rather than guessed. Bundled Command Code
636
+ skills and `--skill` launch flags are not project files and are not scanned.
637
+ `mods.paths` are inventoried; the TypeScript is never executed or imported.
638
+ `auth.json` and `mcp-tokens.json` are never opened. Command Code project
639
+ root is the git root (or cwd outside a git repo); a child `.cursor` does
640
+ not hide `<git-root>/.commandcode/`. Spec/runtime Command Code checks skip
641
+ shadowed lower-precedence definitions; secrets still inspect every readable
642
+ MCP file.
643
+ - **Three Claude hook locations are unread.** `~/.claude/settings.json`, managed policy
576
644
  settings, and installed marketplace plugins under `~/.claude/plugins` all sit
577
645
  outside the scanned project, and the docs say a plugin's install directory
578
646
  changes on every update. A hook registered in one of those, pointing at a
579
647
  missing script, is not reported.
648
+ - **Copilot CLI policy hooks are unread.** `/etc/github-copilot/policy.d` and the
649
+ Windows policy directory are machine-wide admin files. User
650
+ `~/.copilot/hooks` is scanned only under `--global`. Inline Copilot
651
+ `.github/copilot/settings.json` hooks are unread.
652
+ - **Gemini user settings are unread.** `~/.gemini/settings.json` is outside a
653
+ normal project scan. Windsurf's official MCP page is global-only and is not
654
+ scanned.
580
655
  - **Bounded reads.** A `SKILL.md` is read to 64 KB and a policy file to 100 KB,
581
656
  so `skill.broken-reference` and `policyLines` see only that much of a larger
582
657
  file. This is reported — `scan.truncated`, at `info` — rather than left
@@ -590,6 +665,13 @@ Notes for every published version are in [CHANGELOG.md](CHANGELOG.md).
590
665
  GitHub already has pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
591
666
  and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
592
667
 
668
+ **1.1.0** (31 August 2026) adds a native Command Code provider and finishes the
669
+ correctness pass across registered checks: hook schema profiles, Agent Skills
670
+ optional fields, Claude listing budget, Codex chain knobs, and Copilot CLI
671
+ `version: 1` files. Still offline on `check`. Shared `.mcp.json` is `mcp-json`
672
+ (Claude and Command Code), not Claude-only — that MCP hotfix is **1.0.1** in
673
+ the changelog and ships inside this package version.
674
+
593
675
  **1.0.0** (30 August 2026) is the first stable release: 59 checks, 345 tests,
594
676
  still offline on `check`. It adds portable Agent Skills (including Cursor and
595
677
  Codex skill trees), MCP profiles for VS Code / Cursor / Codex / Antigravity /