@chimix/agentscan 0.3.0 → 0.5.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 (3) hide show
  1. package/README.md +87 -40
  2. package/dist/cli.js +1161 -776
  3. 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-23-111111?style=flat-square" alt="23 checks">
9
- <img src="https://img.shields.io/badge/tests-183%20passing-111111?style=flat-square" alt="183 tests">
8
+ <img src="https://img.shields.io/badge/checks-26-111111?style=flat-square" alt="26 checks">
9
+ <img src="https://img.shields.io/badge/tests-226%20passing-111111?style=flat-square" alt="226 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>23 checks &middot; 3.4k lines &middot; 0 network calls &middot; every check sourced to a published spec line</strong><br>
18
+ <strong>26 checks &middot; 4.1k lines &middot; 0 network calls &middot; 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
- WARN hook:PreToolUse:.claude/hooks/guard-destructive-bash.js
51
- rule:hook.missing-script
52
- PreToolUse hook points at a script that does not exist
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: 6 warn · 4 info hidden (--verbose) · score 40/100
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 23 checks, each against one published spec line
67
+ 3. Check 26 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 it against any project. It reads that project, writes nothing, and never
88
- leaves your machine:
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`. On a project with none of those it will
108
- correctly find nothing and say so.
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
@@ -152,6 +160,7 @@ bunx agentscan check --verbose # include KEEP and info-severity findings
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
@@ -164,6 +173,7 @@ Flags for `check`:
164
173
  | `--json` | JSON report (alias for `--output json`) |
165
174
  | `--output <format>` | `human` (default) · `json` · `prompt` |
166
175
  | `--copy` | Also copy the report to the system clipboard |
176
+ | `--no-color` | Never colour, even on a terminal (`NO_COLOR=1` does the same) |
167
177
  | `--quiet` | Summary line only |
168
178
  | `--verbose` | Show KEEP + info-severity findings |
169
179
  | `--fail-on <level>` | `never` (default) · `warning` · `error` |
@@ -176,28 +186,39 @@ Flags for `check`:
176
186
  ## Sample output
177
187
 
178
188
  ```text
179
- agentscan v0.3.0 — touchagency
189
+ ┌─────┐ 40/100 broken · touchagency
190
+ │ ✕ ✕ │ ██████████░░░░░░░░░░░░░░░
191
+ │ ⌒ │ 6 error · 4 info hidden (--verbose)
192
+ └─────┘
193
+
194
+ agentscan v0.5.0 — touchagency
180
195
 
181
196
  Scanned: 46 deps · 54 skills · 1 mcp · 2 agents · packageManager=bun
182
197
 
183
- WARN rule:hook.missing-script ×6
184
- PreToolUse hook points at a script that does not exist
198
+ ERROR rule:hook.missing-script ×6
199
+ Hook points at a script that does not exist
185
200
  PostToolUse @ .claude/settings.json · .claude/hooks/notify-related-tests.js
186
201
  PreToolUse @ .claude/settings.json · .claude/hooks/guard-destructive-bash.js
187
202
  PreToolUse @ .claude/settings.json · .claude/hooks/protect-artists-json.js
188
203
  PreToolUse @ .claude/settings.json · .claude/hooks/protect-env.js
189
204
 
190
- Summary: 6 warn · 4 info hidden (--verbose) · score 40/100 broken
205
+ Summary: 6 error · 4 info hidden (--verbose) · score 40/100 broken
191
206
  ```
192
207
 
193
- The `Stack:` line is orientation only — which project, how big. The summary lists
194
- only actions that actually occurred; a clean project prints `Summary: no findings`.
208
+ On a terminal the header is coloured green, yellow or red and the face tracks
209
+ the score. Redirect or pipe it and you get exactly the text above, with no
210
+ escape sequences — the header box is dropped too, so `--json`, `--output
211
+ prompt`, CI logs and `--copy` are unchanged by any of this.
212
+
213
+ The `Scanned:` line is orientation only — which project, how big. The summary
214
+ lists severities (error / warning / info); a clean project prints
215
+ `Summary: no findings`. Score and `--fail-on` still use severity under the hood.
195
216
 
196
217
  JSON shape (abridged):
197
218
 
198
219
  ```json
