@goplus/agentguard 1.1.9 → 1.1.13

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 (65) hide show
  1. package/README.md +25 -8
  2. package/dist/adapters/common.d.ts.map +1 -1
  3. package/dist/adapters/common.js +3 -1
  4. package/dist/adapters/common.js.map +1 -1
  5. package/dist/adapters/openclaw-plugin.d.ts.map +1 -1
  6. package/dist/adapters/openclaw-plugin.js +17 -3
  7. package/dist/adapters/openclaw-plugin.js.map +1 -1
  8. package/dist/cli.js +170 -11
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +3 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +18 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/feed/cron.d.ts +27 -0
  15. package/dist/feed/cron.d.ts.map +1 -1
  16. package/dist/feed/cron.js +721 -54
  17. package/dist/feed/cron.js.map +1 -1
  18. package/dist/installers.d.ts +1 -1
  19. package/dist/installers.d.ts.map +1 -1
  20. package/dist/installers.js +164 -13
  21. package/dist/installers.js.map +1 -1
  22. package/dist/postinstall.js +29 -0
  23. package/dist/postinstall.js.map +1 -1
  24. package/dist/registry/storage.d.ts.map +1 -1
  25. package/dist/registry/storage.js +5 -1
  26. package/dist/registry/storage.js.map +1 -1
  27. package/dist/runtime/types.d.ts +1 -1
  28. package/dist/runtime/types.d.ts.map +1 -1
  29. package/dist/tests/cli-init.test.d.ts +2 -0
  30. package/dist/tests/cli-init.test.d.ts.map +1 -0
  31. package/dist/tests/cli-init.test.js +130 -0
  32. package/dist/tests/cli-init.test.js.map +1 -0
  33. package/dist/tests/cli-policy.test.js +47 -0
  34. package/dist/tests/cli-policy.test.js.map +1 -1
  35. package/dist/tests/cli-subscribe.test.js +33 -0
  36. package/dist/tests/cli-subscribe.test.js.map +1 -1
  37. package/dist/tests/feed-cron.test.js +441 -13
  38. package/dist/tests/feed-cron.test.js.map +1 -1
  39. package/dist/tests/installer.test.js +37 -2
  40. package/dist/tests/installer.test.js.map +1 -1
  41. package/dist/tests/integration.test.js +9 -5
  42. package/dist/tests/integration.test.js.map +1 -1
  43. package/dist/tests/postinstall.test.d.ts +2 -0
  44. package/dist/tests/postinstall.test.d.ts.map +1 -0
  45. package/dist/tests/postinstall.test.js +31 -0
  46. package/dist/tests/postinstall.test.js.map +1 -0
  47. package/dist/tests/setup-script.test.d.ts +2 -0
  48. package/dist/tests/setup-script.test.d.ts.map +1 -0
  49. package/dist/tests/setup-script.test.js +63 -0
  50. package/dist/tests/setup-script.test.js.map +1 -0
  51. package/dist/tests/smoke.test.js +88 -1
  52. package/dist/tests/smoke.test.js.map +1 -1
  53. package/docs/codex.md +1 -1
  54. package/docs/hermes.md +3 -3
  55. package/package.json +1 -1
  56. package/skills/agentguard/SKILL.md +424 -194
  57. package/skills/agentguard/hermes-hooks.yaml +2 -2
  58. package/skills/agentguard/scan-rules.md +13 -2
  59. package/skills/agentguard/scripts/{action-cli.ts → action-cli.js} +13 -18
  60. package/skills/agentguard/scripts/auto-scan.js +3 -1
  61. package/skills/agentguard/scripts/checkup-score.js +369 -0
  62. package/skills/agentguard/scripts/hermes-hook.js +103 -16
  63. package/skills/agentguard/scripts/scan-to-sarif.js +195 -0
  64. package/skills/agentguard/scripts/{trust-cli.ts → trust-cli.js} +12 -16
  65. package/skills/agentguard/suppress.example.yaml +67 -0
@@ -30,8 +30,8 @@ filesystem-access:
30
30
  access: read-write
31
31
  reason: "Read/write audit log (audit.jsonl) and protection level config (config.json)"
32
32
  user-invocable: true
33
- allowed-tools: Read, Write, Grep, Glob, Bash(node *trust-cli.ts *) Bash(node *action-cli.ts *) Bash(*checkup-report.js) Bash(echo *checkup-report.js) Bash(cat *checkup-report.js) Bash(agentguard *) Bash(openclaw *) Bash(ss *) Bash(lsof *) Bash(ufw *) Bash(iptables *) Bash(crontab *) Bash(systemctl list-timers *) Bash(find *) Bash(stat *) Bash(env) Bash(sha256sum *) Bash(node *) Bash(cd *)
34
- argument-hint: "[scan|action|patrol|subscribe|trust|report|config|checkup|cli] [args...]"
33
+ allowed-tools: Read, Write, Grep, Glob, Bash(node *trust-cli.js *) Bash(node *action-cli.js *) Bash(*checkup-report.js) Bash(*checkup-score.js) Bash(*scan-to-sarif.js) Bash(echo *checkup-report.js) Bash(cat *checkup-report.js) Bash(agentguard *) Bash(openclaw *) Bash(ss *) Bash(lsof *) Bash(ufw *) Bash(iptables *) Bash(crontab *) Bash(systemctl list-timers *) Bash(find *) Bash(stat *) Bash(env) Bash(sha256sum *) Bash(node *) Bash(cd *)
34
+ argument-hint: "[scan|action|patrol|subscribe|trust|report|config|checkup|cli] [args...] [--format sarif|json] [--output <file>]"
35
35
  ---
36
36
 
37
37
  # GoPlus AgentGuard — AI Agent Security Framework
@@ -53,13 +53,30 @@ cd ~/.openclaw/skills/agentguard && node scripts/checkup-report.js
53
53
 
54
54
  ## Command Routing
55
55
 
56
+ ### Post-Install Guidance
57
+
58
+ When AgentGuard has just been installed, or the user asks what to do next after
59
+ installing AgentGuard, recommend the required setup step below instead of
60
+ deriving a longer list from every supported command:
61
+
62
+ ```bash
63
+ agentguard init --agent auto
64
+ ```
65
+
66
+ Use a specific agent such as `hermes`, `qclaw`, `openclaw`, `claude-code`, or
67
+ `codex` only when the current host is known and the user wants targeted setup.
68
+ Treat `agentguard connect` and `agentguard checkup` as optional follow-ups, not
69
+ required post-install steps.
70
+
56
71
  Parse `$ARGUMENTS` to determine the subcommand:
57
72
 
73
+ - **`init [args...]`** — Run `agentguard init`, especially `agentguard init --agent <agent>` after installation
74
+ - **`connect [args...]`** — Run `agentguard connect` to connect optional Cloud policy, audit, and approvals
58
75
  - **`scan <path>`** — Scan a skill or codebase for security risks
59
76
  - **`action <description>`** — Evaluate whether a runtime action is safe
60
77
  - **`patrol [run|setup|status]`** — Daily security patrol for OpenClaw environments
78
+ - **`trust <lookup|attest|revoke|list|seed> [args]`** — Manage skill trust levels
61
79
  - **`subscribe [args...]`** — Pull AgentGuard Cloud threat-feed advisories, self-check local skills, and optionally install the OpenClaw 15-minute conditional notification cron
62
- - **`trust <lookup|attest|revoke|list> [args]`** — Manage skill trust levels
63
80
  - **`report`** — View recent security events from the audit log
64
81
  - **`config <strict|balanced|permissive>`** — Set protection level
65
82
  - **`checkup`** — Run a comprehensive agent health checkup and generate a visual HTML report
@@ -74,20 +91,22 @@ This skill is allowed to run `agentguard *`, so CLI commands and flags are avail
74
91
 
75
92
  The skill's routed subcommands take priority over similarly named CLI commands. Do not route these through the packaged CLI unless the user explicitly prefixes the request with `/agentguard cli`: `scan`, `action`, `patrol`, `trust`, `report`, `config`, `checkup`, `hermes-hooks`.
76
93
 
77
- Use CLI passthrough for the CLI-only commands below, for explicit `/agentguard cli <args...>` requests, or for the targeted `checkup --against-advisory <id>` mode described below.
94
+ Use CLI passthrough for the CLI-only commands below, for `init` and `connect`, for explicit `/agentguard cli <args...>` requests, or for the targeted `checkup --against-advisory <id>` mode described below.
78
95
 
79
96
  Supported CLI commands and options:
80
97
 
81
98
  | CLI command | Options | Notes |
82
99
  |---|---|---|
83
- | `agentguard init` | `--level <level>`, `--agent <agent>`, `--cloud <url>`, `--force` | Creates local config and optionally installs agent templates |
100
+ | `agentguard init` | `--level <level>`, `--agent <agent>`, `--cloud <url>`, `--force` | Creates local config, persists the selected agent host, and optionally installs templates for `claude-code`, `codex`, `openclaw`, `hermes`, or `qclaw` |
84
101
  | `agentguard connect` | `--key <key>`, `--api-key <key>`, `--url <url>`, `--cloud <url>` | Prefer `AGENTGUARD_API_KEY` over passing secrets in flags |
85
102
  | `agentguard disconnect` | none | Removes local Cloud API key, connection timestamp, pending event spool, and cached Cloud policy; keeps Cloud URL, audit log, and installed hooks/templates |
86
103
  | `agentguard status` | none | Shows local config, Cloud URL/API key status, policy cache, audit path |
87
104
  | `agentguard policy pull` | `--json` | Pulls Cloud effective runtime policy into the local cache |
