@akasecurity/ai-tc-claude-code 0.8.2 → 0.9.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/commands/setup.md CHANGED
@@ -1,22 +1,19 @@
1
1
  ---
2
- description: Set up the AKA Control Plane plugin evidence-first detection posture and historical access
2
+ description: Set up AKA Security calibrate notifications and detection posture from Claude's real activity.
3
3
  ---
4
4
 
5
5
  # AKA setup wizard
6
6
 
7
- You are onboarding the AKA Control Plane plugin for this machine. AKA works
7
+ You are onboarding the AKA Security plugin for this machine. AKA works
8
8
  fully locally with **zero backend and zero Docker**: detection runs in-process
9
9
  and findings persist to a local SQLite store at `~/.aka/data/aka.db`.
10
10
 
11
- This wizard is **evidence-first**: instead of asking you to guess a global
12
- redact/warn setting up front, it looks at your _actual_ history for real
13
- leaked findings, triages them (silently filtering the routine false-positive
14
- noise regex rules produce), and recommends a detection **posture per
15
- category** (`secret`, `pii`, `financial`, `phi`, `code_context`, `code_flaw`,
16
- `config`, `custom`) — shown to you with its reasoning before anything is
17
- written. If there isn't enough history to judge, or you decline the
18
- historical review, it falls back to a conservative severity-derived floor
19
- instead of guessing.
11
+ This wizard tells a **calibration story**: introduce AKA show what it does
12
+ offer one retroactive scan report the real numbers it found and the posture it
13
+ recommends apply on confirmation show the installed summary → hand off to the
14
+ dashboard. Everything the user sees is derived from their _actual_ history — never
15
+ a fabricated or demo number. When there isn't enough history to judge, the wizard
16
+ falls back to a conservative severity-derived floor instead of guessing.
20
17
 
21
18
  The false-positive/severity judgment itself runs in a **separate, transient
22
19
  subprocess that writes no transcript** — the raw (unmasked) finding values are
@@ -24,21 +21,101 @@ never read into this conversation or your scannable history. You act only on the
24
21
  raw-free plan that subprocess prints back.
25
22
 
26
23
  Follow the steps below **in order**. Nothing is written to the policy store
27
- until step 5 (or the floor branch in step 2).
24
+ until step 5 (or a floor fallback in step 3 if the calibration can't complete).
25
+
26
+ ## Execution contract (read before step 0)
27
+
28
+ Every script prints output in three region kinds. Your job for each is fixed:
29
+
30
+ - **`<<<AKA_SHOW … AKA_SHOW>>>`** — relay every AKA_SHOW region verbatim as your
31
+ next message: paste the content _between_ the markers exactly — a card region
32
+ carries its own code fence, a plain confirmation line does not, but either way
33
+ you paste exactly what's between the markers — never the marker lines, never a
34
+ paraphrase or summary.
35
+ - **`<<<AKA_FRAME_JSON … AKA_FRAME_JSON>>>`** — machine-only. Parse it if a step
36
+ tells you to read a value from it; never display it.
37
+ - **Anything else on stdout** — status for you (paths like `Plan saved to:`,
38
+ errors, exit signals). Act on it; never relay it.
39
+
40
+ Invariants:
41
+
42
+ - **Never write a confirmation or acknowledgement the wizard did not emit** — the
43
+ script's AKA_SHOW line is the confirmation.
44
+ - **Each step's AKA_SHOW regions must be relayed before you advance.**
45
+ - **One picker per decision; never re-ask a decision already collected.**
28
46
 
29
47
  ## 0. Show the intro card
30
48
 
31
- Run the intro script and show the user its output **exactly as printed** it is a
32
- space-aligned monospace card (name, repository, version, what AKA adds). The script
33
- already prints it inside a Markdown code fence; reproduce that verbatim and do
34
- **not** add another code fence, strip the fence, or reformat it (unfenced, Markdown
35
- collapses the indentation and mangles the `●` line).
49
+ Run the intro script and relay its AKA_SHOW region per the execution contract:
50
+ paste the content between the markers verbatim, never the marker lines. It
51
+ prints a single space-aligned monospace card identity and provenance, then
52
+ what AKA does inside a Markdown code fence that is part of that pasted
53
+ content. Keep the fence as printed and do **not** add another code fence, strip
54
+ the fence, or reformat it (unfenced, Markdown collapses the indentation and
55
+ mangles the `●` lines).
36
56
 
37
57
  ```bash
38
58
  node "${CLAUDE_PLUGIN_ROOT}/scripts/intro.js" "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json"
39
59
  ```
40
60
 
41
- ## 1. Ask historical-review consent
61
+ ## 0b. Repo-aware posture check — tighten-only, working-tree only
62
+
63
+ Before showing any recommended posture — the start-light default table in
64
+ step 2 or the calibrated posture in step 4 — look at the **current project's**
65
+ working tree yourself, with your own Read/Glob tools. There is no script for
66
+ this: it is your own reasoning over facts you read directly, not the isolated
67
+ triage subprocess's raw-free plan, and it needs no user interaction.
68
+
69
+ **In scope:** the manifest's declared frameworks/dependencies
70
+ (`package.json` or equivalent), payment or other third-party API SDKs among
71
+ them, CI config (`.github/workflows/`, etc.), and the **presence and names**
72
+ of `.env*`/config files as a signal that secrets live on disk here — never
73
+ their contents; a secret-bearing file's contents are exactly the kind of raw
74
+ value this wizard never reads. **Out of scope:** Claude's own history and the
75
+ local AKA store (that is the separate, consent-gated scan in steps 1/3) — no
76
+ historical read, and no AskUserQuestion or other consent interaction of any
77
+ kind.
78
+
79
+ The severity-floor default map (secret/pii/financial/phi/code_flaw/custom at
80
+ `warn`, code_context/config at `monitor` — the table step 2's start-light card
81
+ prints) is both the **floor** this check is measured against and its
82
+ **fallback**. From what you directly observe you may **tighten** individual
83
+ categories above that floor — raise the level, never lower one below it —
84
+ each tightened category carrying a one-line rationale naming the concrete
85
+ evidence you found, e.g.:
86
+
87
+ > Stripe + a `Customer` model here — financial → redact
88
+
89
+ Present the tightening on whichever recommended posture is about to render, the
90
+ same "recommended base + changed-packs overlay" shape step 4b's adjust fork
91
+ uses: the tightened categories raise, every other category keeps its existing
92
+ recommended level, each carrying its rationale line. Where you compose the view
93
+ yourself (the adjust fork, the calibrated result) this tightened recommendation
94
+ IS that view; where the view is a script's AKA_SHOW card (the step-2
95
+ start-light card, relayed per the execution contract), show the tightened
96
+ recommendation and its rationale lines adjacent to that card rather than
97
+ rewriting the card's own printed levels.
98
+
99
+ This tightening is a **display-time recommendation**: it shapes the recommended
100
+ posture the user reads, not a separate write. Persisting a tightened level happens
101
+ only where the wizard already writes a per-category override — the adjust fork's
102
+ `onboard.js --posture` write (step 4b), where the user picks each category's level
103
+ explicitly. The keep-defaults path writes the severity floor (`--floor`, step 2)
104
+ and the calibrated accept path applies the isolated subprocess's saved plan
105
+ verbatim (`--confirmed --plan`, step 5); neither carries the tightening on its
106
+ own, so a tightened level the user wants persisted is chosen through the adjust
107
+ fork. Do **not** bolt on an extra `onboard.js --posture` overlay to auto-re-persist
108
+ the tightening across the other paths: it would overwrite — and so could silently
109
+ **downgrade** — a category the user had hardened out of band (a tightening is only
110
+ guaranteed to raise above the severity floor, not above the user's stored level),
111
+ with no downgrade-approval gate. So the tightening is not auto-persisted across
112
+ those paths — a tightened level the user wants kept is set through the adjust fork.
113
+
114
+ **When nothing in the working tree is inferable, change nothing.** Render the
115
+ recommended posture exactly as the static frame already gives it — no rationale
116
+ line and no tightened category (fail-open).
117
+
118
+ ## 1. Offer the retroactive scan
42
119
 
43
120
  Ask this **before** anything about detection posture — the posture
44
121
  recommendation in step 4 is _derived from_ the answer to this question, so it
@@ -47,55 +124,102 @@ interactive picker. The plugin can't draw its own selectable UI (it can't
47
124
  capture keystrokes), so do **not** print a fake option list or ask the user to
