@chimix/agentscan 0.4.0 → 0.6.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.
- package/README.md +105 -53
- package/dist/cli.js +1081 -782
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="https://img.shields.io/badge/checks-
|
|
9
|
-
<img src="https://img.shields.io/badge/tests-
|
|
8
|
+
<img src="https://img.shields.io/badge/checks-27-111111?style=flat-square" alt="27 checks">
|
|
9
|
+
<img src="https://img.shields.io/badge/tests-235%20passing-111111?style=flat-square" alt="235 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">
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<strong>
|
|
18
|
+
<strong>27 checks · 4.2k lines · 0 network calls · every check sourced to a published spec line</strong><br>
|
|
19
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>
|
|
20
20
|
</p>
|
|
21
21
|
|
|
@@ -47,12 +47,11 @@ think you have has silently not existed for six weeks.
|
|
|
47
47
|
```console
|
|
48
48
|
$ agentscan check
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
evidence: hook PreToolUse @ …/.claude/settings.json
|
|
50
|
+
ERROR rule:hook.missing-script
|
|
51
|
+
PreToolUse hook points at a script that does not exist: .claude/hooks/guard-destructive-bash.js
|
|
52
|
+
PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
|
|
54
53
|
|
|
55
|
-
Summary:
|
|
54
|
+
Summary: 1 error · score 90/100
|
|
56
55
|
```
|
|
57
56
|
|
|
58
57
|
That is the whole category: **a config file asserting something that is not true
|
|
@@ -65,7 +64,7 @@ No AI, no network, no heuristics. Read the config, read the disk, compare:
|
|
|
65
64
|
```
|
|
66
65
|
1. Discover .claude/ .agents/ .mcp.json AGENTS.md skills-lock.json
|
|
67
66
|
2. Extract immutable facts — never re-read during checking
|
|
68
|
-
3. Check
|
|
67
|
+
3. Check 27 checks, each against one published spec line
|
|
69
68
|
4. Report text · --json · --output prompt (handoff for a fixing agent)
|
|
70
69
|
```
|
|
71
70
|
|
|
@@ -84,8 +83,16 @@ A check here has to point at a spec line. If it cannot, it does not ship.
|
|
|
84
83
|
|
|
85
84
|
## Try it in 30 seconds
|
|
86
85
|
|
|
87
|
-
Run
|
|
88
|
-
|
|
86
|
+
No project handy? Run the built-in demo — it builds a throwaway fixture with the
|
|
87
|
+
killer case (a `PreToolUse` hook pointing at a missing script), prints the human
|
|
88
|
+
report, and deletes the fixture:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx @chimix/agentscan@latest demo
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Against a real project, it reads that tree, writes nothing, and never leaves
|
|
95
|
+
your machine:
|
|
89
96
|
|
|
90
97
|
```bash
|
|
91
98
|
cd ~/your-project
|
|
@@ -104,8 +111,9 @@ bun add -d @chimix/agentscan # then: bunx agentscan check
|
|
|
104
111
|
```
|
|
105
112
|
|
|
106
113
|
Point it at a project that actually has agent config — a `.claude/` directory,
|
|
107
|
-
an `.mcp.json`, an `AGENTS.md`.
|
|
108
|
-
|
|
114
|
+
an `.mcp.json`, an `AGENTS.md`. A directory with agent config and no
|
|
115
|
+
`package.json` is still scannable. On a directory with neither it exits with a
|
|
116
|
+
clear error.
|
|
109
117
|
|
|
110
118
|
```bash
|
|
111
119
|
# what it could possibly report, before you run it
|
|
@@ -148,10 +156,11 @@ bunx agentscan check # text report (cwd)
|
|
|
148
156
|
bunx agentscan check ./my-app # explicit root
|
|
149
157
|
bunx agentscan check --json # machine-readable
|
|
150
158
|
bunx agentscan check --quiet # summary only
|
|
151
|
-
bunx agentscan check --verbose # include KEEP and
|
|
159
|
+
bunx agentscan check --verbose # include KEEP, info findings, and each finding's id
|
|
152
160
|
bunx agentscan check --fail-on warning
|
|
153
161
|
bunx agentscan check --global # also scan global skill dirs
|
|
154
162
|
|
|
163
|
+
bunx agentscan demo # one-shot fixture (no project required)
|
|
155
164
|
bunx agentscan explain <findingId> # detail one finding
|
|
156
165
|
bunx agentscan rules # every check + rule id that can fire
|
|
157
166
|
bunx agentscan init # write .agentscanrc.json
|
|
@@ -166,7 +175,7 @@ Flags for `check`:
|
|
|
166
175
|
| `--copy` | Also copy the report to the system clipboard |
|
|
167
176
|
| `--no-color` | Never colour, even on a terminal (`NO_COLOR=1` does the same) |
|
|
168
177
|
| `--quiet` | Summary line only |
|
|
169
|
-
| `--verbose` | Show KEEP + info-severity findings |
|
|
178
|
+
| `--verbose` | Show KEEP + info-severity findings, and print each finding's id |
|
|
170
179
|
| `--fail-on <level>` | `never` (default) · `warning` · `error` |
|
|
171
180
|
| `--fail-under <0-100>` | Fail when the score drops below this floor |
|
|
172
181
|
| `--global` | Also scan `~/.claude/skills` and `~/.codex/skills` (see below) |
|
|
@@ -177,23 +186,28 @@ Flags for `check`:
|
|
|
177
186
|
## Sample output
|
|
178
187
|
|
|
179
188
|
```text
|
|
180
|
-
┌─────┐
|
|
181
|
-
│ ✕ ✕ │
|
|
182
|
-
│ ⌒ │ 6
|
|
189
|
+
┌─────┐ 34/100 broken · touchagency
|
|
190
|
+
│ ✕ ✕ │ █████████░░░░░░░░░░░░░░░░
|
|
191
|
+
│ ⌒ │ 6 error · 2 warning · 11 info hidden (--verbose)
|
|
183
192
|
└─────┘
|
|
184
193
|
|
|
185
|
-
agentscan v0.
|
|
194
|
+
agentscan v0.6.0 — touchagency
|
|
186
195
|
|
|
187
|
-
Scanned: 46 deps ·
|
|
196
|
+
Scanned: 46 deps · 108 skills · 1 mcp · 2 agents · packageManager=bun
|
|
188
197
|
|
|
189
|
-
|
|
190
|
-
|
|
198
|
+
ERROR rule:hook.missing-script ×6
|
|
199
|
+
Hook points at a script that does not exist
|
|
191
200
|
PostToolUse @ .claude/settings.json · .claude/hooks/notify-related-tests.js
|
|
192
201
|
PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
|
|
193
202
|
PreToolUse @ .claude/settings.json · .claude/hooks/protect-artists-json.js
|
|
194
203
|
PreToolUse @ .claude/settings.json · .claude/hooks/protect-env.js
|
|
195
204
|
|
|
196
|
-
|
|
205
|
+
WARN rule:agent.invalid-name ×2
|
|
206
|
+
2 findings
|
|
207
|
+
.claude/agents/marketing-seo-specialist.md
|
|
208
|
+
.claude/agents/testing-accessibility-auditor.md
|
|
209
|
+
|
|
210
|
+
Summary: 6 error · 2 warning · 11 info hidden (--verbose) · score 34/100 broken
|
|
197
211
|
```
|
|
198
212
|
|
|
199
213
|
On a terminal the header is coloured green, yellow or red and the face tracks
|
|
@@ -201,14 +215,15 @@ the score. Redirect or pipe it and you get exactly the text above, with no
|
|
|
201
215
|
escape sequences — the header box is dropped too, so `--json`, `--output
|
|
202
216
|
prompt`, CI logs and `--copy` are unchanged by any of this.
|
|
203
217
|
|
|
204
|
-
The `
|
|
205
|
-
|
|
218
|
+
The `Scanned:` line is orientation only — which project, how big. The summary
|
|
219
|
+
lists severities (error / warning / info); a clean project prints
|
|
220
|
+
`Summary: no findings`. Score and `--fail-on` still use severity under the hood.
|
|
206
221
|
|
|
207
222
|
JSON shape (abridged):
|
|
208
223
|
|
|
209
224
|
```json
|
|
210
225
|
{
|
|
211
|
-
"version": "0.
|
|
226
|
+
"version": "0.6.0",
|
|
212
227
|
"root": "/path/to/project",
|
|
213
228
|
"factsSummary": {
|
|
214
229
|
"packageManager": "bun",
|
|
@@ -379,8 +394,9 @@ This is the only place agentscan starts a subprocess, and it runs only under
|
|
|
379
394
|
|
|
380
395
|
`ignoreRules` disables a check project-wide, which is too blunt when a single
|
|
381
396
|
hook uses an exotic launcher. `ignoreFindings` takes exact finding ids — the
|
|
382
|
-
same strings `agentscan explain` accepts
|
|
383
|
-
|
|
397
|
+
same strings `agentscan explain` accepts. `check --verbose` prints the id under
|
|
398
|
+
each finding, and `--json` carries it as `.findings[].id`, so the value you copy
|
|
399
|
+
out of the report is the value you paste:
|
|
384
400
|
|
|
385
401
|
```json
|
|
386
402
|
{ "ignoreFindings": ["hook.missing-script:hook:PreToolUse:./bin/wrapper"] }
|
|
@@ -391,21 +407,26 @@ reading the report is the value you paste:
|
|
|
391
407
|
Every check lives in `src/checks/` and runs on every `check`. `agentscan rules`
|
|
392
408
|
lists all of them with their ids; `agentscan explain <id>` details any finding.
|
|
393
409
|
|
|
394
|
-
Most validate one discovered item against its own file on disk. The
|
|
395
|
-
`budget.*`
|
|
396
|
-
|
|
397
|
-
are retuned through `thresholds` in
|
|
398
|
-
off.
|
|
410
|
+
Most validate one discovered item against its own file on disk. The four
|
|
411
|
+
`budget.*` entries at the bottom judge aggregate size instead, as does
|
|
412
|
+
`skill.description-budget` — all five are **info**, all five are sourced to
|
|
413
|
+
`docs/spec/thresholds.md`, and all five are retuned through `thresholds` in
|
|
414
|
+
`.agentscanrc.json` rather than switched off.
|
|
399
415
|
|
|
400
416
|
| id | Severity | Catches |
|
|
401
417
|
|----|----------|---------|
|
|
402
418
|
| `config.unreadable` | error | A config file that is not valid JSON, so whatever it declares is silently not in effect |
|
|
419
|
+
| `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 |
|
|
403
420
|
| `hook.missing-script` | error | A registered hook whose script does not exist — it never runs |
|
|
404
421
|
| `hook.unknown-event` | error | A hook registered under an event name that is never dispatched |
|
|
405
|
-
| `agent.missing-frontmatter` |
|
|
406
|
-
| `agent.missing-description` |
|
|
407
|
-
| `
|
|
408
|
-
| `
|
|
422
|
+
| `agent.missing-frontmatter` | error | An agent definition with no `---` block |
|
|
423
|
+
| `agent.missing-description` | error | Agent frontmatter has no `description` |
|
|
424
|
+
| `agent.missing-name` | error | Agent frontmatter has no `name` |
|
|
425
|
+
| `agent.invalid-name` | warning | Agent name is not lowercase letters, numbers, and hyphens |
|
|
426
|
+
| `agent.duplicate-name` | error | Multiple agent files declare the same name |
|
|
427
|
+
| `mcp.no-launch` | error | An MCP server with neither `command` nor `url` — unlaunchable by schema |
|
|
428
|
+
| `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
|
|
429
|
+
| `mcp.url-without-type` | error | A remote MCP server with a `url` but no `type` — read as stdio, misconfigured by schema, and skipped |
|
|
409
430
|
| `mcp.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
|
|
410
431
|
| `mcp.literal-env` | warning | Long literal `env` values that should be `${VAR}` |
|
|
411
432
|
| `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
|
|
@@ -417,11 +438,14 @@ off.
|
|
|
417
438
|
| `skill.not-in-lock` | info | A skill on disk that the lockfile does not track — local and unpinned |
|
|
418
439
|
| `skill.description-budget` | info | Skill names + descriptions exceed the startup character budget |
|
|
419
440
|
| `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
|
|
441
|
+
| `budget.agents-md` | info | `AGENTS.md` past the point where added lines stop helping (>150) |
|
|
442
|
+
| `budget.claude-md` | info | `CLAUDE.md` past the instruction count a model reliably follows (>200) |
|
|
443
|
+
| `budget.agents` | info | More agent definitions than a focused set (>8 in `.claude/agents`) |
|
|
444
|
+
| `budget.mcp` | info | More MCP servers than the point where tool selection degrades (>5) |
|
|
420
445
|
|
|
421
446
|
Agent definitions are checked for structure only. An agent's frontmatter `name`
|
|
422
|
-
is
|
|
423
|
-
|
|
424
|
-
and a test guards against re-adding that check. Model identifiers and tool names
|
|
447
|
+
is not compared to the filename; it must simply be a valid lowercase identifier
|
|
448
|
+
and unique. Model identifiers and tool names
|
|
425
449
|
are likewise not validated: both would need a hardcoded list, which is the shape
|
|
426
450
|
that already shipped a false error. See [plans/003](plans/003-validate-agent-definitions.md).
|
|
427
451
|
|
|
@@ -433,13 +457,14 @@ references measured across 17 projects, 1645 resolved skill-relative and 12 only
|
|
|
433
457
|
at the root. Fenced code blocks are stripped first: a path in an example is
|
|
434
458
|
illustration, not a pointer.
|
|
435
459
|
|
|
436
|
-
`hook.missing-script`
|
|
437
|
-
|
|
438
|
-
(`a && b`, `$(...)`, pipes)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
`$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather
|
|
442
|
-
guessed at.
|
|
460
|
+
`hook.missing-script` and `mcp.command-missing` are deliberately conservative.
|
|
461
|
+
A value is only resolved when it is path-like and the answer is certain; shell
|
|
462
|
+
programs (`a && b`, `$(...)`, pipes), bare PATH binaries (`npx`, `uvx`), and
|
|
463
|
+
unresolved env vars are skipped. A false "broken" on a working `npx` server
|
|
464
|
+
would be worse than silence. `node -e "<code>"` is never treated as a path.
|
|
465
|
+
Only `$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather
|
|
466
|
+
than guessed at. Schema checks judge misconfiguration, not whether a correctly
|
|
467
|
+
shaped entry will start at runtime.
|
|
443
468
|
|
|
444
469
|
Checks are written against the published specs, not against what happens to
|
|
445
470
|
appear in real projects. Two that were written the other way round shipped as
|
|
@@ -461,8 +486,15 @@ When adding a check, add its spec line there first.
|
|
|
461
486
|
the `computedHash` in `skills-lock.json` is the obvious `refresh` source; the
|
|
462
487
|
hash algorithm used by the installing tool is not documented here, and it is
|
|
463
488
|
not reproducible from `SKILL.md` bytes alone, so it is not implemented.
|
|
464
|
-
- **
|
|
465
|
-
|
|
489
|
+
- **Runtime split.** Source development and the composite Action use Bun; the
|
|
490
|
+
published `dist/cli.js` runs on Node 20.11+ or Bun. The source entrypoint uses
|
|
491
|
+
Bun's `import.meta.dir` / `import.meta.main` and is not the Node entrypoint.
|
|
492
|
+
- **Bounded reads.** A `SKILL.md` is read to 64 KB and a policy file to 100 KB,
|
|
493
|
+
so `skill.broken-reference` and `policyLines` see only that much of a larger
|
|
494
|
+
file. This is reported — `scan.truncated`, at `info` — rather than left
|
|
495
|
+
silent, and it says nothing about the file being valid: it is, and the tools
|
|
496
|
+
that read it see all of it. Reporting the cap as `config.unreadable` at
|
|
497
|
+
severity error is a defect this tool shipped and [plan 019](plans/019-findings-say-true-things.md) fixed.
|
|
466
498
|
|
|
467
499
|
## Development
|
|
468
500
|
|
|
@@ -480,14 +512,34 @@ bun run src/cli.ts check tests/fixtures/lock-drift
|
|
|
480
512
|
| `test` | `bun test` |
|
|
481
513
|
| `check` | `bun run src/cli.ts check` |
|
|
482
514
|
|
|
515
|
+
### Release checklist
|
|
516
|
+
|
|
517
|
+
Before publishing a release:
|
|
518
|
+
|
|
519
|
+
1. Confirm `package.json` and `src/version.ts` contain the same version.
|
|
520
|
+
2. Run `bun run build`, then verify `node dist/cli.js --version` prints it.
|
|
521
|
+
3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`.
|
|
522
|
+
4. Refresh the test-count badge and version examples above from that release run.
|
|
523
|
+
|
|
483
524
|
## Design docs
|
|
484
525
|
|
|
485
526
|
**This README is the source of truth for current behavior.** The documents under
|
|
486
527
|
`docs/superpowers/specs/` are the original design and are **superseded** —
|
|
487
|
-
several decisions changed while building: Bun-only runtime
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
(`plans/010`). Read
|
|
528
|
+
several decisions changed while building: the original Bun-only runtime claim was
|
|
529
|
+
superseded by a Node-compatible published bundle, the dep-to-skill map and its
|
|
530
|
+
"orphan" heuristic dropped in favour of `skills-lock.json`, budget checks added,
|
|
531
|
+
structural checks added, and the YAML rule engine deleted (`plans/010`). Read
|
|
532
|
+
them as history, not as behavior.
|
|
533
|
+
|
|
534
|
+
## Skill
|
|
535
|
+
|
|
536
|
+
A Cursor / Agent Skills file lives at [`skills/agentscan/SKILL.md`](skills/agentscan/SKILL.md). Copy the folder so the agent runs the audit before it edits hooks or claims a guard is in place:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
cp -R skills/agentscan .cursor/skills/agentscan
|
|
540
|
+
# or: .agents/skills/agentscan
|
|
541
|
+
# or: .claude/skills/agentscan
|
|
542
|
+
```
|
|
491
543
|
|
|
492
544
|
## License
|
|
493
545
|
|