105
+ | `agentguard policy show` | `--json` | Shows the cached effective runtime policy, or the bundled default policy when no cache exists |
88
106
  | `agentguard doctor` | none | Checks local setup and Cloud reachability when connected |
89
107
  | `agentguard protect` | `--agent <agent>`, `--action-type <type>`, `--tool-name <name>`, `--session-id <id>`, `--decision-mode <local-first|cloud>`, `--json` | Evaluates one runtime action from stdin or hook environment |
90
- | `agentguard subscribe` | `--since <iso>`, `--json`, `--quiet`, `--no-report`, `--cron <expr>`, `--cron-name <name>`, `--force`, `--cron-run` | Pulls Cloud threat advisories and optionally self-checks local skills |
108
+ | `agentguard subscribe` | `--since <iso>`, `--json`, `--quiet`, `--no-report`, `--cron <expr>`, `--cron-target <auto|openclaw|qclaw|hermes|system>`, `--cron-name <name>`, `--force`, `--cron-run`, `--cron-notify-run` | Pulls Cloud threat advisories and optionally self-checks local skills |
109
+ | `agentguard checkup` | `--json` | Runs the local agent health checkup |
91
110
  | `agentguard checkup --against-advisory <id>` | `--json` | CLI threat-feed self-check for one advisory; this is a targeted mode, not the default health-check workflow |
92
111
 
93
112
  If the user writes `/agentguard cli <args...>`, execute `agentguard <args...>` directly.
@@ -101,8 +120,10 @@ If the user writes `/agentguard checkup --against-advisory <id>`, use the CLI co
101
120
  Help the user configure AgentGuard runtime protection for Hermes Agent.
102
121
 
103
122
  Hermes does **not** load hooks from `SKILL.md` automatically. Hermes shell hooks
104
- must be present in `~/.hermes/config.yaml`. This skill ships the hook runner at
105
- `scripts/hermes-hook.js` and a copyable template at `hermes-hooks.yaml`.
123
+ must be present in `~/.hermes/config.yaml`; `agentguard init --agent hermes`
124
+ now installs the skill and merges the AgentGuard hook entries automatically.
125
+ This skill ships the hook runner at `scripts/hermes-hook.js` and a copyable
126
+ template at `hermes-hooks.yaml`.
106
127
 
107
128
  ### What the Hermes hook protects
108
129
 
@@ -111,11 +132,13 @@ must be present in `~/.hermes/config.yaml`. This skill ships the hook runner at
111
132
  | `pre_tool_call` | `terminal`, `execute_code` | `exec_command` |
112
133
  | `pre_tool_call` | `write_file`, `patch`, `skill_manage` | `write_file` |
113
134
  | `pre_tool_call` | `read_file` | `read_file` |
114
- | `pre_tool_call` | `web_search`, `web_extract`, `browser_navigate` | `network_request` |
135
+ | `pre_tool_call` | `web_search`, `web_extract`, `browser_navigate`, `browser_open`, `web_open`, `open_url`, `visit_url`, `open` | `network_request` |
115
136
  | `post_tool_call` | Same tools | Audit-only |
116
137
 
117
138
  Hermes `pre_tool_call` supports allow/block only. If AgentGuard returns `ask`,
118
139
  the Hermes hook reports it as a block with a confirmation-oriented message.
140
+ When AgentGuard Cloud is connected through `agentguard connect`, the hook uses
141
+ the shared runtime protection path and syncs pre-tool decisions to Cloud.
119
142
 
120
143
  ### Procedure
121
144
 
@@ -130,12 +153,13 @@ the Hermes hook reports it as a block with a confirmation-oriented message.
130
153
  ```bash
131
154
  npm install -g @goplus/agentguard
132
155
  ```
133
- 3. Read `hermes-hooks.yaml`, replace `AGENTGUARD_SKILL_DIR` with the absolute
134
- skill directory, and show the resulting YAML to the user.
135
- 4. Ask for explicit confirmation before editing `~/.hermes/config.yaml`.
136
- 5. If confirmed, merge the `hooks:` entries into `~/.hermes/config.yaml`.
137
- Preserve existing hooks and config values. Do not overwrite unrelated user
138
- configuration.
156
+ 3. Prefer `agentguard init --agent hermes --force` to install and merge the
157
+ hook entries automatically.
158
+ 4. For manual setup, read `hermes-hooks.yaml`, replace
159
+ `AGENTGUARD_SKILL_DIR` with the absolute skill directory, and show the
160
+ resulting YAML to the user.
161
+ 5. Ask for explicit confirmation before manually editing
162
+ `~/.hermes/config.yaml`.
139
163
  6. Tell the user to restart Hermes or launch it with one of the first-use
140
164
  consent options:
141
165
  ```bash
@@ -143,6 +167,11 @@ the Hermes hook reports it as a block with a confirmation-oriented message.
143
167
  HERMES_ACCEPT_HOOKS=1 hermes chat
144
168
  ```
145
169
  They may also set `hooks_auto_accept: true` in `~/.hermes/config.yaml`.
170
+ 7. For troubleshooting, run Hermes hook checks with
171
+ `AGENTGUARD_HERMES_DEBUG=1` to print the runtime decision, risk level, and
172
+ policy source to stderr. Use `hermes hooks doctor` or
173
+ `hermes hooks test pre_tool_call --for-tool terminal` when available to
174
+ confirm Hermes is parsing the block response.
146
175
 
147
176
  ### Verification
148
177
 
@@ -169,7 +198,7 @@ printf '{"hook_event_name":"pre_tool_call","tool_name":"terminal","tool_input":{
169
198
  Expected output contains:
170
199
 
171
200
  ```json
172
- {"action":"block"}
201
+ {"action":"block","decision":"block","block":true}
173
202
  ```
174
203
 
175
204
  ## Subcommand: subscribe
@@ -185,6 +214,10 @@ agentguard subscribe --json
185
214
  agentguard subscribe --since 2026-05-01T00:00:00.000Z
186
215
  agentguard subscribe --no-report
187
216
  agentguard subscribe --cron "0 * * * *"
217
+ agentguard subscribe --cron "0 * * * *" --cron-target system
218
+ agentguard subscribe --cron "0 * * * *" --cron-target openclaw
219
+ agentguard subscribe --cron "0 * * * *" --cron-target qclaw
220
+ agentguard subscribe --cron "0 * * * *" --cron-target hermes
188
221
  agentguard subscribe --cron "0 * * * *" --quiet
189
222
  agentguard subscribe --cron "0 * * * *" --cron-name agentguard-threat-feed
190
223
  agentguard subscribe --cron "0 * * * *" --force
@@ -192,11 +225,13 @@ agentguard subscribe --cron "0 * * * *" --force
192
225
 
193
226
  Without `--quiet`, `agentguard subscribe` pulls new threat-feed advisories and notifies the user to review them manually. With `--quiet`, it runs the full automated flow: pull new advisories, self-check local skills, report local matches back to Cloud, and notify only when local matches are found.
194
227
 
195
- When `--cron <expr>` is used, the CLI registers an OpenClaw isolated cron job through the local OpenClaw Gateway at `127.0.0.1:18789` using a standard five-field crontab expression such as `"0 * * * *"`. Pass `--cron-name <name>` to choose the job name. If a job with the same name already exists, the CLI leaves it untouched unless `--force` is passed. The cron delivery is intentionally silent (`delivery.mode = "none"`); the isolated turn executes `agentguard subscribe --json --cron-run` or `agentguard subscribe --quiet --json --cron-run` depending on whether `--quiet` was used during installation. Non-quiet cron sends the configured notification when new advisories are found; quiet cron sends it when local matches are found.
228
+ When `--cron <expr>` is used, the CLI first runs the subscribe flow once, then installs a recurring job using a standard five-field crontab expression such as `"0 * * * *"`. `--cron-target auto` is the default and uses the agent host saved by `agentguard init --agent`: `openclaw` uses the native `openclaw cron add` command and falls back to the OpenClaw Gateway at `127.0.0.1:18789`, `qclaw` uses the QClaw Gateway at `127.0.0.1:28789`, `hermes` uses native `hermes cron create` with a no-agent script under `~/.hermes/scripts/`, while `claude-code` and `codex` install a user crontab entry. OpenClaw/QClaw cron jobs use host `announce` delivery to the last chat route and run internal `--cron-notify-run`, which prints either the exact notification body or `NO_REPLY`; this keeps no-op cron ticks silent without embedding chat IDs in the job. If no agent host is saved, auto asks the user to run `agentguard init --agent <claude-code|codex|openclaw|hermes|qclaw>` first or pass `--cron-target openclaw`, `--cron-target qclaw`, `--cron-target hermes`, or `--cron-target system` explicitly. Pass `--cron-name <name>` to choose the job name. If a job with the same name already exists, the CLI leaves it untouched unless `--force` is passed.
229
+
230
+ System cron writes output to `~/.agentguard/feed-cron.log`; it does not send OpenClaw agent-channel notifications.
196
231
 
197
232
  `agentguard subscribe --json` always includes a stable `cron` object with `requested`, `installed`, and optional `result` fields. If cron installation fails, the command exits non-zero instead of printing a misleading success summary.
198
233
 
199
- `--since <iso>` overrides the persisted feed cursor for one run. `--no-report` skips uploading local matches back to Cloud in quiet mode. `--cron-run` is internal and should only be used by the OpenClaw cron prompt unless the user explicitly asks to reproduce cron behavior.
234
+ `--since <iso>` overrides the persisted feed cursor for one run. `--no-report` skips uploading local matches back to Cloud in quiet mode. `--cron-run` and `--cron-notify-run` are internal and should only be used by installed cron jobs unless the user explicitly asks to reproduce cron behavior.
200
235
 