48
125
  "reply with a number"; let the picker collect the answer.
49
126
 
50
- **Historical & memory access** "Secrets often leak before AKA is installed. May I also review your temp files, agent memory & prior conversation transcripts?"
51
-
52
- List **Grant full review** as the first (top) option:
127
+ **Want me to look over what Claude's been up to?** "I'll review Claude's recent work transcripts, temp files, agent memory to tune what I bring to you next."
53
128
 
54
- - **Grant full review** → `full` — scan scratch/temp files, agent memory & prior
55
- transcripts for leaked secrets (deepest coverage · one-time consent, revocable
56
- under Policies). This is what lets AKA recommend a posture backed by your
57
- real findings instead of a generic default.
58
- - **Current session only** → `session-only` — decline historical access. AKA
59
- starts in a conservative observe-first posture instead (step 2) and can
60
- still review: the **working tree** (all source, config & dotfiles in the
61
- repo), **this session** (prompts, tool calls & files Claude reads or writes
62
- now), **git history** (commits reachable from HEAD, incl.
63
- removed-but-tracked secrets), and **pointed scans** (any path you explicitly
64
- hand AKA during a run).
129
+ Offer exactly two options:
65
130
 
66
- Map the picked label to the flag value shown above (`full`/`session-only`).
131
+ - **Yes, take a look** "tune what I bring you, based on Claude's real work here"
132
+ - **Not now** — "start light and I'll learn as we go"
67
133
 
68
- ## 2. Save the historical answer, then branch
134
+ Choosing **Yes, take a look** records the same historical-review consent the wizard has
135
+ always recorded — the identical scope, the one-time grant, and the
136
+ revocable-under-Policies semantics — so the simpler question broadens nothing
137
+ about what AKA may access. Those granular scope and revocation details stay
138
+ inspectable on request and in the dashboard.
69
139
 
70
- Run the onboarding writer with the answer from step 1. This must happen
71
- **before** the backfill (step 3), because the backfill script reads
72
- `historicalAccess` from the saved settings to decide whether it's allowed to
73
- run. Omitting `--policy` is deliberate — the old global redact/warn toggle no
74
- longer drives enforcement (posture is per-category now); its field is kept
75
- for backward compatibility but this wizard doesn't ask about it.
140
+ ## 2. Save the answer, then branch
76
141
 
77
- ```bash
78
- node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --historical <full|session-only>
79
- ```
142
+ Branch on the answer from step 1. On the **Yes, take a look** path the onboarding
143
+ writer runs, and it must run **before** the backfill (step 3), because the
144
+ backfill script reads `historicalAccess` from the saved settings to decide
145
+ whether it's allowed to run. Omitting `--policy` is deliberate — the old global
146
+ redact/warn toggle no longer drives enforcement (posture is per-category now);
147
+ its field is kept for backward compatibility but this wizard doesn't ask about
148
+ it.
80
149
 
81
- **Branch on the historical answer:**
150
+ **Branch on the choice:**
82
151
 
83
- - **If the user chose "Current session only" (`session-only`)** — there is no
84
- history to calibrate a posture from. Write the severity-derived floor
85
- immediately and skip straight to step 6 (first-run summary); steps 3–5 do
86
- not run:
152
+ - **If the user chose "Yes, take a look"** — record the historical-review consent and
153
+ continue to step 3 (which runs the scan and leads to the calibrated result in
154
+ step 4). "Yes, take a look" maps to the existing full historical-review path no
155
+ access is granted beyond what that path already granted:
87
156
 
