@chimix/agentscan 0.7.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +225 -0
  2. package/README.md +145 -65
  3. package/dist/cli.js +4673 -552
  4. package/package.json +5 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,225 @@
1
+ # Changelog
2
+
3
+ Notable changes to `@chimix/agentscan`. Dates are UTC publish days from git
4
+ and the existing GitHub releases. The `check` path stays offline and
5
+ read-only in every version below.
6
+
7
+ GitHub already has release pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
8
+ and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
9
+ 0.9.0 was planned as a separate train and shipped inside 1.0.0.
10
+
11
+ ## 1.0.0 — 2026-08-30
12
+
13
+ First stable release. 59 checks, 345 tests. The coverage matrix in the
14
+ README is the honesty contract: `full` only where discovery, spec-required
15
+ fields, and a conformance fixture (or equivalent tests) all exist.
16
+
17
+ ### Added
18
+
19
+ - Portable Agent Skills required fields (`name`, `description`, directory
20
+ match) for `.agents/skills`, nested Cursor `.cursor/skills`, and
21
+ `.codex/skills`.
22
+ - Path-keyed MCP parsers: VS Code `servers`, Cursor, Antigravity
23
+ `serverUrl`, Codex TOML, Gemini `command` / `url` / `httpUrl`, OpenCode
24
+ V1 and V2, Continue `config.yaml` plus `.continue/mcpServers/*`.
25
+ - Nested / walk-up `AGENTS.md` with nearest-wins; Codex instruction budget
26
+ uses the effective per-directory file and `project_doc_max_bytes`.
27
+ - Walk-up `.claude/agents`; VS Code `.github/agents` without Claude
28
+ missing-name errors.
29
+ - VS Code `.github/hooks/*.json` (eight events) and per-provider hook
30
+ event sets.
31
+ - Cursor `.cursor/rules/**/*.mdc` 500-line recommendation
32
+ (`cursor.rule.too-large`, info). Claude rules are inventoried only.
33
+ - Official-shaped fixtures under `tests/fixtures/conformance/`.
34
+ - `spec:check` tracks 21 captured surfaces, not only Claude hook events.
35
+ - Launch `cwd` and `windows` / `linux` / `osx` overrides on hooks and MCP.
36
+ - GitHub Action pin `@v1`.
37
+
38
+ ### Changed
39
+
40
+ - Product copy describes what 1.0.0 actually implements. Heuristics stay
41
+ at `info` and are labeled in the registry.
42
+ - `--global` also scans `~/.codex/skills` under the Agent Skills contract.
43
+ - In a monorepo, each `skills-lock.json` governs only the skills under
44
+ its directory (nearest lock wins).
45
+
46
+ ### Fixed
47
+
48
+ - Nested `SKILL.md` trees are one skill, not a missing-md folder.
49
+ - Codex instruction chain: at most one file per directory; honour
50
+ `project_doc_max_bytes`.
51
+ - Interpreter argv (`node script.js`), MCP `command` arrays, and OS
52
+ overrides are path-checked. `--` inline code (`node -e`) is not a path.
53
+ - JSONC (comments and trailing commas) parses via `jsonc-parser`; the
54
+ published bundle imports the ESM entry so Node can load `dist/cli.js`.
55
+ - Scan boundary stays at Git/workspace. A child `.cursor` does not hide
56
+ parent Claude or Codex.
57
+ - `uses:` is a launch only on Continue. Other profiles treat a uses-only
58
+ entry as non-launchable.
59
+ - OpenCode V2 requires `type` plus the matching launch field. V1
60
+ enabled-only overrides that inherit externally do not emit a hard error.
61
+ - Windows drive / UNC cwd values and OS-specific script paths
62
+ (`scripts\format.ps1`) are not joined as POSIX relative paths when the
63
+ scanner runs on Linux or macOS. Those launches are inventoried;
64
+ `scriptExists` / `commandExists` stay unset. Host-matching and
65
+ platform-neutral launches stay fully checked. `C:foo` is unresolved,
66
+ not absolute.
67
+ - Old 0.7 rule ids still work in `ignoreRules` and `explain`.
68
+
69
+ ## 0.8.0 — 2026-08-30
70
+
71
+ Honesty pass after the multi-provider audit. 38 checks, 267 tests.
72
+
73
+ ### Added
74
+
75
+ - Agent Skills profile on `.agents/skills` (required `name` /
76
+ `description`).
77
+ - MCP profile parsers for VS Code `.vscode/mcp.json` (`servers`), Cursor
78
+ `.cursor/mcp.json`, Antigravity `serverUrl`, and Codex
79
+ `[mcp_servers.*]` TOML.
80
+ - Nearest-signal project root: a child `.cursor` / `.claude` wins over a
81
+ parent `package.json`.
82
+ - Provenance on every registry entry (`spec-required`,
83
+ `vendor-recommendation`, `security`, `internal-consistency`,
84
+ `heuristic`).
85
+ - First coverage matrix (full / partial / none) for the surfaces this
86
+ version actually implemented.
87
+
88
+ ### Changed
89
+
90
+ - Claude check ids are namespaced (`claude.hook.missing-script`, …).
91
+ `ignoreRules` and `explain` accept the old 0.7 ids as aliases.
92
+ - README no longer claims every check is spec-sourced or that there are
93
+ no heuristics. Size opinions and installer policy stay at `info`.
94
+ - `budget.claude-md` quotes the official “target under 200 lines”
95
+ recommendation instead of an unsourced 150–200 / 50-token story.
96
+
97
+ ## 0.7.0 — 2026-08-24
98
+
99
+ 27 checks, 250 tests.
100
+
101
+ ### Added
102
+
103
+ - Hooks from in-tree plugin `hooks/hooks.json` and from skill / subagent
104
+ frontmatter, not only the two settings files.
105
+ `${CLAUDE_PLUGIN_ROOT}` expands only for a plugin-sourced hook.
106
+
107
+ ### Changed
108
+
109
+ - `engines.bun` is `>=1.4.0`. The repo ships the text `bun.lock`
110
+ (lockfileVersion 1); Bun 1.1 wrote `bun.lockb` and cannot read it, so
111
+ the old `>=1.1.0` floor was false. `engines.node` stays `>=20.11.0`.
112
+ - CI pins Bun; `Bun.YAML` is rejected in favour of the `yaml` package.
113
+
114
+ ## 0.6.0 — 2026-08-24
115
+
116
+ Stop reporting valid config as broken. 27 checks, 235 tests.
117
+
118
+ ### Added
119
+
120
+ - `scan.truncated` (info): a file past the 64 KB skill or 100 KB policy
121
+ cap is parsed from its prefix. The finding is about this scanner’s
122
+ reach, not about the file being invalid.
123
+
124
+ ### Changed
125
+
126
+ - `skill.duplicate-description` and `skill.description-budget` group by
127
+ the directory that owns the skill, so two apps in a monorepo no longer
128
+ look like a collision.
129
+ - `agent.invalid-name` drops from error to warning. The subagent
130
+ reference specifies the name format but documents a load failure only
131
+ for `:`.
132
+ - `--verbose` prints each finding’s id so the value `explain` and
133
+ `ignoreFindings` take can be copied out of the report.
134
+
135
+ ### Fixed
136
+
137
+ - A `SKILL.md` over the scan cap is no longer `config.unreadable` at
138
+ error. Claude Code still loads that file; the frontmatter sits in the
139
+ first 300 bytes.
140
+ - The Action’s `GITHUB_OUTPUT` heredoc delimiter is randomised.
141
+
142
+ Measured on the three projects that reproduced the defects: touchagency
143
+ 0 → 34, kronstadt-ehs-2026 40 → 94, optimad 0 → 85.
144
+
145
+ ## 0.5.0 — 2026-08-12
146
+
147
+ 26 checks.
148
+
149
+ ### Added
150
+
151
+ - `agentscan demo`: throwaway fixture with the killer missing-hook case,
152
+ human report, then delete.
153
+ - Trees with agent config and no `package.json` are scannable.
154
+ - `mcp.command-missing`: path-like `command` values are compared to
155
+ disk. Bare PATH binaries, shell compounds, and unresolved env vars
156
+ are skipped.
157
+
158
+ ### Changed
159
+
160
+ - Severity labels and neutral multi-event group headlines in the text
161
+ report.
162
+ - Public check count synced to 26.
163
+
164
+ ## 0.4.0 — 2026-08-09
165
+
166
+ Colour, a face, and a report that reads at a glance. 23 checks, 195
167
+ tests. [GitHub release](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
168
+
169
+ ### Added
170
+
171
+ - Terminal header with score, face, and bar. Piped output stays
172
+ byte-identical to the uncoloured report. `--json`, `--output prompt`,
173
+ and `--copy` are never coloured. `--no-color` and `NO_COLOR` are
174
+ honoured.
175
+
176
+ ### Changed
177
+
178
+ - The YAML rule engine is gone (709 lines, 9 info findings across 21
179
+ projects, unused extension point). Its five rules are code, with
180
+ byte-identical output.
181
+
182
+ ## 0.3.0 — 2026-08-09
183
+
184
+ ### Changed
185
+
186
+ - Repeated findings group: six dead hooks print one header with `×6`
187
+ and one line per occurrence.
188
+ - Paths are shown relative to the scanned project.
189
+ - The score word (`broken`, …) comes from the same thresholds as the
190
+ face and the colour, so they cannot disagree.
191
+
192
+ ## 0.2.1 — 2026-08-09
193
+
194
+ ### Changed
195
+
196
+ - Bare invocation (`npx @chimix/agentscan`) scans the current
197
+ directory. The quick start shows findings instead of a usage screen.
198
+
199
+ ## 0.2.0 — 2026-08-09
200
+
201
+ ### Added
202
+
203
+ - Published `dist/cli.js` runs on Node 20.11+ as well as Bun. `npx
204
+ @chimix/agentscan@latest` works without Bun installed. One bundled
205
+ file, no install-time dependencies.
206
+
207
+ ## 0.1.0 — 2026-08-09
208
+
209
+ First alpha. [GitHub release](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0).
210
+ 23 checks, each written against a published spec line in `docs/spec/`.
211
+
212
+ ### Added
213
+
214
+ - Deterministic offline CLI: broken hooks, unlaunchable MCP, credentials
215
+ pasted into config, skills that disagree with their lockfile.
216
+ - `check`, `--output prompt`, `--fail-on`, `--json`.
217
+ - Published score: `max(0, 100 - 10 × errors - 3 × warnings)`.
218
+ Coverage-ratio scoring was built, measured, and rejected.
219
+ - Published as `@chimix/agentscan` — npm rejects the bare name as too
220
+ close to an unrelated `agent-scan`. The command stays `agentscan`.
221
+
222
+ An earlier build reported 37 findings across 17 projects of which 25
223
+ were false, because two checks were written from observation. Both were
224
+ deleted before this release. Requires Bun for source development; `check`
225
+ never opens a socket and never writes the scanned tree.
package/README.md CHANGED
@@ -5,18 +5,19 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <img src="https://img.shields.io/badge/checks-27-111111?style=flat-square" alt="27 checks">
9
- <img src="https://img.shields.io/badge/tests-250%20passing-111111?style=flat-square" alt="250 tests">
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">
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
16
  </p>
16
17
 
17
18
  <p align="center">
18
- <strong>27 checks &middot; 4.2k lines &middot; 0 network calls &middot; every check sourced to a published spec line</strong><br>
19
- <sub>Alpha. An earlier build reported 37 findings across 17 real projects of which <strong>25 were false</strong> — two checks had been written from what real projects looked like instead of from the spec. Both were deleted, and every check that survived is recorded in <a href="docs/spec/">docs/spec/</a> with the URL it came from and the date it was read. That story is the reason this tool exists in its current shape.</sub>
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>
20
21
  </p>
21
22
 
22
23
  ---
@@ -47,7 +48,7 @@ think you have has silently not existed for six weeks.
47
48
  ```console
48
49
  $ agentscan check
49
50
 
50
- ERROR rule:hook.missing-script
51
+ ERROR rule:claude.hook.missing-script
51
52
  PreToolUse hook points at a script that does not exist: .claude/hooks/guard-destructive-bash.js
52
53
  PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
53
54
 
@@ -59,12 +60,13 @@ of the filesystem**, where nothing else in your stack will ever tell you.
59
60
 
60
61
  ## How it works
61
62
 
62
- No AI, no network, no heuristics. Read the config, read the disk, compare:
63
+ No AI, no network on `check`. Read the config, read the disk, compare:
63
64
 
64
65
  ```
65
- 1. Discover .claude/ .agents/ .mcp.json AGENTS.md skills-lock.json plugin hooks/
66
+ 1. Discover .claude/ .agents/ .vscode/ .cursor/ .codex/ .gemini/ .github/ .continue/ AGENTS.md skills-lock.json
66
67
  2. Extract immutable facts — never re-read during checking
67
- 3. Check 27 checks, each against one published spec line
68
+ 3. Check 59 checks, each labeled spec-required, vendor-recommendation,
69
+ security, internal-consistency, or heuristic
68
70
  4. Report text · --json · --output prompt (handoff for a fixing agent)
69
71
  ```
70
72
 
@@ -73,13 +75,17 @@ scans and never opens a socket.
73
75
 
74
76
  ## What it will not do
75
77
 
76
- It will not compare a skill's frontmatter `name` to its directory, and it will
77
- not validate model ids or MCP tool names. All three need a hardcoded list of
78
- valid values, with everything absent from the list reported as broken. That is
79
- exactly how the 25 false findings happened: a hook-event list with 9 names when
80
- the spec has 31, calling working hooks dead at severity `error`.
78
+ It will not compare a Claude-native skill's frontmatter `name` to its directory
79
+ (portable Agent Skills under `.agents/skills` and Cursor `.cursor/skills` still
80
+ require that match), and it will not validate model ids or MCP tool names.
81
+ Those last two need a hardcoded list of valid values, with everything absent
82
+ from the list reported as broken. That is exactly how the 25 false findings
83
+ happened: a hook-event list with 9 names when the spec has 31, calling working
84
+ hooks dead at severity `error`.
81
85
 
82
- A check here has to point at a spec line. If it cannot, it does not ship.
86
+ A spec-required check has to point at a published line. If it cannot, it does
87
+ not ship as an error. Size opinions and installer policy remain as `info`
88
+ heuristics and are labeled that way in the registry.
83
89
 
84
90
  ## Try it in 30 seconds
85
91
 
@@ -123,7 +129,7 @@ bunx @chimix/agentscan rules
123
129
  bunx @chimix/agentscan check ~/your-project --verbose
124
130
 
125
131
  # why one specific finding fired
126
- bunx @chimix/agentscan explain hook.missing-script:hook:PreToolUse:./x.js ~/your-project
132
+ bunx @chimix/agentscan explain claude.hook.missing-script:hook:PreToolUse:./x.js ~/your-project
127
133
  ```
128
134
 
129
135
  `check` never opens a socket and never writes to the tree it scans. Contributing
@@ -138,7 +144,7 @@ The Action runs from its own checkout, so it uses the ref you pin rather than
138
144
  whatever is on npm:
139
145
 
140
146
  ```yaml
141
- - uses: SimaAlexandru99/agentscan@v0
147
+ - uses: SimaAlexandru99/agentscan@v1
142
148
  with:
143
149
  fail-on: error
144
150
  ```
@@ -193,18 +199,18 @@ Flags for `check`:
193
199
  │ ⌒ │ 6 error · 2 warning · 11 info hidden (--verbose)
194
200
  └─────┘
195
201
 
196
- agentscan v0.7.0 — touchagency
202
+ agentscan v1.0.0 — touchagency
197
203
 
198
204
  Scanned: 46 deps · 108 skills · 1 mcp · 2 agents · packageManager=bun
199
205
 
200
- ERROR rule:hook.missing-script ×6
206
+ ERROR rule:claude.hook.missing-script ×6
201
207
  Hook points at a script that does not exist
202
208
  PostToolUse @ .claude/settings.json · .claude/hooks/notify-related-tests.js
203
209
  PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
204
210
  PreToolUse @ .claude/settings.json · .claude/hooks/protect-artists-json.js
205
211
  PreToolUse @ .claude/settings.json · .claude/hooks/protect-env.js
206
212
 
207
- WARN rule:agent.invalid-name ×2
213
+ WARN rule:claude.agent.invalid-name ×2
208
214
  2 findings
209
215
  .claude/agents/marketing-seo-specialist.md
210
216
  .claude/agents/testing-accessibility-auditor.md
@@ -225,7 +231,7 @@ JSON shape (abridged):
225
231
 
226
232
  ```json
227
233
  {
228
- "version": "0.7.0",
234
+ "version": "1.0.0",
229
235
  "root": "/path/to/project",
230
236
  "factsSummary": {
231
237
  "packageManager": "bun",
@@ -235,8 +241,8 @@ JSON shape (abridged):
235
241
  },
236
242
  "findings": [
237
243
  {
238
- "id": "hook.missing-script:hook:PreToolUse:.claude/hooks/protect-env.js",
239
- "ruleId": "hook.missing-script",
244
+ "id": "claude.hook.missing-script:hook:PreToolUse:.claude/hooks/protect-env.js",
245
+ "ruleId": "claude.hook.missing-script",
240
246
  "action": "warn",
241
247
  "severity": "error",
242
248
  "subject": "hook:PreToolUse:.claude/hooks/protect-env.js",
@@ -255,11 +261,14 @@ Same tree → same sorted findings, with stable unique `id`s.
255
261
 
256
262
  Adds `~/.claude/skills` and `~/.codex/skills` to the structural checks: a
257
263
  `SKILL.md` that is malformed is malformed wherever it lives, and those findings
258
- carry a `source: global` evidence entry so you can tell them apart.
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).
259
266
 
260
267
  Lockfile checks stay project-scoped — a project lockfile cannot pin a skill that
261
268
  lives in your home directory, so reporting one as "not in the lockfile" could
262
- only ever be wrong.
269
+ only ever be wrong. In a monorepo, each `skills-lock.json` governs only the
270
+ skills under its directory (nearest lock wins). A skill with no ancestor
271
+ lockfile is not compared to a distant lock.
263
272
 
264
273
  `includeGlobal: true` in `.agentscanrc.json` does the same thing without the
265
274
  flag.
@@ -305,7 +314,7 @@ only says how many points of unspecified trouble is too much.
305
314
  ## CI
306
315
 
307
316
  ```yaml
308
- - uses: SimaAlexandru99/agentscan@v0
317
+ - uses: SimaAlexandru99/agentscan@v1
309
318
  with:
310
319
  fail-on: error # never | warning | error
311
320
  output: human # human | json | prompt
@@ -333,8 +342,8 @@ Optional `.agentscanrc.json` (create with `agentscan init`):
333
342
 
334
343
  ```json
335
344
  {
336
- "skillPaths": [".agents/skills", ".claude/skills", "skills", ".cursor/skills"],
337
- "mcpPaths": [".mcp.json", ".claude/mcp.json", "mcp.json"],
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"],
338
347
  "policyFiles": ["AGENTS.md", "CLAUDE.md"],
339
348
  "ignoreSkills": [],
340
349
  "ignoreRules": [],
@@ -401,7 +410,7 @@ each finding, and `--json` carries it as `.findings[].id`, so the value you copy
401
410
  out of the report is the value you paste:
402
411
 
403
412
  ```json
404
- { "ignoreFindings": ["hook.missing-script:hook:PreToolUse:./bin/wrapper"] }
413
+ { "ignoreFindings": ["claude.hook.missing-script:hook:PreToolUse:./bin/wrapper"] }
405
414
  ```
406
415
 
407
416
  ## What it checks
@@ -409,41 +418,92 @@ out of the report is the value you paste:
409
418
  Every check lives in `src/checks/` and runs on every `check`. `agentscan rules`
410
419
  lists all of them with their ids; `agentscan explain <id>` details any finding.
411
420
 
412
- Most validate one discovered item against its own file on disk. The four
413
- `budget.*` entries at the bottom judge aggregate size instead, as does
414
- `skill.description-budget` — all five are **info**, all five are sourced to
415
- `docs/spec/thresholds.md`, and all five are retuned through `thresholds` in
416
- `.agentscanrc.json` rather than switched off.
421
+ Most validate one discovered item against its own file on disk. Budget and
422
+ hygiene checks are **info** (hidden unless `--verbose`). Each registry entry
423
+ carries `provenance`: spec-required, vendor-recommendation, security,
424
+ internal-consistency, or heuristic. `agentscan rules` lists them all.
417
425
 
418
426
  | id | Severity | Catches |
419
427
  |----|----------|---------|
420
428
  | `config.unreadable` | error | A config file that is not valid JSON, so whatever it declares is silently not in effect |
421
429
  | `scan.truncated` | info | A file past the scan cap, so the checks that read its body saw only a prefix — about this tool's reach, not about your project |
422
- | `hook.missing-script` | error | A registered hook whose script does not exist — it never runs. Reads all four in-tree registration sites (see below) |
423
- | `hook.unknown-event` | error | A hook registered under an event name that is never dispatched |
424
- | `agent.missing-frontmatter` | error | An agent definition with no `---` block |
425
- | `agent.missing-description` | error | Agent frontmatter has no `description` |
426
- | `agent.missing-name` | error | Agent frontmatter has no `name` |
427
- | `agent.invalid-name` | warning | Agent name is not lowercase letters, numbers, and hyphens |
428
- | `agent.duplicate-name` | error | Multiple agent files declare the same name |
429
- | `mcp.no-launch` | error | An MCP server with neither `command` nor `url` — unlaunchable by schema |
430
+ | `claude.hook.missing-script` | error | A registered Claude command hook whose script does not exist — it never runs |
431
+ | `claude.hook.unknown-event` | error | A Claude hook registered under an event name that is never dispatched |
432
+ | `vscode.hook.missing-script` | error | A VS Code command hook whose script does not exist |
433
+ | `vscode.hook.unknown-event` | error | A VS Code hook registered under an event name that is never dispatched |
434
+ | `claude.agent.missing-frontmatter` | error | A Claude agent definition with no `---` block |
435
+ | `claude.agent.missing-description` | error | Claude agent frontmatter has no `description` |
436
+ | `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 |
438
+ | `claude.agent.duplicate-name` | error | Multiple Claude agent files declare the same name |
439
+ | `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 |
441
+ | `vscode.mcp.no-launch` | error | A VS Code MCP server (`servers`) with neither `command` nor `url` |
442
+ | `cursor.mcp.no-launch` | error | A Cursor MCP server with neither `command` nor `url` |
443
+ | `antigravity.mcp.no-launch` | error | An Antigravity MCP server with neither `command` nor `serverUrl` |
444
+ | `codex.mcp.no-launch` | error | A Codex `[mcp_servers.*]` entry with neither `command` nor `url` |
445
+ | `gemini.mcp.no-launch` | error | A Gemini MCP server with neither `command`, `url`, nor `httpUrl` |
446
+ | `opencode.mcp.no-launch` | error | An OpenCode MCP server with neither `command` nor `url` |
447
+ | `opencode.mcp.missing-type` | error | An OpenCode MCP server missing `type: local` or `type: remote` |
448
+ | `opencode.mcp.local-without-command` | error | An OpenCode `type: local` server with no `command` |
449
+ | `opencode.mcp.remote-without-url` | error | An OpenCode `type: remote` server with no `url` |
450
+ | `opencode.mcp.invalid-launch-for-type` | error | An OpenCode server whose launch field does not match its `type` |
451
+ | `continue.mcp.no-launch` | error | A Continue MCP server with neither `command`, `url`, nor `uses` |
452
+ | `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 |
430
454
  | `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
431
- | `mcp.url-without-type` | error | A remote MCP server with a `url` but no `type` — read as stdio, misconfigured by schema, and skipped |
432
- | `mcp.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
433
- | `mcp.literal-env` | warning | Long literal `env` values that should be `${VAR}` |
455
+ | `security.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
456
+ | `mcp.literal-env` | warning | Secret-named `env` values that are literals instead of interpolation |
434
457
  | `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
435
- | `skill.missing-frontmatter` | warning | `SKILL.md` with no `---` block |
458
+ | `claude.skill.missing-frontmatter` | warning | Claude `SKILL.md` with no `---` block |
459
+ | `claude.skill.missing-description` | info | Claude frontmatter has no `description` (Recommended) |
460
+ | `agent-skills.skill.missing-frontmatter` | error | Portable skill `SKILL.md` has no YAML frontmatter |
461
+ | `agent-skills.skill.missing-name` | error | Portable skill frontmatter has no required `name` |
462
+ | `agent-skills.skill.missing-description` | error | Portable skill frontmatter has no required `description` |
463
+ | `agent-skills.skill.invalid-name` | error | Portable skill name is not `[a-z0-9]` + hyphens |
464
+ | `agent-skills.skill.name-does-not-match-directory` | error | Portable skill `name` does not match its directory |
465
+ | `agent-skills.skill.name-too-long` | error | Portable skill name exceeds 64 characters |
466
+ | `agent-skills.skill.description-too-long` | error | Portable skill description exceeds 1024 characters |
436
467
  | `skill.broken-reference` | warning | The body points at a bundled file that does not exist |
437
- | `skill.duplicate-description` | warning | Two or more skills carry an identical description, so routing between them is arbitrary |
438
- | `skill.missing-description` | info | Frontmatter has no `description`, so Claude will not load the skill on its own |
468
+ | `skill.duplicate-description` | info | Two or more skills carry an identical description (heuristic) |
439
469
  | `skill.locked-not-installed` | warning | `skills-lock.json` pins a skill that is not on disk |
440
- | `skill.not-in-lock` | info | A skill on disk that the lockfile does not track — local and unpinned |
441
- | `skill.description-budget` | info | Skill names + descriptions exceed the startup character budget |
470
+ | `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) |
442
472
  | `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
443
- | `budget.agents-md` | info | `AGENTS.md` past the point where added lines stop helping (>150) |
444
- | `budget.claude-md` | info | `CLAUDE.md` past the instruction count a model reliably follows (>200) |
445
- | `budget.agents` | info | More agent definitions than a focused set (>8 in `.claude/agents`) |
446
- | `budget.mcp` | info | More MCP servers than the point where tool selection degrades (>5) |
473
+ | `budget.agents-md` | info | `AGENTS.md` past a secondary 150-line hint (heuristic; not an AGENTS.md requirement) |
474
+ | `budget.claude-md` | info | `CLAUDE.md` past the official “target under 200 lines” recommendation |
475
+ | `budget.agents` | info | More agent definitions than a focused set (>8) — heuristic proxy |
476
+ | `budget.mcp` | info | More MCP servers than a tool-selection hint (>5) — heuristic proxy |
477
+ | `codex.budget.instructions` | info | Codex root→cwd `AGENTS.md` chain exceeds the default 32 KiB |
478
+ | `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.
505
+
506
+ Old 0.7 rule ids still work in `ignoreRules` and `explain` (for example `hook.missing-script` aliases `claude.hook.missing-script`).
447
507
 
448
508
  Agent definitions are checked for structure only. An agent's frontmatter `name`
449
509
  is not compared to the filename; it must simply be a valid lowercase identifier
@@ -461,8 +521,8 @@ illustration, not a pointer.
461
521
 
462
522
  ### Where it looks for hooks
463
523
 
464
- The hooks reference lists seven places a hook can be registered. agentscan reads
465
- the four that live inside the scanned project:
524
+ 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:
466
526
 
467
527
  | Registered in | Script paths resolve against |
468
528
  |---|---|
@@ -470,14 +530,15 @@ the four that live inside the scanned project:
470
530
  | A plugin's `hooks/hooks.json` (a directory with `.claude-plugin/plugin.json`, or the scan root itself) | the plugin root, `${CLAUDE_PLUGIN_ROOT}` |
471
531
  | `SKILL.md` frontmatter | the skill's own directory, then the project root |
472
532
  | `.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 |
473
534
 
474
535
  `${CLAUDE_PLUGIN_ROOT}` is expanded **only** for a hook that came from a plugin.
475
536
  In a settings file it names nothing, so the path is skipped rather than guessed
476
537
  at — measured across 17 installed plugins, 31 of 33 plugin hook commands use it
477
- and none of them are settings hooks. `hook.unknown-event` covers all four sites
538
+ and none of them are settings hooks. `claude.hook.unknown-event` covers all four sites
478
539
  at no extra cost. Sources and measurements: [docs/spec/hook-sources.md](docs/spec/hook-sources.md).
479
540
 
480
- `hook.missing-script` and `mcp.command-missing` are deliberately conservative.
541
+ `claude.hook.missing-script` and `mcp.command-missing` are deliberately conservative.
481
542
  A value is only resolved when it is path-like and the answer is certain; shell
482
543
  programs (`a && b`, `$(...)`, pipes), bare PATH binaries (`npx`, `uvx`), and
483
544
  unresolved env vars are skipped. A false "broken" on a working `npx` server
@@ -486,15 +547,17 @@ Only `$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather
486
547
  than guessed at. Schema checks judge misconfiguration, not whether a correctly
487
548
  shaped entry will start at runtime.
488
549
 
489
- Checks are written against the published specs, not against what happens to
490
- appear in real projects. Two that were written the other way round shipped as
491
- false positives — a nine-name hook-event list where the spec has 31, and a
492
- `name` must equal the directory rule that the spec explicitly contradicts
493
- (`name` is optional and defaults to the directory). Both are gone.
550
+ Spec-required checks are written against published pages, not against what
551
+ happens to appear in real projects. Two that were written the other way round
552
+ shipped as false positives — a nine-name hook-event list where the spec has 31,
553
+ and a `name` must equal the directory rule that Claude's spec explicitly
554
+ 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`.
494
557
 
495
- Every assumption a check encodes is recorded in **[docs/spec/](docs/spec/)**
496
- with its source URL, the date it was read, and an honest confidence rating.
497
- When adding a check, add its spec line there first.
558
+ Assumptions are recorded in **[docs/spec/](docs/spec/)** with source URL, read
559
+ date, and provenance. When adding a spec-required check, add its spec line there
560
+ first.
498
561
 
499
562
  ## Known limits
500
563
 
@@ -521,6 +584,21 @@ When adding a check, add its spec line there first.
521
584
  that read it see all of it. Reporting the cap as `config.unreadable` at
522
585
  severity error is a defect this tool shipped and [plan 019](plans/019-findings-say-true-things.md) fixed.
523
586
 
587
+ ## Releases
588
+
589
+ Notes for every published version are in [CHANGELOG.md](CHANGELOG.md).
590
+ GitHub already has pages for [v0.1.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.1.0)
591
+ and [v0.4.0](https://github.com/SimaAlexandru99/agentscan/releases/tag/v0.4.0).
592
+
593
+ **1.0.0** (30 August 2026) is the first stable release: 59 checks, 345 tests,
594
+ still offline on `check`. It adds portable Agent Skills (including Cursor and
595
+ Codex skill trees), MCP profiles for VS Code / Cursor / Codex / Antigravity /
596
+ Gemini / OpenCode / Continue, nested `AGENTS.md`, walk-up agents, VS Code
597
+ hooks, and platform-aware launch checks — a `windows:` script is not
598
+ POSIX-joined when the scanner runs on Linux. Old 0.7 rule ids still work in
599
+ `ignoreRules` and `explain`. 0.9.0 was planned as a separate train and shipped
600
+ inside this release.
601
+
524
602
  ## Development
525
603
 
526
604
  ```bash
@@ -545,6 +623,8 @@ Before publishing a release:
545
623
  2. Run `bun run build`, then verify `node dist/cli.js --version` prints it.
546
624
  3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`.
547
625
  4. Refresh the test-count badge and version examples above from that release run.
626
+ 5. Add a section to [CHANGELOG.md](CHANGELOG.md) for the version, then publish
627
+ the GitHub release from that text.
548
628
 
549
629
  ## Design docs
550
630