201
236
  ---
202
237
 
@@ -206,6 +241,33 @@ When `--cron <expr>` is used, the CLI registers an OpenClaw isolated cron job th
206
241
 
207
242
  Scan the target path for security risks using all detection rules.
208
243
 
244
+ **Argument parsing**: Extract from `$ARGUMENTS`:
245
+ - The scan target path (first positional argument, or value after `scan`)
246
+ - `--format <fmt>` flag: supported values are `sarif` (SARIF 2.1.0 JSON) and `text` (default markdown)
247
+ - `--output <file>` flag: write output to this file instead of stdout
248
+
249
+ If `--format sarif` is present, follow the **SARIF Output Flow** at the end of this section instead of the standard Output Format.
250
+
251
+ ### Suppression Rules (read first)
252
+
253
+ Before running any detection, check for a suppression config file in the scan target root:
254
+
255
+ 1. Use the Read tool to read `<scan_target>/.agentguard-suppress.yaml`. If the file does not exist (Read returns an error or empty), skip suppression — no findings will be filtered.
256
+ 2. Parse the `suppress:` list. Each entry has:
257
+ - `rule` (required): rule ID to suppress (e.g. `PRIVATE_KEY_PATTERN`)
258
+ - `paths` (optional): list of glob patterns matched against the finding's file path (relative to scan root). `*` matches within one directory level; `**` matches across directories.
259
+ - `domains` (optional): list of substring/wildcard patterns matched against the finding's evidence text. `*` acts as a wildcard prefix or suffix.
260
+ - `reason` (required): explanation shown in the suppression summary.
261
+ 3. Keep this suppression list in memory — you will apply it after all detection rules have run.
262
+
263
+ **A finding is suppressed when ALL of the following are true:**
264
+ - Its `rule_id` exactly matches the entry's `rule` field.
265
+ - If the entry has `paths`: the finding's file path matches at least one glob pattern.
266
+ - If the entry has `domains`: the finding's evidence text contains at least one domain pattern match.
267
+ - If neither `paths` nor `domains` are specified: the finding is suppressed regardless of file or evidence.
268
+
269
+ Suppressed findings are **excluded from the findings table and risk level calculation**. At the end of the report, add a note: `> N finding(s) suppressed via .agentguard-suppress.yaml — run with details to review.`
270
+
209
271
  ### File Discovery
210
272
 
211
273
  Use Glob to find all scannable files at the given path. Include: `*.js`, `*.ts`, `*.jsx`, `*.tsx`, `*.mjs`, `*.cjs`, `*.py`, `*.json`, `*.yaml`, `*.yml`, `*.toml`, `*.sol`, `*.sh`, `*.bash`, `*.md`
@@ -227,8 +289,8 @@ For each rule, use Grep to search the relevant file types. Record every match wi
227
289
  | 4 | READ_ENV_SECRETS | MEDIUM | js,ts,mjs,py | Environment variable access |
228
290
  | 5 | READ_SSH_KEYS | CRITICAL | all | SSH key file access |
229
291
  | 6 | READ_KEYCHAIN | CRITICAL | all | System keychain / browser profiles |
230
- | 7 | PRIVATE_KEY_PATTERN | CRITICAL | all | Hardcoded private keys |
231
- | 8 | MNEMONIC_PATTERN | CRITICAL | all | Hardcoded mnemonic phrases |
292
+ | 7 | PRIVATE_KEY_PATTERN | CRITICAL* | all | Hardcoded private keys |
293
+ | 8 | MNEMONIC_PATTERN | CRITICAL* | all | Hardcoded mnemonic phrases |
232
294
  | 9 | WALLET_DRAINING | CRITICAL | js,ts,sol | Approve + transferFrom patterns |
233
295
  | 10 | UNLIMITED_APPROVAL | HIGH | js,ts,sol | Unlimited token approvals |
234
296
  | 11 | DANGEROUS_SELFDESTRUCT | HIGH | sol | selfdestruct in contracts |
@@ -246,6 +308,19 @@ For each rule, use Grep to search the relevant file types. Record every match wi
246
308
  | 23 | SUSPICIOUS_IP | MEDIUM | all | Hardcoded public IPv4 addresses |
247
309
  | 24 | SOCIAL_ENGINEERING | HIGH | md | Pressure language + execution instructions |
248
310
 
311
+ ### Git Context Check (Rules 7 & 8 only)
312
+
313
+ Rules marked **CRITICAL\*** start at CRITICAL but must be downgraded based on git context **before** being added to the findings list. For every file that matched Rule 7 (PRIVATE_KEY_PATTERN) or Rule 8 (MNEMONIC_PATTERN), run the following checks in order and assign the final severity:
314
+
315
+ 1. **Not in a git repo** — if `git -C <file_dir> rev-parse --git-dir 2>/dev/null` returns nothing → keep **CRITICAL**. Stop.
316
+ 2. **Ever committed** — run `git -C <file_dir> log --all --oneline -- <file_path>`. If output is non-empty → keep **CRITICAL**. Stop.
317
+ 3. **Not gitignored** — run `git -C <file_dir> check-ignore -q <file_path>`. If exit code is non-zero (file is NOT ignored) → downgrade to **HIGH**. Stop.
318
+ 4. **Gitignored** — exit code 0 → downgrade to **MEDIUM**.
319
+
320
+ Record the git context result (`committed` / `not-ignored` / `gitignored` / `no-git-repo`) in the finding's Evidence column alongside the matched content.
321
+
322
+ **Important**: these checks require `git` to be available. If `git` is not in PATH, skip the check and keep **CRITICAL**.
323
+
249
324
  ### Risk Level Calculation
250
325
 
251
326
  - Any **CRITICAL** finding -> Overall **CRITICAL**
@@ -261,7 +336,7 @@ For each rule, use Grep to search the relevant file types. Record every match wi
261
336
  **Target**: <scanned path>
262
337
  **Risk Level**: CRITICAL | HIGH | MEDIUM | LOW
263
338
  **Files Scanned**: <count>
264
- **Total Findings**: <count>
339
+ **Total Findings**: <count of non-suppressed findings>
265
340
 
266
341
  ### Findings
267
342
 
@@ -271,7 +346,10 @@ For each rule, use Grep to search the relevant file types. Record every match wi
271
346
 
272
347
  ### Summary
273
348
  <Human-readable summary of key risks, impact, and recommendations>
349
+
350
+ > N finding(s) suppressed via .agentguard-suppress.yaml
274
351
  ```
352
+ (Omit the suppression note line if no suppression file was found or no findings were suppressed.)
275
353
 
276
354
  ### Post-Scan Trust Registration
277
355
 
@@ -294,15 +372,53 @@ After outputting the scan report, if the scanned target appears to be a skill (c
294
372
  - `id`: the directory name of the scanned path
295
373
  - `source`: the absolute path to the scanned directory
296
374
  - `version`: read the `version` field from `package.json` in the scanned directory using the Read tool (if present), otherwise use `unknown`
297
- - `hash`: compute by running AgentGuard's own script: `node scripts/trust-cli.ts hash --path <scanned_path>` and extracting the `hash` field from the JSON output
375
+ - `hash`: compute by running AgentGuard's own script: `node scripts/trust-cli.js hash --path <scanned_path>` and extracting the `hash` field from the JSON output
298
376
  3. Show the user the full registration command and ask for confirmation before executing:
299
377
  ```
300
- node scripts/trust-cli.ts attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by agentguard-scan --notes "Auto-registered after scan. Risk level: <risk_level>." --force
378
+ node scripts/trust-cli.js attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by agentguard-scan --notes "Auto-registered after scan. Risk level: <risk_level>." --force
301
379
  ```
302
380
  4. Only execute after user approval. Show the registration result.
303
381
 
304
382
  If scripts are not available (e.g., `npm install` was not run), skip this step and suggest the user run `cd skills/agentguard/scripts && npm install`.
305
383
 
384
+ ### SARIF Output Flow (when `--format sarif` is present)
385
+
386
+ **Run Steps 1–3 (File Discovery, Detection Rules, Risk Level Calculation) exactly as above.** Then, instead of the standard markdown Output Format, do the following:
387
+
388
+ **Step A — Assemble findings as structured JSON** and write to `/tmp/agentguard-scan-findings.json`:
389
+
390
+ ```json
391
+ {
392
+ "target": "<scanned path>",
393
+ "scanned_at": "<ISO 8601 timestamp>",
394
+ "files_scanned": <number>,
395
+ "risk_level": "<CRITICAL|HIGH|MEDIUM|LOW>",
396
+ "findings": [
397
+ {
398
+ "rule_id": "<RULE_ID>",
399
+ "severity": "<CRITICAL|HIGH|MEDIUM|LOW>",
400
+ "file": "<relative/path/to/file.ext>",
401
+ "line": <line number>,
402
+ "evidence": "<matched content snippet>"
403
+ }
404
+ ]
405
+ }
406
+ ```
407
+
408
+ Use relative paths for `file` (relative to the scan target root). If no findings, use `"findings": []`.
409
+
410
+ **Step B — Run the SARIF converter** (cd into the skill directory first):
411
+
412
+ ```bash
413
+ cd <skill_directory> && node scripts/scan-to-sarif.js --file /tmp/agentguard-scan-findings.json
414
+ ```
415
+
416
+ **Step C — Handle output**:
417
+ - If `--output <file>` was specified: write the SARIF JSON to that file using the Write tool, then tell the user the file path.
418
+ - Otherwise: print the SARIF JSON to stdout (the user will redirect it, e.g. `> findings.sarif`).
419
+
420
+ **Do NOT** output the standard markdown report when `--format sarif` is active. Skip the Post-Scan Trust Registration offer.
421
+
306
422
  ---
307
423
 
308
424
  ## Subcommand: action
@@ -342,27 +458,27 @@ Parse the user's action description and apply the appropriate detector:
342
458
 
343
459
  ### Web3 Enhanced Detection
344
460
 
345
- When the action involves **web3_tx** or **web3_sign**, use AgentGuard's bundled `action-cli.ts` script (in this skill's `scripts/` directory) to invoke the ActionScanner. This script integrates the trust registry and optionally the GoPlus API (requires `GOPLUS_API_KEY` and `GOPLUS_API_SECRET` environment variables, if available):
461
+ When the action involves **web3_tx** or **web3_sign**, use AgentGuard's bundled `action-cli.js` script (in this skill's `scripts/` directory) to invoke the ActionScanner. This script integrates the trust registry and optionally the GoPlus API (requires `GOPLUS_API_KEY` and `GOPLUS_API_SECRET` environment variables, if available):
346
462
 
347
463
  For web3_tx:
348
464
  ```