88
157
  ```bash
89
- node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --floor
158
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --historical full
90
159
  ```
91
160
 
92
- Briefly tell the user why: without evidence, AKA starts every high-impact
93
- category (`secret`/`pii`/`financial`/`phi`/`code_flaw`/`custom`) at `warn`
94
- so nothing is under-watched, and low-value/observe-only categories
95
- (`code_context`/`config`) at `monitor` — conservative, no guessing. This can
96
- be revisited any time via `/aka:setup` or Policies.
97
-
98
- - **If the user chose "Grant full review" (`full`)** continue to step 3.
161
+ - **If the user chose "Not now"** take the **start-light** path.
162
+ This path takes **zero historical access**: do **not** read any history, do
163
+ **not** run the backfill, and do **not** record consent — nothing about the
164
+ machine's past is touched.
165
+ Instead present the start-light posture card, write the posture the user picks
166
+ (this write **is** the applying frame — it stands in for step 5, which never
167
+ runs here because there is no scan plan to apply), and rejoin the spine at the
168
+ installed summary (step 6). **Skip steps 3, 4, and 5 entirely** — there is no
169
+ scan to triage, no calibrated result to confirm, and no suppression plan to
170
+ write. Do the following in order:
171
+
172
+ 1. **Show the start-light card.** Run the start-light script and relay its
173
+ AKA_SHOW card per the execution contract — the
174
+ `● Starting light — your detection categories` heading, the full 8-pack ×
175
+ 4-level default posture table, the per-pack rationale, and the re-tune
176
+ hint, pasted between the markers exactly as printed, fence included. It
177
+ reads no history and writes nothing; it only prints the card
178
+ (the severity-floor default map — secret, pii, financial, phi, code_flaw, custom at
179
+ `warn`; code_context, config at `monitor`).
180
+
181
+ ```bash
182
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/start-light.js"
183
+ ```
184
+
185
+ 2. **Confirm or adjust.** Use **AskUserQuestion** — Claude Code's built-in
186
+ picker — to let the user keep the recommended defaults or tune individual
187
+ packs. The plugin can't draw its own selectable UI, so do **not** print a
188
+ fake option list or ask the user to "reply with a number"; let the picker
189
+ collect the answer.
190
+
191
+ **Set your detection categories** — "Keep the defaults I'd recommend, or adjust any of them?"
192
+
193
+ - **Keep defaults** _(recommended)_ — "the careful defaults shown above"
194
+ - **Adjust** — "change one or more levels, keep the rest as I recommend"
195
+
196
+ If they choose **Adjust**, use AskUserQuestion again to collect the new
197
+ level (monitor/warn/redact/block) for each pack they want to change, then
198
+ merge those overrides over the severity-floor defaults to form the full 8-pack map.
199
+
200
+ 3. **Write the chosen posture.** The default map is the severity floor,
201
+ so when the user keeps the defaults, write the floor directly; when they
202
+ adjusted packs, write the merged 8-pack map:
203
+
204
+ ```bash
205
+ # Kept the recommended defaults
206
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --floor
207
+
208
+ # Adjusted one or more packs — <json> is the merged 8-pack map
209
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --posture '<json>'
210
+ ```
211
+
212
+ Either write prints only `✓ Set all K detection categories` (the `--floor`
213
+ write appends ` — safe defaults`) — which is the honest confirmation here,
214
+ because nothing was scanned or suppressed. Show that line to the user; do
215
+ **not** invent a dismissed count or any calibration counts.
216
+
217
+ 4. **Rejoin the spine at the installed summary (step 6).** Continue to step 6
218
+ to show the installed summary and hand off to the dashboard, using honest
219
+ no-scan copy. No scan ran, so there is **no surfaced count** — call
220
+ `firstrun.js` with **no `--surfaced` flag** (the same floor-fallback rule
221
+ step 6 already follows when no calibration frame was emitted). Step 7
222
+ then runs as written.
99
223
 
100
224
  ## 3. Run the evidence triage — isolated judgment, nothing written yet
101
225
 
@@ -111,8 +235,9 @@ and streams one masked-plus-raw triage hit per line; masked findings are
111
235
  recorded to the local store as a side effect. The adapter runs the
112
236
  false-positive/severity **judgment in a separate transient subprocess** (no
113
237
  transcript), then prints back a **raw-free plan** you can safely show the user:
114
- the per-category posture it would apply, the masked false positives it would
115
- suppress, any categories it skipped, and its notes.
238
+ the calibrated-result card (the real-count headline and the recommended posture),
239
+ the per-category reasoning, the masked false positives it would suppress, any
240
+ categories it skipped, and its notes.
116
241
 
117
242
  The preview also **persists that exact raw-free plan to a temp file and prints
118
243
  its path** — a line beginning `Plan saved to: <path>`. Capture that path: step 5
@@ -120,6 +245,28 @@ applies **this saved plan verbatim**, so the confirm step performs no second sca
120
245
  and no second judgment. (The plan file carries only masked/fingerprint/enum data;
121
246
  it is deleted after a successful apply.)
122
247
 