199
220
  {
200
- "version": "0.1.0",
221
+ "version": "0.5.0",
201
222
  "root": "/path/to/project",
202
223
  "factsSummary": {
203
224
  "packageManager": "bun",
@@ -381,7 +402,7 @@ Every check lives in `src/checks/` and runs on every `check`. `agentscan rules`
381
402
  lists all of them with their ids; `agentscan explain <id>` details any finding.
382
403
 
383
404
  Most validate one discovered item against its own file on disk. The five
384
- `budget.*` / `policy.*` entries at the bottom judge aggregate size instead —
405
+ `budget.*` entries at the bottom judge aggregate size instead —
385
406
  they are all **info**, they are sourced to `docs/spec/thresholds.md`, and they
386
407
  are retuned through `thresholds` in `.agentscanrc.json` rather than switched
387
408
  off.
@@ -391,10 +412,14 @@ off.
391
412
  | `config.unreadable` | error | A config file that is not valid JSON, so whatever it declares is silently not in effect |
392
413
  | `hook.missing-script` | error | A registered hook whose script does not exist — it never runs |
393
414
  | `hook.unknown-event` | error | A hook registered under an event name that is never dispatched |
394
- | `agent.missing-frontmatter` | warning | An agent definition with no `---` block |
395
- | `agent.missing-description` | info | Agent frontmatter has no `description`, so it will not be dispatched on its own |
396
- | `mcp.no-launch` | error | An MCP server with neither `command` nor `url`; its tools are never available |
397
- | `mcp.url-without-type` | error | A remote MCP server with a `url` but no `type` — read as stdio, fails, and is skipped |
415
+ | `agent.missing-frontmatter` | error | An agent definition with no `---` block |
416
+ | `agent.missing-description` | error | Agent frontmatter has no `description` |
417
+ | `agent.missing-name` | error | Agent frontmatter has no `name` |
418
+ | `agent.invalid-name` | error | Agent name is not lowercase letters, numbers, and hyphens |
419
+ | `agent.duplicate-name` | error | Multiple agent files declare the same name |
420
+ | `mcp.no-launch` | error | An MCP server with neither `command` nor `url` — unlaunchable by schema |
421
+ | `mcp.command-missing` | error | An MCP `command` that is a path-like value whose file does not exist on disk |
422
+ | `mcp.url-without-type` | error | A remote MCP server with a `url` but no `type` — read as stdio, misconfigured by schema, and skipped |
398
423
  | `mcp.hardcoded-secret` | error | A token-shaped literal in MCP config (the value is never echoed back) |
399
424
  | `mcp.literal-env` | warning | Long literal `env` values that should be `${VAR}` |
400
425
  | `skill.missing-skill-md` | warning | A directory under a skill path with no `SKILL.md` |
@@ -408,9 +433,8 @@ off.
408
433
  | `skill.no-lockfile` | info | Skills present with no lockfile at all (only with `requireLock`) |
409
434
 
410
435
  Agent definitions are checked for structure only. An agent's frontmatter `name`
411
- is a display name — 16 of 34 real files declare one that differs from the
412
- filename, and nothing keys on the filename — so it is deliberately not compared,
413
- and a test guards against re-adding that check. Model identifiers and tool names
436
+ is not compared to the filename; it must simply be a valid lowercase identifier
437
+ and unique. Model identifiers and tool names
414
438
  are likewise not validated: both would need a hardcoded list, which is the shape
415
439
  that already shipped a false error. See [plans/003](plans/003-validate-agent-definitions.md).
416
440
 
@@ -422,13 +446,14 @@ references measured across 17 projects, 1645 resolved skill-relative and 12 only
422
446
  at the root. Fenced code blocks are stripped first: a path in an example is
423
447
  illustration, not a pointer.
424
448
 
425
- `hook.missing-script` is deliberately conservative. A command is only resolved
426
- when it is a single invocation with a path-like argument; shell programs
427
- (`a && b`, `$(...)`, pipes) are skipped, because a hook written as
428
- `[ ! -f x ] || node x` already handles the missing file and flagging it would be
429
- a false positive. `node -e "<code>"` is never treated as a path. Only
430
- `$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather than
431
- guessed at.
449
+ `hook.missing-script` and `mcp.command-missing` are deliberately conservative.
450
+ A value is only resolved when it is path-like and the answer is certain; shell
451
+ programs (`a && b`, `$(...)`, pipes), bare PATH binaries (`npx`, `uvx`), and
452
+ unresolved env vars are skipped. A false "broken" on a working `npx` server
453
+ would be worse than silence. `node -e "<code>"` is never treated as a path.
454
+ Only `$CLAUDE_PROJECT_DIR` is expanded — other variables are left alone rather
455
+ than guessed at. Schema checks judge misconfiguration, not whether a correctly
456
+ shaped entry will start at runtime.
432
457
 
433
458
  Checks are written against the published specs, not against what happens to
434
459
  appear in real projects. Two that were written the other way round shipped as
@@ -450,7 +475,9 @@ When adding a check, add its spec line there first.
450
475
  the `computedHash` in `skills-lock.json` is the obvious `refresh` source; the
451
476
  hash algorithm used by the installing tool is not documented here, and it is
452
477
  not reproducible from `SKILL.md` bytes alone, so it is not implemented.
453
- - **Bun only.** `import.meta.dir` / `import.meta.main`; it will not run on Node.
478
+ - **Runtime split.** Source development and the composite Action use Bun; the
479
+ published `dist/cli.js` runs on Node 20.11+ or Bun. The source entrypoint uses
480
+ Bun's `import.meta.dir` / `import.meta.main` and is not the Node entrypoint.
454
481
  - Policy files are read up to 100 KB, so `policyLines` undercounts past that.
455
482
 
456
483
  ## Development
@@ -469,14 +496,34 @@ bun run src/cli.ts check tests/fixtures/lock-drift
469
496
  | `test` | `bun test` |
470
497
  | `check` | `bun run src/cli.ts check` |
471
498
 
499
+ ### Release checklist
500
+
501
+ Before publishing a release:
502
+
503
+ 1. Confirm `package.json` and `src/version.ts` contain the same version.
504
+ 2. Run `bun run build`, then verify `node dist/cli.js --version` prints it.
505
+ 3. Run `bun run typecheck`, `bun test`, and `bun run spec:check`.
506
+ 4. Refresh the test-count badge and version examples above from that release run.
507
+
472
508
  ## Design docs
473
509
 
474
510
  **This README is the source of truth for current behavior.** The documents under
475
511
  `docs/superpowers/specs/` are the original design and are **superseded** —
476
- several decisions changed while building: Bun-only runtime, the dep-to-skill map
477
- and its "orphan" heuristic dropped in favour of `skills-lock.json`, budget
478
- checks added, structural checks added, and the YAML rule engine deleted
479
- (`plans/010`). Read them as history, not as behavior.
512
+ several decisions changed while building: the original Bun-only runtime claim was
513
+ superseded by a Node-compatible published bundle, the dep-to-skill map and its
514
+ "orphan" heuristic dropped in favour of `skills-lock.json`, budget checks added,
515
+ structural checks added, and the YAML rule engine deleted (`plans/010`). Read
516
+ them as history, not as behavior.
517
+
518
+ ## Skill
519
+
520
+ 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:
521
+
522
+ ```bash
523
+ cp -R skills/agentscan .cursor/skills/agentscan
524
+ # or: .agents/skills/agentscan
525
+ # or: .claude/skills/agentscan
526
+ ```
480
527
 
481
528
  ## License
482
529