349
- node scripts/action-cli.ts decide --type web3_tx --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>] [--user-present]
465
+ node scripts/action-cli.js decide --type web3_tx --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>] [--user-present]
350
466
  ```
351
467
 
352
468
  For web3_sign:
353
469
  ```
354
- node scripts/action-cli.ts decide --type web3_sign --chain-id <id> --signer <addr> [--message <msg>] [--typed-data <json>] [--origin <url>] [--user-present]
470
+ node scripts/action-cli.js decide --type web3_sign --chain-id <id> --signer <addr> [--message <msg>] [--typed-data <json>] [--origin <url>] [--user-present]
355
471
  ```
356
472
 
357
473
  For standalone transaction simulation:
358
474
  ```
359
- node scripts/action-cli.ts simulate --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>]
475
+ node scripts/action-cli.js simulate --chain-id <id> --from <addr> --to <addr> --value <wei> [--data <calldata>] [--origin <url>]
360
476
  ```
361
477
 
362
478
  The `decide` command also works for non-Web3 actions (exec_command, network_request, etc.) and automatically resolves the skill's trust level and capabilities from the registry:
363
479
 
364
480
  ```
365
- node scripts/action-cli.ts decide --type exec_command --command "<cmd>" [--skill-source <source>] [--skill-id <id>]
481
+ node scripts/action-cli.js decide --type exec_command --command "<cmd>" [--skill-source <source>] [--skill-id <id>]
366
482
  ```
367
483
 
368
484
  Parse the JSON output and incorporate findings into your evaluation:
@@ -394,29 +510,25 @@ Always combine script results with the policy-based checks (webhook domains, sec
394
510
 
395
511
  ## Subcommand: patrol
396
512
 
397
- **OpenClaw-specific daily security patrol.** Runs 8 automated checks that leverage AgentGuard's scan engine, trust registry, and audit log to assess the security posture of an OpenClaw deployment.
513
+ **Daily security patrol.** Runs 8 automated checks that leverage AgentGuard's scan engine, trust registry, and audit log to assess the security posture of your agent deployment. Works on OpenClaw and standard cron environments.
398
514
 
399
515
  For detailed check definitions, commands, and thresholds, see [patrol-checks.md](patrol-checks.md).
400
516
 
401
517
  ### Sub-subcommands
402
518
 
403
519
  - **`patrol`** or **`patrol run`** — Execute all 8 checks and output a patrol report
404
- - **`patrol setup`** — Configure as an OpenClaw daily cron job
520
+ - **`patrol setup`** — Configure as a daily cron job (OpenClaw or system crontab)
405
521
  - **`patrol status`** — Show last patrol results and cron schedule
406
522
 
407
- ### Pre-flight: OpenClaw Detection
523
+ ### Platform Detection
408
524
 
409
- Before running any checks, verify the OpenClaw environment:
525
+ Before running `patrol setup` or `patrol status`, detect the available scheduling platform:
410
526
 
411
- 1. Check for `$OPENCLAW_STATE_DIR` env var, fall back to `~/.openclaw/`
412
- 2. Verify the directory exists and contains `openclaw.json`
413
- 3. Check if `openclaw` CLI is available in PATH
527
+ 1. **OpenClaw**: Check for `$OPENCLAW_STATE_DIR` env var (fall back to `~/.openclaw/`), verify the directory exists and contains `openclaw.json`, and check if `openclaw` CLI is in PATH. If all three pass → use OpenClaw path.
528
+ 2. **System crontab**: Check if `crontab` command is available in PATH → use crontab path.
529
+ 3. **Neither available**: Inform the user and output the manual cron entry for them to add themselves.
414
530
 
415
- If OpenClaw is not detected, output:
416
- ```
417
- This command requires an OpenClaw environment. Detected: <what was found/missing>
418
- For non-OpenClaw environments, use /agentguard scan and /agentguard report instead.
419
- ```
531
+ For `patrol run`, no scheduling platform is needed run checks on any platform.
420
532
 
421
533
  Set `$OC` to the resolved OpenClaw state directory for all subsequent checks.
422
534
 
@@ -428,8 +540,8 @@ Detect tampered or unregistered skill packages by comparing file hashes against
428
540
 
429
541
  **Steps**:
430
542
  1. Discover skill directories under `$OC/skills/` (look for dirs containing `SKILL.md`)
431
- 2. For each skill, compute hash: `node scripts/trust-cli.ts hash --path <skill_dir>`
432
- 3. Look up the attested hash: `node scripts/trust-cli.ts lookup --source <skill_dir>`
543
+ 2. For each skill, compute hash: `node scripts/trust-cli.js hash --path <skill_dir>`
544
+ 3. Look up the attested hash: `node scripts/trust-cli.js lookup --source <skill_dir>`
433
545
  4. If hash differs from attested → **INTEGRITY_DRIFT** (HIGH)
434
546
  5. If skill has no trust record → **UNREGISTERED_SKILL** (MEDIUM)
435
547
  6. For drifted skills, run the scan rules against the changed files to detect new threats
@@ -473,13 +585,13 @@ Audit all cron jobs for download-and-execute patterns.
473
585
  Detect suspicious file modifications in the last 24 hours.
474
586
 
475
587
  **Steps**:
476
- 1. Find recently modified files: `find $OC/ ~/.ssh/ ~/.gnupg/ /etc/cron.d/ -type f -mtime -1`
588
+ 1. Find recently modified files: use Glob with patterns `$OC/**/*`, `~/.ssh/**/*`, `~/.gnupg/**/*` and filter results by mtime within 24h using `stat -f '%m %N' <file>` (macOS) or `stat -c '%Y %n' <file>` (Linux) — do NOT use the `find` binary as it may be unavailable in hardened environments
477
589
  2. For modified files with scannable extensions (.js/.ts/.py/.sh/.md/.json), run the full scan rule set
478
590
  3. Check permissions on critical files:
479
591
  - `$OC/openclaw.json` → should be 600
480
592
  - `$OC/devices/paired.json` → should be 600
481
593
  - `~/.ssh/authorized_keys` → should be 600
482
- 4. Detect new executable files in workspace: `find $OC/workspace/ -type f -perm +111 -mtime -1`
594
+ 4. Detect new executable files in workspace: use Glob `$OC/workspace/**/*` and check each file's executable bit with `stat` — do NOT use `find` with `-perm`
483
595
 
484
596
  #### [6] Audit Log Analysis (24h)
485
597
 
@@ -510,7 +622,7 @@ Verify security configuration is production-appropriate.
510
622
  Check for expired, stale, or over-privileged trust records.
511
623
 
512
624
  **Steps**:
513
- 1. List all records: `node scripts/trust-cli.ts list`
625
+ 1. List all records: `node scripts/trust-cli.js list`
514
626
  2. Flag:
515
627
  - Expired attestations (`expires_at` in the past)
516
628
  - Trusted skills not re-scanned in 30+ days
@@ -563,17 +675,20 @@ After outputting the report, append a summary entry to `~/.agentguard/audit.json
563
675
 
564
676
  ### patrol setup
565
677
 
566
- Configure the patrol as an OpenClaw daily cron job.
678
+ Configure the patrol as a daily cron job. Detects the available platform and uses the appropriate method.
567
679
 
568
680
  **Steps**:
569
681
 
570
- 1. Verify OpenClaw environment (same pre-flight as `patrol run`)
682
+ 1. Run platform detection (see above).
571
683
  2. Ask the user for:
572
- - **Timezone** (default: UTC). Examples: `Asia/Shanghai`, `America/New_York`, `Europe/London`
573
684
  - **Schedule** (default: `0 3 * * *` — daily at 03:00)
574
- - **Notification channel** (optional): `telegram`, `discord`, `signal`
685
+ - **Timezone** (default: UTC). Examples: `Asia/Shanghai`, `America/New_York`, `Europe/London`
686
+ - **Notification channel** (optional, OpenClaw only): `telegram`, `discord`, `signal`
575
687
  - **Chat ID / webhook** (required if channel is set)
576
- 3. Generate the cron registration command:
688
+
689
+ #### Path A — OpenClaw available
690
+
691
+ Generate and show the OpenClaw cron registration command:
577
692
 
578
693
  ```bash
579
694
  openclaw cron add \
@@ -591,11 +706,41 @@ openclaw cron add \
591
706
  --to <chat-id>
592
707
  ```
593
708
 