248
+ Alongside the human copy, the preview also emits a **machine-readable calibration
249
+ frame** — a single JSON block delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>`
250
+ carrying the raw-free calibration counts and categories, plus (when the scan
251
+ surfaced any) a `maskedFindings` array of raw-free secret-leak summaries. Do
252
+ **not** show this block to the user (it is additive to the human copy above).
253
+ Capture its `counts.important` value — the **surfaced count** — and pass it to the
254
+ first-run summary in step 6 as `--surfaced <count>` — but **only when the preview
255
+ also printed a `Plan saved to:` path** (a real calibrated plan to confirm in step
256
+ 4). The `Plan saved to:` line is the completion signal: a preview that omits it did
257
+ not calibrate a plan you can confirm. The fallback branches below carry no
258
+ surfaced count; the scan-ran-clean empty state (a scan that completed but
259
+ surfaced nothing) emits a zero-count frame but **no plan path**, so it too routes
260
+ to the floor branch below rather than step 4.
261
+
262
+ Also **retain the block's full text verbatim** (not just the counts you read out
263
+ of it) — step 6's "Review leaked keys" branch feeds this same text to the
264
+ secret-leak remediation entry, which reads its own `maskedFindings` from it,
265
+ and step 4's finding narration and step 6's secret-leak narration (both below)
266
+ read the same `maskedFindings` array off it too. When present, the block's
267
+ `falsePositivePatterns` array is what step 4's fixture/exception offer (below)
268
+ names its pattern and count from — never invent either off-signal.
269
+
123
270
  Everything you show the user in step 4 comes from **this command's output**. You
124
271
  never read the raw finding values yourself — do not echo, quote, or reconstruct
125
272
  them; by design they stay inside the isolated subprocess.
@@ -139,82 +286,223 @@ conservative severity floor (high-impact categories at `warn`, observe-only at
139
286
  `monitor`) instead of a calibrated posture, and it can be re-run any time with
140
287
  `/aka:setup`.
141
288
 
142
- **Nothing to calibrate.** If the adapter reports there were no triage hits to
143
- review (an empty or intentionally-skipped scan), there's no evidence to
144
- calibrate from: take the same floor branch (`onboard.js --floor`), tell the
145
- user the scan found nothing to calibrate from, and skip to step 6.
146
-
147
- Otherwise continue to step 4.
148
-
149
- ## 4. Show the calibration and get explicit confirmation before any write
150
-
151
- The preview output is raw-free and has three parts. Show the user **all** of
152
- them.
153
-
154
- **Known limitation — the showcase is a first-run artifact.** The backfill records
155
- each masked finding to the local store as a side effect, so a second `/aka:setup`
156
- over the _same_ history dedups those already-recorded findings to zero triage hits
157
- and the showcase comes back empty (the adapter reports "no triage hits to
158
- review"). That is expected, not a failure: a re-run recalibrates **only if there
159
- is genuinely new history** since the last run. The **first run's** showcase is the
160
- one that matters — it is not reconstructed from the store on a re-run (the raw
161
- values it needs are deliberately never persisted). If a re-run shows nothing to
162
- calibrate, take the floor branch as usual and tell the user the scan found no new
163
- history.
164
-
165
- 1. **The per-category posture plan.** The action
166
- (`monitor`/`warn`/`redact`/`block`) the writeback would set for every
167
- category present in the evidence. Show it in full.
168
- - **Surface every downgrade — this is not optional.** The preview flags any
169
- category whose action would be **LOWERED** from a stronger existing setting
170
- (e.g. an existing `block`/`redact` dropping to `warn`/`monitor`) and prints
171
- a `WARNING` line summarizing them. Call these out prominently: a user who
172
- hardened a category must **explicitly approve weakening it**. Never let an
173
- enforcement downgrade through on the "apply as recommended" path without the
174
- user having seen it.
175
- - A category the adapter had to **skip** for its suppressions can still carry
176
- a posture change; it appears in this plan too, so the user sees any posture
177
- change even on a skipped category.
178
- 2. **The intelligence showcase.** The masked-only per-category reasoning and
179
- notes the judgment produced. Frame it as "look what it caught — and correctly
180
- dismissed": the false-positive discard is as much the pitch as the catch (a
181
- plain regex scanner would scream "161 CRITICAL secrets!"; AKA says "…all
182
- placeholders — `warn` is enough"). Keep the framing neutral — no "sloppy" or
183
- "bad practice".
289
+ **Nothing to calibrate.** A scan that **completes but surfaces nothing** prints
290
+ the honest **scan-ran-clean** card `I looked over Claude's recent work
291
+ nothing needs your attention right now. You're starting clean; here's what I'd
292
+ recommend:` over the recommended posture with a zero-count calibration frame
293
+ (its `counts.important` is `0`) and **no `Plan saved to:` path**. An empty or
294
+ intentionally-skipped scan instead prints `I didn't find anything to review
295
+ nothing to tune.`. In either case there's no evidence to calibrate from and no
296
+ plan to confirm: show the card the adapter printed, take the floor branch
297
+ (`onboard.js --floor`), tell the user the scan found nothing to calibrate from, and
298
+ skip to step 6 (with **no `--surfaced`**, the floor-fallback rule there nothing
299
+ was surfaced to carry over). Do **not** continue to step 4.
300
+
301
+ Otherwise (the preview printed a `Plan saved to:` path) continue to step 4.
302
+
303
+ ## 4. Show the calibrated result and get explicit confirmation before any write
304
+
305
+ The preview output is raw-free. Lead with the **calibrated-result card** it
306
+ printed and show it in full:
307
+
308
+ 1. **The calibrated headline.** The `I went through Claude's recent work N
309
+ detections, M results worth a look.` line every count templated over the
310
+ real scan (surfaced findings are the `M results` worth a look; the rest are
311
+ routine noise a plain scanner would have screamed about). Show it verbatim;
312
+ never substitute a demo number.
313
+ 2. **The recommended posture.** The condensed one-row-per-pack recommended view
314
+ the card printed the level AKA would set for each category. Show it in full.
315
+ - **Surface the downgrades the preview flags — this is not optional.** For the
316
+ recommended posture it is about to write, the preview compares each category
317
+ against its stored setting and flags any that would be **LOWERED** from a
318
+ stronger existing one (e.g. an existing `block`/`redact` dropping to
319
+ `warn`/`monitor`), printing a `WARNING` line summarizing them. Call these out
320
+ prominently: a user who hardened a category must **explicitly approve weakening
321
+ it** before applying.
184
322
  3. **The false positives to be suppressed (the human gate).** The masked value,
185
- rule, and masked context for each detection the writeback would suppress.
186
- This is the checkpoint that stops a genuine secret being silenced: the user
187
- reads the masked evidence and approves it.
323
+ rule, and masked context for each detection the writeback would suppress
324
+ the routine noise being dismissed. This is the checkpoint that stops a genuine
325
+ secret being silenced: the user reads the masked evidence and approves it.
326
+ 4. **Explain what surfaced, in plain language.** When the frame carries
327
+ `maskedFindings`, walk through them — what each one is, where it showed up,
328
+ and why it matters — grounded entirely in that array: every count you speak
329
+ equals the frame's own count for it (`counts.important`/`counts.total`, or
330
+ a specific finding kind's count), and every value you reference appears
331
+ masked, exactly as the frame gives it — never a raw value, never an
332
+ invented one. This is an actual explanation of the known findings, not a
333
+ restatement of the headline's counts. When the frame carries no
334
+ `maskedFindings` (nothing surfaced, or a fallback floor ran), skip this —
335
+ the calibrated-result card already said so; do not invent narration over a
336
+ missing signal.
337
+ 5. **Offer an exception for a grounded false-positive pattern.** When the
338
+ frame carries `falsePositivePatterns`, name each group's pattern and count
339
+ **strictly from that signal** — never invent a pattern name or fabricate a
340
+ count. For each group, use **AskUserQuestion** to offer a pre-filled
341
+ exception with a duration picker (the exception scope axis — `once` /
342
+ `temporary` / `permanent`):
343
+
344
+ **Make an exception for `<pattern>` (×N)?** — "This `<pattern>` looks like a
345
+ test fixture — want me to set an exception so it stops popping up?"
346
+
347
+ - **Once** — just this once — expires in 30 minutes
348
+ - **Temporary** — for a set window, then I'll check it again
349
+ - **Permanent** — stays until you revoke it
350
+ - **Not now** — skip — I won't write anything
351
+
352
+ **Temporary needs a concrete window.** `once` and `permanent` fully determine
353
+ the scope on their own, but `temporary` does not — resolving it into the
354
+ stored `{scope, expiresAt, maxUses}` triple requires an actual duration, and
355
+ you must **never** invent or default one. When the user picks **Temporary**,
356
+ follow up with a second **AskUserQuestion** that offers concrete windows only
357
+ — `30m` / `1h` / `24h` (the exception scope resolver accepts `<n>m`/`<n>h`,
358
+ capped at 24h; a longer bypass is a `permanent` grant, not a forgotten timer)
359
+ — and resolve the exact chosen string through that resolver. **Once** and
360
+ **Permanent** take no follow-up.
361
+
362
+ Accepting surfaces the exact pre-filled exception — one **per distinct value
363
+ identity** (`ruleId`/`valueFingerprint`/`keyVersion`) at the chosen
364
+ `{scope, expiresAt, maxUses}` — for review, and the marked pattern is
365
+ suppressed as part of the calibration plan confirmed below (the same store
366
+ `/aka:exceptions` reads). A group whose displayed pattern covers more than one
367
+ distinct value surfaces one exception per distinct value — never a single
368
+ grant collapsing them — and a value missing its exact identity is not
369
+ offered for. Declining surfaces nothing; this offer is separate from the
370
+ calibration plan's suppressions confirmed below, so declining here changes
371
+ nothing about that confirmation. When the frame carries no
372
+ `falsePositivePatterns` (nothing was marked a likely false positive, or the
373
+ scan was declined), skip this entirely — no offer, nothing invented
374
+ (fail-open).
188
375
 
189
376
  Then use **AskUserQuestion** (the real picker, never a printed numbered list) to
190
377
  confirm:
191
378
 
192
- **Apply this calibration?** — "Apply this detection posture and suppress the
193
- false positives shown above?"
379
+ **Want me to apply this?** — "I'll set these levels and suppress the false
380
+ positives above."
194
381
 
195
382
  - **Yes, apply** _(recommended)_ — write the posture and suppressions exactly as
196
383
  previewed.
197
- - **Let me adjust a category** — override one or more categories before saving
198
- (for example, keep a category the plan would lower).
199
-
200
- If they choose to adjust, ask a follow-up **AskUserQuestion** per category,
201
- offering the four actions with honest semantics so they choose with full
202
- information: `monitor` logs only; `warn` flags the request and lets them decide;
203
- `redact` strips the value from **tool I/O** but is a **no-op on the
204
- prompt/conversation channel** (a secret pasted into chat still reaches the
205
- model); `block` refuses the action outright. Collect the overrides as a
206
- `{category: action}` map. Do **not** proceed to step 5 until the user has
207
- explicitly confirmed.
384
+ - **Adjust a category** — "change one or more first; keep the rest as I
385
+ recommend"
386
+
387
+ Do **not** proceed until the user picks one. On **Yes, apply**, continue to
388
+ step 5 and apply the previewed plan verbatim that is the confirm spine,
389
+ unchanged. On **Adjust a category**, take the **adjust fork** (step 4b), which
390
+ applies within the fork and rejoins the spine at the installed summary (step 6).
391
+
392
+ ## 4b. Adjust a category the override fork
393
+
394
+ The **adjust base is the calibrated recommended posture the preview just
395
+ printed** — the condensed one-row-per-pack view from step 4, not the cold-start
396
+ severity floor. The user changes the packs they want and keeps the rest as
397
+ recommended.
398
+
399
+ 1. **Collect the changes.** Use **AskUserQuestion** — the built-in picker — to
400
+ ask which packs to change and to which level (monitor/warn/redact/block). The
401
+ plugin can't draw its own selectable UI, so do **not** print a fake option list
402
+ or ask the user to "reply with a number"; let the picker collect the answer.
403
+
404
+ 2. **Show the adjust-confirm table.** Compose the merged 8-pack map — the
405
+ recommended base with the user's picks overlaid — and render the adjust-confirm
406
+ card by passing the calibrated recommended posture as `--recommended` and that
407
+ merged map as `--posture`. Relay its AKA_SHOW region per the execution
408
+ contract — the fenced `category │ recommended │ yours` table, pasted between
409
+ the markers exactly as printed (it is space-aligned monospace; do **not** add
410
+ another code fence, strip the fence, or reformat it):
411
+
412
+ ```bash
413
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/start-light.js" --adjust-confirm --recommended '<recommended-json>' --posture '<merged-json>' --current '<current-json>'
414
+ ```
415
+
416
+ `<recommended-json>` is the calibrated recommended posture the preview printed
417
+ (the adjust base) — so the `recommended` column shows each pack's calibrated
418
+ level, and a pack calibration escalated above the floor never renders as a
419
+ spurious change. `<merged-json>` is the full 8-pack map — that same recommended
420
+ base with the user's overrides overlaid — so a changed pack reads as a different
421
+ `yours` value and every untouched pack repeats its recommended level.
422
+ `<current-json>` is the `current` object from the plan file at the path step 3
423
+ printed (`Plan saved to: <path>`) — the store's per-category action at preview
424
+ time, the baseline the downgrade check compares against. Pass it verbatim; do
425
+ not retype or summarize it.
426
+
427
+ 3. **Surface any downgrade — the card computes this, you do not.** With
428
+ `--current` passed, the card itself appends the `WARNING: N categories … would
429
+ be LOWERED from a stronger existing setting` footer whenever a pick weakens
430
+ enforcement — the same rule and the same wording as the confirm gate above,
431
+ from the same code. Show the card in full, footer included, and when that
432
+ footer is present get explicit approval before saving. Never let an enforcement
433
+ downgrade through without the user having seen it.
434
+
435
+ 4. **Save or back out.** Use **AskUserQuestion** with **N** the number of packs
436
+ the user changed and **M** the number kept as recommended (`M = 8 − N`), both
437
+ real — never a placeholder:
438
+
439
+ **Save your adjustments?**
440
+
441
+ - **Save adjusted — N changed, M as recommended** — apply with the adjusted
442
+ posture.
443
+ - **Back to recommended** — discard the changes and apply the recommended
444
+ posture instead.
445
+
446
+ 5. **On "Save adjusted" — produce the applying frame here, carrying the adjusted
447
+ posture, then rejoin the spine at the installed summary (step 6).** This fork
448
+ applies within itself and **stands in for step 5**, so step 5 never runs on
449
+ this path. First apply the previewed plan with the **unchanged confirm spine**,
450
+ so the reviewed false positives are dismissed and the recommended base is
451
+ written (the reviewed evidence packs overwrite; the severity floor fill-gaps the
452
+ rest, so a pack hardened out of band is never downgraded):
453
+
454
+ ```bash
455
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/apply-suppressions.js" --confirmed --plan <path>
456
+ ```
457
+
458
+ If that `--confirmed` run exits non-zero (or `--plan` is missing/unreadable),
459
+ handle it exactly as step 5 does: tell the user the write did not complete, fall
460
+ back to the floor (`onboard.js --floor`), and continue to step 6. Do **not** run
461
+ the overlay below on a failed spine — nothing was written, so there is no
462
+ recommended base to overlay the changes onto.
463
+
464
+ Then overwrite **only the packs the user changed** with their chosen levels:
465
+
466
+ ```bash
467
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --posture '<changed-packs-json>'
468
+ ```
469
+
470
+ `<changed-packs-json>` carries **only** the packs the user adjusted (not the
471
+ full 8-pack map), so the packs kept as recommended keep the fill-gaps-safe
472
+ values the spine wrote and only the user's explicit, downgrade-approved changes
473
+ overwrite.
474
+
475
+ If that overlay exits non-zero, the spine already wrote the recommended base, so
476
+ the store holds a valid posture — but **not** the user's overrides. Tell the user
477
+ their adjustments did not save and the store holds the recommended posture, then
478
+ continue to step 6. Do **not** report the adjusted posture as saved on a failed
479
+ overlay.
480
+
481
+ On success, present the applying-frame confirmation the **spine** printed —
482
+ `✓ Set all 8 detection categories · set aside N routine results · Ready: …` —
483
+ the store now holds the adjusted posture. The overlay's own smaller
484
+ `✓ Set all N detection categories` line (the count of just the changed packs)
485
+ is bookkeeping — **do not show it**; the applying frame reports the full
486
+ 8-pack posture. Then continue to step 6.
487
+
488
+ **On "Back to recommended"** — take the **Yes, apply** path instead: continue
489
+ to step 5 and apply the previewed plan verbatim with no override.
490
+
491
+ Do **not** write anything until the user has explicitly confirmed at step 4
492
+ (Yes, apply) or saved at step 4b (Save adjusted).
208
493
 
209
494
  ## 5. Write the posture and suppressions
210
495
 
211
496
  On confirmation, run the adapter again with `--confirmed --plan <path>`, passing
212
497
  the **plan-file path the preview printed in step 3** (`Plan saved to: <path>`). It
213
- reads that saved plan back and applies it **exactly as previewed** — it overwrites
214
- the per-category posture and writes one 30-day suppression per confirmed false
215
- positive **without re-running the backfill or the judge**. There is deliberately
216
- **no `backfill.js` pipe here**: re-scanning and re-judging would produce a fresh,
217
- non-deterministic plan and silently defeat the human gate the user just approved.
498
+ reads that saved plan back and applies it **exactly as previewed** — establishing
499
+ the **full 8-pack posture** the recommended view showed (the reviewed
500
+ evidence packs overwrite; the conservative severity floor fill-gaps the remaining
501
+ packs, so a pack the user had already hardened out of band is never downgraded) and
502
+ writing one 30-day suppression per confirmed false positive **without re-running the
503
+ backfill or the judge**. There is deliberately **no `backfill.js` pipe here**:
504
+ re-scanning and re-judging would produce a fresh, non-deterministic plan and
505
+ silently defeat the human gate the user just approved.
218
506
 
219
507
  The posture overwrite and the suppression writes are applied as a **single
220
508
  all-or-nothing transaction**: a mid-batch failure rolls back the posture change
@@ -226,40 +514,162 @@ conservative floor cannot collide with a partially-written posture.
226
514
  node "${CLAUDE_PLUGIN_ROOT}/scripts/apply-suppressions.js" --confirmed --plan <path>
227
515
  ```