594
- 4. **Show the exact command to the user and wait for explicit confirmation** before executing
595
- 5. After execution, verify with `openclaw cron list`
596
- 6. Output confirmation with the cron schedule
709
+ **Show the exact command and wait for explicit user confirmation before executing.**
710
+ After execution, verify with `openclaw cron list`.
711
+
712
+ > **Note**: `--timeout-seconds 300` is required because isolated sessions need cold-start time.
713
+
714
+ #### Path B — System crontab available (OpenClaw not available)
715
+
716
+ Resolve the absolute path to this skill's directory (parent of this SKILL.md file) as `<SKILL_DIR>`.
717
+
718
+ Validate before generating the entry:
719
+ - `<schedule>` must be a standard five-field cron expression. Reject values that contain newlines.
720
+ - `<SKILL_DIR>` must be an absolute path. Reject paths containing single quotes, double quotes, null bytes, or newlines.
721
+ - Do not include notification channel, chat ID, or webhook values in the system crontab entry. System cron writes only to the local patrol log.
722
+
723
+ Generate the crontab entry using a single-quoted skill directory. If `<SKILL_DIR>` contains spaces, keep it inside the quotes exactly as shown:
724
+ ```
725
+ <schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> "$HOME/.agentguard/patrol.log" 2>&1
726
+ ```
727
+
728
+ **Show the exact entry and wait for explicit user confirmation before writing.**
729
+
730
+ After confirmation, add the entry to the user's crontab:
731
+ ```bash
732
+ (crontab -l 2>/dev/null; printf '%s\n' "<schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> \"\$HOME/.agentguard/patrol.log\" 2>&1") | crontab -
733
+ ```
734
+
735
+ Verify with `crontab -l | grep agentguard`.
597
736
 
598
- > **Note**: `--timeout-seconds 300` is required because isolated sessions need cold-start time. The default 120s is not enough.
737
+ #### Path C Neither available
738
+
739
+ Output the crontab entry for the user to add manually:
740
+ ```
741
+ <schedule> cd '<SKILL_DIR>' && AGENTGUARD_AUTO_SCAN=1 node scripts/auto-scan.js >> "$HOME/.agentguard/patrol.log" 2>&1
742
+ ```
743
+ Explain that neither `openclaw` nor `crontab` was found in PATH, so the entry must be added manually.
599
744
 
600
745
  ### patrol status
601
746
 
@@ -603,11 +748,10 @@ Show the current patrol state.
603
748
 
604
749
  **Steps**:
605
750
 
606
- 1. Read `~/.agentguard/audit.jsonl`, find the most recent `event: "patrol"` entry
607
- 2. If found, display: timestamp, overall status, finding counts
608
- 3. Run `openclaw cron list` and look for `agentguard-patrol` job
609
- 4. If cron is configured, show: schedule, timezone, last run time, next run time
610
- 5. If cron is not configured, suggest: `/agentguard patrol setup`
751
+ 1. Read `~/.agentguard/audit.jsonl`, find the most recent `event: "patrol"` or `event: "auto_scan"` entry. If found, display: timestamp, overall status, finding counts.
752
+ 2. **OpenClaw available**: run `openclaw cron list` and look for `agentguard-patrol`. Show schedule, timezone, last/next run time if found.
753
+ 3. **System crontab available**: run `crontab -l 2>/dev/null | grep agentguard`. Show the matching entry if found.
754
+ 4. If no cron is configured on any platform, suggest: `/agentguard patrol setup`.
611
755
 
612
756
  ---
613
757
 
@@ -648,23 +792,127 @@ web3.tx_policy: 'allow' | 'confirm_high_risk' | 'deny'
648
792
 
649
793
  ### Operations
650
794
 
651
- **lookup** — `node scripts/trust-cli.ts lookup --source <source> --version <version>`
795
+ **lookup** — `node scripts/trust-cli.js lookup --source <source> --version <version>`
652
796
  Query the registry for a skill's trust record.
653
797
 
654
- **attest** — `node scripts/trust-cli.ts attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by <name>`
798
+ **attest** — `node scripts/trust-cli.js attest --id <id> --source <source> --version <version> --hash <hash> --trust-level <level> --preset <preset> --reviewed-by <name>`
655
799
  Create or update a trust record. Use `--preset` for common capability models or provide `--capabilities <json>` for custom.
656
800
 
657
- **revoke** — `node scripts/trust-cli.ts revoke --source <source> --reason <reason>`
801
+ **revoke** — `node scripts/trust-cli.js revoke --source <source> --reason <reason>`
658
802
  Revoke trust for a skill. Supports `--source-pattern` for wildcards.
659
803
 
660
- **list** — `node scripts/trust-cli.ts list [--trust-level <level>] [--status <status>]`
804
+ **list** — `node scripts/trust-cli.js list [--trust-level <level>] [--status <status>]`
661
805
  List all trust records with optional filters.
662
806
 
807
+ **seed** — `agentguard trust seed [--auto-attest-low-risk] [--auto-attest-medium-risk] [--dry-run]`
808
+ Batch-scan all installed skills and auto-attest those meeting the risk threshold. Designed for initial baseline setup when many skills are already installed.
809
+
810
+ Flags:
811
+ - `--auto-attest-low-risk` (default when `seed` is invoked): attest LOW-risk skills as `trusted` with `read_only` preset.
812
+ - `--auto-attest-medium-risk`: also attest MEDIUM-risk skills as `restricted` with `none` preset.
813
+ - `--dry-run`: preview only — show the plan table without executing any attest commands.
814
+
815
+ **HIGH and CRITICAL risk skills are never auto-attested** regardless of flags. They must be reviewed and attested manually.
816
+
817
+ #### seed Flow
818
+
819
+ **Step 1 — Discover skills**
820
+
821
+ Glob all of the following paths for `*/SKILL.md` (same as checkup):
822
+ - `~/.claude/skills/*/SKILL.md`
823
+ - `~/.openclaw/skills/*/SKILL.md`
824
+ - `~/.openclaw/workspace/skills/*/SKILL.md`
825
+ - `~/.qclaw/skills/*/SKILL.md`
826
+ - `~/.qclaw/workspace/skills/*/SKILL.md`
827
+
828
+ Skip `agentguard` itself. Collect the parent directory of each found `SKILL.md` as the skill path.
829
+
830
+ **Step 2 — Filter unregistered skills**
831
+
832
+ For each discovered skill, run:
833
+ ```
834
+ node scripts/trust-cli.js lookup --source <skill_path>
835
+ ```
836
+ If the lookup returns a record with `status: active`, the skill is already registered — skip it and note "already registered" in the summary. Only proceed with skills that have no active trust record.
837
+
838
+ **Step 3 — Scan unregistered skills**
839
+
840
+ For each unregistered skill, run the full scan (24 detection rules, same as `/agentguard scan <skill_path>`). Record: skill name, skill path, risk level (LOW/MEDIUM/HIGH/CRITICAL), finding count.
841
+
842
+ **Step 4 — Build preview table**
843
+
844
+ Output a plan table before taking any action:
845
+
846
+ ```
847
+ ## AgentGuard Trust Seed — Plan
848
+
849
+ Scanned <N> unregistered skills. Proposed actions:
850
+
851
+ | Skill | Path | Risk | Findings | Proposed Action |
852
+ |-------|------|------|----------|-----------------|
853
+ | foo | ~/.claude/skills/foo | LOW | 0 | ✅ attest trusted/read_only |
854
+ | bar | ~/.claude/skills/bar | MEDIUM | 2 | ⚠️ attest restricted/none (requires --auto-attest-medium-risk) |
855
+ | baz | ~/.claude/skills/baz | HIGH | 5 | 🚫 SKIP — manual review required |
856
+ | qux | ~/.claude/skills/qux | CRITICAL | 8 | 🚫 SKIP — manual review required |
857
+
858
+ Already registered (skipped): <M> skills
859
+ Will attest: <K> skills
860
+ Will skip (HIGH/CRITICAL): <J> skills
861
+ ```
862
+
863
+ If `--dry-run` is present: output this table and stop. Add: `Dry run complete — no changes made. Remove --dry-run to execute.`
864
+
865
+ **Step 5 — User confirmation (REQUIRED)**
866
+
867
+ After showing the plan table, **always ask for explicit user confirmation** before executing any attest commands:
868
+
869
+ > "Ready to attest <K> skill(s). Confirm? (yes/no)"
870
+
871
+ Do NOT proceed without a clear affirmative response. If the user declines, stop and suggest `--dry-run` for future previews.
872
+
873
+ **Step 6 — Batch attest**
874
+
875
+ For each skill approved for attestation, compute its hash and run attest:
876
+
877
+ ```bash
878
+ # Compute hash
879
+ node scripts/trust-cli.js hash --path <skill_path>
880
+
881
+ # Attest
882
+ node scripts/trust-cli.js attest \
883
+ --id <skill_dir_name> \
884
+ --source <skill_path> \
885
+ --version <version_from_package.json_or_unknown> \
886
+ --hash <computed_hash> \
887
+ --trust-level <trusted|restricted> \
888
+ --preset <read_only|none> \
889
+ --reviewed-by agentguard-seed \
890
+ --notes "Auto-attested by trust seed. Scan risk: <risk_level>. Findings: <count>." \
891
+ --force
892
+ ```
893
+
894
+ Run these sequentially (not in parallel) to avoid registry write conflicts.
895
+
896
+ **Step 7 — Result summary**
897
+
898
+ ```
899
+ ## Trust Seed Complete
900
+
901
+ ✅ Attested: <N> skills
902
+ ⚠️ Skipped (already registered): <M> skills
903
+ 🚫 Skipped (HIGH/CRITICAL risk — manual review required): <J> skills
904
+ ❌ Failed: <K> skills (list errors)
905
+
906
+ Skills requiring manual review:
907
+ - <skill_name> (<path>) — Risk: HIGH/CRITICAL, <N> findings
908
+ Run: /agentguard scan <path> then /agentguard trust attest ...
909
+ ```
910
+
663
911
  ### Script Execution
664
912
 
665
913
  If the agentguard package is installed, execute trust operations via AgentGuard's own bundled script:
666
914
  ```
667
- node scripts/trust-cli.ts <subcommand> [args]
915
+ node scripts/trust-cli.js <subcommand> [args]
668
916
  ```
669
917
 
670
918
  For operations that modify the trust registry (`attest`, `revoke`), always show the user the exact command and ask for explicit confirmation before executing.
@@ -761,7 +1009,15 @@ If the log file doesn't exist, inform the user that no security events have been
761
1009
 
762
1010
  ## Subcommand: checkup
763
1011
 
764
- Run a comprehensive agent health checkup across 6 security dimensions. Generates a visual HTML report with a lobster mascot and opens it in the browser. The lobster's appearance reflects the agent's health: muscular bodybuilder (score 90+), healthy with shield (70–89), tired with coffee (50–69), or sick with bandages (0–49).
1012
+ Run a comprehensive agent health checkup across 5 security dimensions. Generates a visual HTML report with a lobster mascot and opens it in the browser. The lobster's appearance reflects the agent's health: muscular bodybuilder (score 90+), healthy with shield (70–89), tired with coffee (50–69), or sick with bandages (0–49).
1013
+
1014
+ **Scoring is handled by `checkup-score.js` — you MUST NOT calculate scores yourself. Your role is to collect raw facts, assemble them into structured JSON, and pass to the script.**
1015
+
1016
+ **Argument parsing**: Extract from `$ARGUMENTS`:
1017
+ - `--format json` flag: skip HTML generation and write the checkup JSON to a file instead
1018
+ - `--output <file>` flag: path for the JSON output file (required when `--format json` is used; defaults to `/tmp/agentguard-checkup-data.json` if omitted)
1019
+
1020
+ If `--format json` is present, follow the modified flow noted in Step 4 below.
765
1021
 
766
1022
  Plain `checkup` must always run this comprehensive workflow, even if the user phrases it as `agentguard checkup`. Do not answer that an advisory ID is required. Advisory IDs are optional and only switch to the targeted threat-feed self-check mode described below.
767
1023
 
@@ -778,6 +1034,8 @@ That CLI path fetches the current Cloud advisory feed and checks local skills ag
778
1034
 
779
1035
  **IMPORTANT: You MUST run ALL 7 checks below — not just the skill scan. The checkup covers 5 security dimensions, not just code scanning. Do NOT skip checks 2–7.**
780
1036
 
1037
+ **EVIDENCE RULE: Every finding you report MUST be backed by actual tool output collected in this step. You MUST quote the exact command output (or "no output" if the command returned nothing) in the finding's evidence field. Findings without concrete evidence from tool execution are FORBIDDEN — do not infer, assume, or fabricate results.**
1038
+
781
1039
  Run these checks in parallel where possible. These are **universal agent security checks** — they apply to any Claude Code or OpenClaw environment, regardless of whether AgentGuard is installed.
782
1040
 
783
1041
  1. **[REQUIRED] Discover & scan installed skills** (→ feeds Dimension 1: Code Safety): Glob ALL of the following paths for `*/SKILL.md`:
@@ -787,164 +1045,131 @@ Run these checks in parallel where possible. These are **universal agent securit
787
1045
  - `~/.qclaw/skills/*/SKILL.md`
788
1046
  - `~/.qclaw/workspace/skills/*/SKILL.md`
789
1047
 
790
- For **every** discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Do NOT skip any skill regardless of how many are found. Collect the scan results (risk level, findings count, risk tags) for each skill.
1048
+ For **every** discovered skill, **run `/agentguard scan <skill_path>`** using the scan subcommand logic (24 detection rules). Do NOT skip any skill regardless of how many are found. Record for each skill: name, risk_level, and exact findings list (rule, severity, file, line).
791
1049
  2. **[REQUIRED] Credential file permissions** (→ feeds Dimension 2: Credential Safety): Platform-aware check — behavior differs by OS:
792
- - **macOS/Linux**: Run `stat -f '%Lp' <path> 2>/dev/null || stat -c '%a' <path> 2>/dev/null` on `~/.ssh/`, `~/.gnupg/`, and if OpenClaw: on `$OC/openclaw.json`, `$OC/devices/paired.json`. **If the command returns empty output, the directory does not exist — treat as N/A (award full points), do NOT flag as a failure.**
793
- - **Windows**: `stat` is not available. Use `icacls <path>` to check ACLs instead. If the directory does not exist, treat as N/A (award full points). If it exists, check that the ACL grants access only to the current user (no `Everyone`, `Users`, or `Authenticated Users` with write/read access). Flag as FAIL only if the directory exists AND the ACL is overly permissive.
1050
+ - **macOS/Linux**: Run `stat -f '%Lp' <path> 2>/dev/null || stat -c '%a' <path> 2>/dev/null` on `~/.ssh/`, `~/.gnupg/`. **If the command returns empty output, the directory does not exist — record `exists: false`.**
1051
+ - **Windows**: `stat` is not available. Use `icacls <path>` to check ACLs instead. If directory doesn't exist, record `exists: false`. If it exists, record whether the ACL grants access to `Everyone`, `Users`, or `Authenticated Users`.
1052
+ - Also check OpenClaw config files if applicable (`$OC/openclaw.json`, `$OC/devices/paired.json`).
794
1053
  3. **[REQUIRED] Sensitive credential scan / DLP** (→ feeds Dimension 2: Credential Safety): Use Grep to scan **all** agent workspace directories for leaked secrets. This MUST cover the entire workspace root, not just the current agent's directory:
795
- - For OpenClaw / QClaw: scan `~/.openclaw/workspace/` and `~/.qclaw/workspace/` recursively — this includes **all** `workspace-agent-*/` subdirectories, not just the current agent's workspace
1054
+ - For OpenClaw / QClaw: scan `~/.openclaw/workspace/` and `~/.qclaw/workspace/` recursively
796
1055
  - For Claude Code: scan `~/.claude/` recursively
797
1056
  - For Hermes Agent: scan `~/.hermes/` recursively
798
1057
  - Patterns to detect:
799
1058
  - Private keys: `0x[a-fA-F0-9]{64}`, `-----BEGIN.*PRIVATE KEY-----`
800
1059
  - Mnemonics: sequences of 12+ BIP-39 words, `seed_phrase`, `mnemonic`
801
1060
  - API keys/tokens: `AKIA[0-9A-Z]{16}`, `gh[pousr]_[A-Za-z0-9_]{36}`, plaintext passwords
1061
+ - Record: `private_keys_found`, `mnemonics_found`, `api_keys_found` (boolean, with location if found).
802
1062
  - **Important**: Use the workspace *root* directory as the scan target (e.g. `~/.qclaw/workspace/`), not a specific agent subdirectory. All sibling `workspace-agent-*` directories must be included.
803
- 4. **[REQUIRED] Network exposure** (→ feeds Dimension 3: Network & System): Run `lsof -i -P -n 2>/dev/null | grep LISTEN` or `ss -tlnp 2>/dev/null` to check for dangerous open ports (Redis 6379, Docker API 2375, MySQL 3306, MongoDB 27017 on 0.0.0.0)
804
- 5. **[REQUIRED] Scheduled tasks audit** (→ feeds Dimension 3: Network & System): Check `crontab -l 2>/dev/null` for suspicious entries containing `curl|bash`, `wget|sh`, or accessing `~/.ssh/`
805
- 6. **[REQUIRED] Environment variable exposure** (→ feeds Dimension 3: Network & System): Run `env` and check for sensitive variable names (`PRIVATE_KEY`, `MNEMONIC`, `SECRET`, `PASSWORD`) — detect presence only, mask values
806
- 7. **[REQUIRED] Runtime protection check** (→ feeds Dimension 4: Runtime Protection): Check if security hooks exist in `~/.claude/settings.json`, `~/.openclaw/openclaw.json`, or `~/.hermes/config.yaml`, check for audit logs at `~/.agentguard/audit.jsonl`
807
-
808
- ### Step 2: Score Calculation
809
-
810
- **Additive scoring**: Each dimension starts at **0**. For each check that **passes**, add the listed points. Maximum is 100 per dimension. **Every failed check = 1 finding with severity and description.**
811
-
812
- #### Dimension 1: Skill & Code Safety (weight: 25%)
1063
+ 4. **[REQUIRED] Network exposure** (→ feeds Dimension 3: Network & System): Run `lsof -i -P -n 2>/dev/null | grep LISTEN` or `ss -tlnp 2>/dev/null` to check for dangerous open ports (Redis 6379, Docker API 2375, MySQL 3306, MongoDB 27017 on 0.0.0.0). Record list of dangerous ports found (e.g. `["Redis on 0.0.0.0:6379"]`).
1064
+ 5. **[REQUIRED] Scheduled tasks audit** (→ feeds Dimension 3: Network & System): Check `crontab -l 2>/dev/null` for suspicious entries containing `curl|bash`, `wget|sh`, or accessing `~/.ssh/`. Record list of suspicious cron command strings found.
1065
+ 6. **[REQUIRED] Environment variable exposure** (→ feeds Dimension 3: Network & System): Run `env` and check for sensitive variable names (`PRIVATE_KEY`, `MNEMONIC`, `SECRET`, `PASSWORD`) — detect presence only, mask values. Record list of sensitive variable names found.
1066
+ 7. **[REQUIRED] Runtime protection check** (→ feeds Dimension 4: Runtime Protection): Check if security hooks exist in `~/.claude/settings.json`, `~/.openclaw/openclaw.json`, or `~/.hermes/config.yaml`. Check for audit logs at `~/.agentguard/audit.jsonl`. Check if installed skills have been previously scanned (audit log contains `scan` events). Record booleans: `hooks_installed`, `audit_log_exists`, `skills_ever_scanned`.
813
1067
 