228
516
 
517
+ The script prints the applying confirmation — `✓ Set all K detection categories
518
+ · set aside N routine results · Ready: …` — with both counts threaded from the
519
+ real write. Show that line to the user.
520
+
229
521
  If `--plan` is missing or the file is unreadable/invalid, the adapter **fails loud
230
522
  (non-zero) and writes nothing** — it never falls back to a re-judge. Treat that
231
523
  like the `--confirmed` failure below: tell the user the write did not complete,
232
524
  fall back to the floor, and continue to step 6.
233
525
 
234
- If the user chose to **adjust** categories in step 4, apply their overrides on
235
- top afterwards. `onboard.js --posture` overwrites only the categories in the map
236
- it's given, leaving the rest as written by the adapter:
526
+ If the `--confirmed` run exits non-zero, tell the user the write did not
527
+ complete, fall back to the floor (`onboard.js --floor`), and continue to step 6
528
+ so setup still finishes.
529
+
530
+ ## 6. Show the installed summary and hand off to the dashboard
531
+
532
+ Run the first-run script and relay its **install-complete summary** AKA_SHOW
533
+ region per the execution contract (live findings/recommendation counts, the
534
+ health score, and the per-category posture just written or floored). The
535
+ script wraps that summary in a Markdown code fence; paste it between the
536
+ markers exactly as printed and do **not** add another code fence, strip the
537
+ fence, or reformat it (it is space-aligned monospace that Markdown would
538
+ otherwise collapse).
539
+
540
+ Pass the **surfaced count** captured from step 3's calibration frame
541
+ (`counts.important`) as `--surfaced <count>` — this is the 'N worth a look' figure
542
+ the script emits in its own machine-readable handoff payload — but only when step 3
543
+ carried a surfaced count forward (it printed a `Plan saved to:` path). If the
544
+ calibration fell back to the floor (no plan path in step 3 — a fallback branch, or
545
+ the scan-ran-clean empty state whose zero-count frame carries nothing to look at),
546
+ **omit `--surfaced` entirely** — the script then withholds that payload rather than
547
+ fabricating a count.
548
+
549
+ Alongside it, pass the **surfaced live-key count** — the number of surfaced
550
+ live-key secret findings, which is the length of the calibration frame's
551
+ `maskedFindings` array (absent ⇒ 0) — as `--live-keys <count>`. This is the
552
+ narrower secret subset of the surfaced count; it gates the remediation
553
+ chain-entry the handoff offers, so a calibration that surfaced only non-secret
554
+ findings passes `--live-keys 0` and offers no remediation.
555
+
556
+ When `--surfaced` is passed, the script appends that handoff payload as a single
557
+ JSON block delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>` after the fenced
558
+ card. Like step 3's calibration frame, do **not** show this block to the user — it
559
+ is additive and machine-only; only the fenced install summary above is
560
+ user-facing.
237
561
 
238
562
  ```bash