814
- Uses AgentGuard's 24-rule scan engine (`/agentguard scan`) to audit each installed skill. Start at base 100 and **deduct** for findings:
1068
+ ### Step 2: Assemble Raw Facts JSON
815
1069
 
816
- - Base score: **100**
817
- - Each CRITICAL finding: **−15**
818
- - Each HIGH finding: **−8**
819
- - Each MEDIUM finding: **−3**
820
- - Floor at **0** (never negative)
1070
+ After completing all 7 checks, assemble the raw facts into a structured JSON and write it to a temporary file (e.g. `/tmp/agentguard-raw-facts.json`):
821
1071
 
822
- For each finding, add: `"<rule_id> in <skill>:<file>:<line>"` with its severity.
823
-
824
- **False-positive suppression**: When the scanned skill is `agentguard` itself (skill path contains `agentguard`), suppress `READ_ENV_SECRETS` findings — AgentGuard reads environment variables as part of its own configuration detection, which is expected behaviour and not a security risk. Do not deduct points or list these as findings in the report.
825
-
826
- If no skills installed: score = **70**, add finding: "No third-party skills installed — no code to audit" (LOW).
827
-
828
- #### Dimension 2: Credential & Secret Safety (weight: 25%)
829
-
830
- Checks for leaked credentials and permission hygiene. Start at **0**, add points for each check that **passes** (total possible = 100):
831
-
832
- | Check | Points if PASS | If FAIL → finding |
833
- |-------|---------------|-------------------|
834
- | `~/.ssh/` permissions are 700 or stricter | **+25** | "~/.ssh/ permissions too open (<actual>) should be 700" (HIGH) |
835
- | `~/.gnupg/` permissions are 700 or stricter | **+15** | "~/.gnupg/ permissions too open (<actual>) — should be 700" (MEDIUM) |
836
-
837
- **Permission check rules (to avoid false positives):**
838
- - **Directory does not exist** (stat/icacls returns empty or "file not found"): Treat as N/A — award the points. A missing `~/.ssh/` or `~/.gnupg/` is not a security risk.
839
- - **Windows**: Use `icacls` instead of `stat`. Award full points if directory doesn't exist. Flag as FAIL only if directory exists AND ACL grants access to `Everyone`, `Users`, or `Authenticated Users`.
840
- - **macOS/Linux**: Flag as FAIL only when the directory exists AND stat returns a numeric value AND that value is greater than 700.
841
- | No private keys (hex 0x..64, PEM) found in skill code or workspace | **+25** | "Plaintext private key found in <location>" (CRITICAL) |
842
- | No mnemonic phrases found in skill code or workspace | **+20** | "Plaintext mnemonic found in <location>" (CRITICAL) |
843
- | No API keys/tokens (AWS AKIA.., GitHub gh*_) found in skill code | **+15** | "API key/token found in <location>" (HIGH) |
844
-
845
- #### Dimension 3: Network & System Exposure (weight: 20%)
846
-
847
- Checks for dangerous network exposure and system-level risks. Start at **0**, add points for each check that **passes** (total possible = 100):
848
-
849
- | Check | Points if PASS | If FAIL → finding |
850
- |-------|---------------|-------------------|
851
- | No high-risk ports exposed on 0.0.0.0 (Redis/Docker/MySQL/MongoDB) | **+35** | "Dangerous port exposed: <service> on 0.0.0.0:<port>" (HIGH) |
852
- | No suspicious cron jobs (curl\|bash, wget\|sh, accessing ~/.ssh/) | **+30** | "Suspicious cron job: <command>" (HIGH) |
853
- | No sensitive env vars with dangerous names (PRIVATE_KEY, MNEMONIC) | **+20** | "Sensitive env var exposed: <name>" (MEDIUM) |
854
- | OpenClaw config files have proper permissions (600) if applicable | **+15** | "OpenClaw config <file> permissions too open" (MEDIUM) |
855
-
856
- **Example**: If no dangerous ports (+35), no suspicious cron (+30), but env var `PRIVATE_KEY` found (+0), and not OpenClaw (+15 skip, give points) → score = 35 + 30 + 0 + 15 = **80**.
857
-
858
- #### Dimension 4: Runtime Protection (weight: 15%)
859
-
860
- Checks whether the agent has active security monitoring. Start at **0**, add points for each check that **passes** (total possible = 100):
861
-
862
- | Check | Points if PASS | If FAIL → finding |
863
- |-------|---------------|-------------------|
864
- | Security hooks/guards installed (AgentGuard, custom hooks, etc.) | **+40** | "No security hooks installed — actions are unmonitored" (HIGH) |
865
- | Security audit log exists with recent events | **+30** | "No security audit log — no threat history available" (MEDIUM) |
866
- | Skills have been security-scanned at least once | **+30** | "Installed skills have never been security-scanned" (MEDIUM) |
867
-
868
- #### Dimension 5: Web3 Safety (weight: 15% if applicable)
1072
+ ```json
1073
+ {
1074
+ "skills": [
1075
+ {
1076
+ "name": "<skill-name>",
1077
+ "risk_level": "<low|medium|high|critical>",
1078
+ "findings": [
1079
+ { "rule": "<RULE_ID>", "severity": "<CRITICAL|HIGH|MEDIUM|LOW>", "file": "<filename>", "line": <number> }
1080
+ ]
1081
+ }
1082
+ ],
1083
+ "credential_files": {
1084
+ "ssh_dir": { "exists": <bool>, "permissions": "<octal string, e.g. 700>" },
1085
+ "gnupg_dir": { "exists": <bool>, "permissions": "<octal string>" },
1086
+ "openclaw_config": { "exists": <bool>, "ok": <bool> }
1087
+ },
1088
+ "dlp": {
1089
+ "private_keys_found": <bool>,
1090
+ "mnemonics_found": <bool>,
1091
+ "api_keys_found": <bool>
1092
+ },
1093
+ "network": {
1094
+ "dangerous_ports": ["<description>"],
1095
+ "suspicious_crons": ["<command>"],
1096
+ "sensitive_env_vars": ["<VAR_NAME>"],
1097
+ "openclaw_config_ok": <bool|null>
1098
+ },
1099
+ "runtime": {
1100
+ "hooks_installed": <bool>,
1101
+ "audit_log_exists": <bool>,
1102
+ "skills_ever_scanned": <bool>
1103
+ },
1104
+ "web3": {
1105
+ "detected": <bool>,
1106
+ "wallet_draining_found": <bool>,
1107
+ "unlimited_approval_found": <bool>,
1108
+ "goplus_configured": <bool>
1109
+ }
1110
+ }
1111
+ ```
869
1112
 
870
- Only if Web3 usage is detected (env vars like `GOPLUS_API_KEY`, `CHAIN_ID`, `RPC_URL`, or web3-related skills installed). Otherwise `{ "score": null, "na": true }`. Start at **0**, add points for each check that **passes** (total possible = 100):
1113
+ **Web3 detection**: set `detected: true` if any of these are present: env vars `GOPLUS_API_KEY`, `CHAIN_ID`, or `RPC_URL`; or any skill with web3-related findings (WALLET_DRAINING, UNLIMITED_APPROVAL).
871
1114
 
872
- | Check | Points if PASS | If FAIL → finding |
873
- |-------|---------------|-------------------|
874
- | No wallet-draining patterns (approve+transferFrom) in skill code | **+40** | "Wallet-draining pattern detected in <skill>" (CRITICAL) |
875
- | No unlimited token approval patterns in skill code | **+30** | "Unlimited approval pattern detected in <skill>" (HIGH) |
876
- | Transaction security API configured (GoPlus or equivalent) | **+30** | "No transaction security API Web3 calls are unverified" (MEDIUM) |
1115
+ **Pre-Step-3 validation** verify all fields are populated before proceeding:
1116
+ - [ ] `skills` — from check 1
1117
+ - [ ] `credential_files` from check 2
1118
+ - [ ] `dlp` from check 3
1119
+ - [ ] `network`from checks 4, 5, 6
1120
+ - [ ] `runtime` — from check 7
1121
+ - [ ] `web3` — detected flag + fields
877
1122
 
878
- #### Composite Score Calculation
1123
+ **If any field is missing, go back and run the missing check. Do NOT proceed with incomplete data.**
879
1124
 
880
- Calculate the weighted average of all applicable dimensions:
1125
+ ### Step 3: Compute Scores with checkup-score.js
881
1126
 
882
- ```
883
- composite_score = (code_safety × 0.25) + (credential_safety × 0.25) + (network_exposure × 0.20) + (runtime_protection × 0.15) + (web3_safety × 0.15)
884
- ```
1127
+ Run the scoring script (it reads the raw facts and deterministically computes all dimension scores, composite score, and tier — do NOT calculate these yourself):
885
1128
 
886
- If Web3 Safety is N/A, redistribute its 15% weight proportionally across the other 4 dimensions:
887
- ```
888
- composite_score = (code_safety × 0.294) + (credential_safety × 0.294) + (network_exposure × 0.235) + (runtime_protection × 0.176)
1129
+ ```bash
1130
+ cd <skill_directory> && node scripts/checkup-score.js --file /tmp/agentguard-raw-facts.json
889
1131
  ```
890
1132
 
891
- Round to the nearest integer.
1133
+ The script outputs a JSON object with:
1134
+ - `composite_score` (0–100)
1135
+ - `tier` (S/A/B/F) and `tier_label`
1136
+ - `total_findings`
1137
+ - `dimensions`: `code_safety`, `credential_safety`, `network_exposure`, `runtime_protection`, `web3_safety` — each with `score` and `findings[]`
892
1138
 
893
- **Tier assignment (MUST use these exact thresholds):**
1139
+ Capture this JSON output — you will use it in Step 4.
894
1140
 
895
- | Score Range | Tier | Label |
896
- |-------------|------|-------|
897
- | **90–100** | **S** | JACKED |
898
- | **70–89** | **A** | Healthy |
899
- | **50–69** | **B** | Tired |
900
- | **0–49** | **F** | Critical |
1141
+ ### Step 4: Generate Analysis Report
901
1142
 
902
- **Example**: code_safety=100, credential_safety=80, network_exposure=85, runtime_protection=30, web3=N/A composite = (100×0.294)+(80×0.294)+(85×0.235)+(30×0.176) = 29.4+23.5+20.0+5.3 = **78** Tier **A** (Healthy).
903
-
904
- ### Step 3: Generate Analysis Report
905
-
906
- Based on all collected data and findings, write a **comprehensive security analysis report** as a single text block. This is where you use your AI reasoning ability — don't just list facts, **analyze** them:
1143
+ Based on the scored output from Step 3 and the raw facts you collected, write a **comprehensive security analysis report** as a single text block. This is where you use your AI reasoning ability — don't just list facts, **analyze** them:
907
1144
 
908
1145
  - Summarize the overall security posture in 2-3 sentences
909
1146
  - Highlight the most critical risks and explain **why** they matter (e.g. "Your ~/.ssh/ permissions allow any process running as your user to read your private keys, which means a malicious skill could silently exfiltrate them")
910
- - For each major finding, provide a specific actionable fix (exact command to run)
1147
+ - For each major finding from the scored output, provide a specific actionable fix (exact command to run)
911
1148
  - Note what's going well — acknowledge secure areas
912
- - If applicable, explain attack scenarios that the current configuration is vulnerable to (e.g. "A malicious skill could install a cron job that phones home your credentials every hour")
1149
+ - If applicable, explain attack scenarios that the current configuration is vulnerable to
913
1150
  - Keep the tone professional but direct, like a security consultant's report
914
1151
 
915
- This report goes into the `"analysis"` field of the JSON output.
1152
+ This report goes into the `"analysis"` field of the final JSON.
916
1153
 
917
1154
  Also generate a list of actionable recommendations as `{ "severity": "...", "text": "..." }` objects for the structured view.
918
1155
 
919
- ### Pre-Step-4 Validation
920
-
921
- **Before assembling the JSON, verify you have collected data for ALL 5 dimensions:**
922
-
923
- - [ ] `code_safety` — from Step 1 check 1 (skill scanning)
924
- - [ ] `credential_safety` — from Step 1 checks 2 + 3 (permissions + DLP)
925
- - [ ] `network_exposure` — from Step 1 checks 4 + 5 + 6 (ports + cron + env vars)
926
- - [ ] `runtime_protection` — from Step 1 check 7 (hooks + audit log)
927
- - [ ] `web3_safety` — from Step 2 (only if Web3 detected, otherwise `{ "score": null, "na": true }`)
1156
+ ### Step 5: Generate HTML Report
928
1157
 
929
- **If any dimension is missing data, go back and run the missing checks. Do NOT submit a report with only code_safety filled in.**
930
-
931
- ### Step 4: Generate Report
932
-
933
- Assemble the results into a JSON object and pipe it to the report generator:
1158
+ Assemble the final JSON by merging the scored output from Step 3 with the analysis from Step 4, then pass it to the report generator:
934
1159
 
935
1160
  ```json
936
1161
  {
937
1162
  "timestamp": "<ISO 8601>",
938
- "composite_score": <0-100>,
939
- "tier": "<S|A|B|F>",
1163
+ "composite_score": <from checkup-score.js>,
1164
+ "tier": "<from checkup-score.js>",
940
1165
  "dimensions": {
941
- "code_safety": { "score": <n>, "findings": [...], "details": "<one-line summary>" },
942
- "credential_safety": { "score": <n>, "findings": [...], "details": "<one-line summary>" },
943
- "network_exposure": { "score": <n>, "findings": [...], "details": "<one-line summary>" },
944
- "runtime_protection": { "score": <n>, "findings": [...], "details": "<one-line summary>" },
945
- "web3_safety": { "score": <n|null>, "na": <bool>, "findings": [...], "details": "<one-line summary>" }
1166
+ "code_safety": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
1167
+ "credential_safety": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
1168
+ "network_exposure": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
1169
+ "runtime_protection": { "score": <from score>, "findings": [...], "details": "<one-line summary>" },
1170
+ "web3_safety": { "score": <from score|null>, "na": <bool>, "findings": [...], "details": "<one-line summary>" }
946
1171
  },
947
- "skills_scanned": <count>,
1172
+ "skills_scanned": <count of skills from Step 1>,
948
1173
  "protection_level": "<level>",
949
1174
  "analysis": "<the comprehensive AI-written security analysis report>",
950
1175
  "recommendations": [
@@ -953,19 +1178,24 @@ Assemble the results into a JSON object and pipe it to the report generator:
953
1178
  }
954
1179
  ```
955
1180
 
956
- Execute the report generator. **Use the `--file` method for cross-platform compatibility** (the `echo | pipe` method fails on Windows due to shell quoting differences):
1181
+ **If `--format json` was specified**:
1182
+ 1. Write this JSON to the `--output <file>` path (or `/tmp/agentguard-checkup-data.json` if no `--output` given) using the Write tool.
1183
+ 2. Tell the user: "Checkup JSON written to `<file>`." — include the composite score and tier in the message.
1184
+ 3. **Stop here** — skip Steps 5 and 6 (HTML generation and MEDIA delivery). The terminal summary in Step 5 is also skipped since the user is consuming the raw JSON programmatically.
1185
+
1186
+ **Otherwise (default HTML flow)**:
1187
+
1188
+ Write the JSON to a temporary file using the Write tool (e.g. `/tmp/agentguard-checkup-data.json`), then run (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
957
1189
 
958
- 1. First, write the JSON to a temporary file using the Write tool (e.g. `/tmp/agentguard-checkup-data.json`)
959
- 2. Then run (remember to `cd` into the skill directory first — see "Resolving Script Paths" above):
960
1190
  ```bash
961
1191
  cd <skill_directory> && node scripts/checkup-report.js --file /tmp/agentguard-checkup-data.json
962
1192
  ```
963
1193
 
964
- The script outputs the HTML file path to stdout (e.g. `/tmp/agentguard-checkup-1234567890.html`). Capture this path — you will need it for delivery in Step 6.
1194
+ The script outputs the HTML file path to stdout (e.g. `/tmp/agentguard-checkup-1234567890.html`). Capture this path — you will need it for delivery in Step 7.
965
1195
 
966
- > **Note**: The script also supports stdin pipe (`echo '<json>' | node scripts/checkup-report.js`) but this may fail on Windows cmd.exe where single quotes are not string delimiters. Always prefer `--file`.
1196
+ > **Note**: The script also supports stdin pipe (`echo '<json>' | node scripts/checkup-report.js`) but this may fail on Windows cmd.exe. Always prefer `--file`.
967
1197
 
968
- ### Step 5: Terminal Summary (REQUIRED)
1198
+ ### Step 6: Terminal Summary (REQUIRED)
969
1199
 
970
1200
  **You MUST output this summary after the report generates.** This is the primary output the user sees. Do NOT skip this step — always show the score, dimension table, and report path:
971
1201
 
@@ -1009,11 +1239,11 @@ Examples of plain-language descriptions:
1009
1239
  - SSH permissions: "Your SSH key folder has loose permissions — other processes on this machine could potentially read your private keys."
1010
1240
  - Plaintext credential: "A private key or API token was found in plain text in a file — it should be removed and rotated."
1011
1241
 
1012
- ### Step 6: Deliver the Report to the User
1242
+ ### Step 7: Deliver the Report to the User
1013
1243
 
1014
1244
  After printing the terminal summary, deliver the HTML report file. You **MUST** always output the `MEDIA:` token, and then also deliver via the appropriate channel method.
1015
1245
 
1016
- #### 6a. MEDIA token (required — always do this)
1246
+ #### 7a. MEDIA token (required — always do this)
1017
1247
 
1018
1248
  Output the following line on its **own line** in your response:
1019
1249
 
@@ -1025,10 +1255,10 @@ For example: `MEDIA:/tmp/agentguard-checkup-1234567890.html`
1025
1255
 
1026
1256
  This is how platforms like OpenClaw automatically deliver the file as a Telegram/Discord/WhatsApp attachment via `sendDocument`. The platform strips this line from visible text — the user won't see it. **Always output this regardless of what channel you think you're in.**
1027
1257
 
1028
- #### 6b. Channel-specific delivery (in addition to MEDIA token)
1258
+ #### 7b. Channel-specific delivery (in addition to MEDIA token)
1029
1259
 
1030
1260
  **Claude Code (local desktop)**
1031
- - The browser should already be open from Step 4.
1261
+ - The browser should already be open from Step 5.
1032
1262
  - Also copy to Desktop: `cp <file_path> ~/Desktop/agentguard-checkup-$(date +%Y-%m-%d).html`
1033
1263
  - Tell the user: "✅ Report saved to your Desktop and opened in browser."
1034
1264