239
- node "${CLAUDE_PLUGIN_ROOT}/scripts/onboard.js" --posture '{"secret":"block"}'
563
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/firstrun.js" --surfaced <count> --live-keys <count>
240
564
  ```
241
565
 
242
- (The JSON is illustrative pass only the categories the user changed, values
243
- one of `monitor`/`warn`/`redact`/`block`.) This is the only point in the wizard
244
- where a posture is persisted.
566
+ **Then hand off to the dashboard.** When the payload carries a positive
567
+ `worthALook` count, issue an explicit **AskUserQuestion** (the real picker, never a
568
+ printed list) using that count for `N`:
245
569
 
246
- If the `--confirmed` run exits non-zero, tell the user the write did not
247
- complete, fall back to the floor (`onboard.js --floor`), and continue to step 6
248
- so setup still finishes.
570
+ **N worth a look want to see them in the browser?**
571
+
572
+ - **Review leaked keys** — "let's deal with the exposed keys I found" —
573
+ _(offer this option first only when the payload's `options` include
574
+ `enter-remediation`, i.e. `liveKeys > 0`)_; entering it starts the
575
+ secret-leak remediation chain on the surfaced live keys. This composes with —
576
+ never replaces — the dashboard handoff below, so both stay reachable.
577
+ - **Open dashboard** — "open the local dashboard on what I found"
578
+ - **Not now** — "stay here — you can open it anytime"
579
+
580
+ Use the payload's `worthALook` value for `N` verbatim — do not invent or round it.
581
+ Offer **Review leaked keys** exactly when the payload's `options` carry the
582
+ `enter-remediation` entry (never otherwise); the **Open dashboard** / **Not now**
583
+ handoff is always present. If the payload was withheld (the floor fallback, or
584
+ nothing surfaced), skip this handoff question rather than inventing a count.
585
+
586
+ **If they choose "Review leaked keys"** — run the secret-leak remediation entry's
587
+ **present** mode, feeding it the calibration frame block you captured in step 3
588
+ (the same text `maskedFindings` came from) on stdin:
589
+
590
+ ```bash
591
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/remediate.js" <<'AKA_FRAME'
592
+ <the <<<AKA_FRAME_JSON … AKA_FRAME_JSON>>> block captured in step 3, verbatim>
593
+ AKA_FRAME
594
+ ```
249
595
 
250
- ## 6. Show the first-run summary
596
+ It prints the decision as human-facing text, then a machine-readable block
597
+ delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>` carrying the same decision
598
+ structured (do **not** show that block to the user). The human text has three
599
+ parts, all of which you **show to the user verbatim, in order**:
600
+
601
+ 1. the templated count line ("I found N exposed secret keys sitting in old
602
+ transcripts."),
603
+ 2. the fenced finding table (provider, masked token, where, state), and
604
+ 3. inside that same fence, a most-exposed-first recommendation line and a
605
+ secret-scan chaining line.
606
+
607
+ This entire human-text block is the entry's AKA_SHOW region — relay it per the
608
+ execution contract, pasting it between the markers exactly as printed — do not
609
+ drop the recommendation or chaining lines, and do not paraphrase.
610
+
611
+ Alongside that fenced block, explain the findings in plain language grounded
612
+ in the same `maskedFindings` array the block came from — what each finding is
613
+ and why it matters, not a bare recital of the count line above it. The same
614
+ grounding discipline as step 4's narration applies here: every count you
615
+ speak matches the frame's own count, and every value you reference stays
616
+ masked. With no `maskedFindings` present there is nothing to narrate beyond
617
+ the count line and table already shown — do not invent an explanation.
618
+
619
+ Then issue an
620
+ **AskUserQuestion** offering exactly these four options, in order (each option's
621
+ label maps to the `--option` id shown in parentheses):
622
+
623
+ - **Redact + rotation checklist** (`redact-rotation-checklist`)
624
+ - **Redact only** (`redact-only`)
625
+ - **Set 'secret' to redact** (`set-secret-redact`)
626
+ - **Leave** (`leave`)
627
+
628
+ **If they chose "Redact + rotation checklist" or "Redact only"** — before running
629
+ the route, issue a second **AskUserQuestion** presenting the standing-posture
630
+ prompt, offering exactly these four options, in order (each option's label maps
631
+ to the `--posture` level in parentheses):
632
+
633
+ **Set the 'secret' detection level**
634
+
635
+ - **Redact** (`redact`)
636
+ - **Warn** (`warn`)
637
+ - **Block** (`block`)
638
+ - **Monitor** (`monitor`)
639
+
640
+ Then run the entry's **route** mode ONCE with the chosen redact option's id AND
641
+ the chosen posture level, feeding it the SAME calibration frame block again on
642
+ stdin:
251
643
 
252
- Run the first-run script and show its output **exactly as printed** (the
253
- install-complete summary with live findings/recommendation counts, the health
254
- score, and — now — the per-category posture just written or floored). The
255
- script already prints it inside a Markdown code fence; reproduce that
256
- verbatim and do **not** add another code fence, strip the fence, or reformat it (it
257
- is space-aligned monospace that Markdown would otherwise collapse).
644
+ ```bash
645
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/remediate.js" --option <id> --posture <level> <<'AKA_FRAME'
646
+ <the same block>
647
+ AKA_FRAME
648
+ ```
649
+
650
+ Never run the route a second time for this choice — a repeat call would strike
651
+ the already-redacted keys again and corrupt the reported count. Show its printed
652
+ result verbatim, in order. For "Redact only" that is the redaction confirmation
653
+ then the standing-posture confirmation. For "Redact + rotation checklist" it is
654
+ the standing-posture confirmation then the resolved rotation-checklist summary —
655
+ which reports the redaction itself, so the script does not print a separate
656
+ redaction confirmation ahead of it.
657
+
658
+ **If they chose "Set 'secret' to redact" or "Leave"** — run the entry's **route**
659
+ mode with the chosen option's id (the id in parentheses above, e.g. **Leave** →
660
+ `leave`), feeding it the SAME calibration frame block again on stdin:
258
661
 
259
662
  ```bash
260
- node "${CLAUDE_PLUGIN_ROOT}/scripts/firstrun.js"
663
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/remediate.js" --option <id> <<'AKA_FRAME'
664
+ <the same block>
665
+ AKA_FRAME
261
666
  ```
262
667
 
668
+ Show its printed result verbatim — a standing-posture confirmation, or (choosing
669
+ "Leave") a plain note that nothing changed. This entry reads its findings from the
670
+ calibration frame alone and holds no wizard state of its own, so it works
671
+ identically from any caller.
672
+
263
673
  ## 7. Offer the AKA CLI + local dashboard (opt-in)
264
674
 
265
675
  Now that the plugin is set up, offer the optional **AKA CLI + local dashboard** —
@@ -267,16 +677,16 @@ a richer, still-fully-local surface over the same `~/.aka` store this plugin
267
677
  writes. The plugin works completely on its own; this is additive (and the path to
268
678
  future multi-agent support). Use **AskUserQuestion**:
269
679
 
270
- **Add the AKA CLI + dashboard?** — "Install the `aka` CLI for a local web +
271
- terminal dashboard and on-demand scans? Everything stays on your machine."
680
+ **Want the AKA CLI + dashboard too?** — "The `aka` CLI adds a local user
681
+ interface + terminal dashboard and on-demand scans."
272
682
 
273
- - **Yes, install it** _(recommended)_ — adds the `aka` binary: `aka stats`,
274
- `aka tui` (terminal dashboard), `aka dashboard` (local web UI), `aka scan`.
275
- - **Not now** — skip; it can be added anytime with the one-liner below.
683
+ - **Yes, add it** _(recommended)_ — "adds the `aka` command: stats, a terminal
684
+ dashboard, a local user interface, and on-demand scans"
685
+ - **Not now** — "skip you can add it anytime with the one-liner below"
276
686
 
277
- If they choose **Yes**, run the bootstrap installer (it ensures Node is available
278
- and installs the global CLI from the public npm registry). **Ask permission
279
- before running it**, then run the line for their OS:
687
+ **Yes, add it** is the install authorization run the bootstrap installer
688
+ directly, with no second picker (it ensures Node is available and installs the
689
+ global CLI from the public npm registry). Run the line for their OS:
280
690
 
281
691
  ```bash
282
692
  # macOS / Linux
@@ -304,8 +714,10 @@ what happened, show the one-liner so they can retry later, and continue the
304
714
  wizard normally. The plugin is already fully set up and works on its own; a
305
715
  failed CLI install changes nothing about that.
306
716
 
307
- ## 8. Report the result
717
+ **Close the wizard.** The first-run summary already confirmed the saved posture
718
+ and pointed at `/health`. Whichever way the CLI offer went — installed,
719
+ declined, or a failed install you already reported — end with one warm close:
720
+ "That's it — I'm watching out for Claude going forward."
308
721
 
309
- - The first-run summary already confirms the saved posture and points at
310
- `/health`. Add at most one short sentence: detection runs locally and nothing
311
- leaves the machine.
722
+ Before you finish, confirm every AKA_SHOW region on the path you took was
723
+ relayed to the user. If you summarized one instead of pasting it, paste it now.